From 583c36e24b4e90042fd37f8fc03cbebeaf15b302 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 2 Nov 2023 15:27:49 -0600 Subject: [PATCH] WIP --- Cargo.lock | 55 + crates/editor2/Cargo.toml | 44 +- crates/editor2/src/blink_manager.rs | 6 +- crates/editor2/src/display_map.rs | 11 +- crates/editor2/src/display_map/block_map.rs | 1248 +- crates/editor2/src/display_map/fold_map.rs | 4 +- crates/editor2/src/display_map/inlay_map.rs | 2 +- crates/editor2/src/display_map/wrap_map.rs | 618 +- crates/editor2/src/editor.rs | 14747 +++++++++--------- crates/editor2/src/editor_settings.rs | 4 +- crates/editor2/src/element.rs | 6274 ++++---- crates/editor2/src/hover_popover.rs | 2 +- crates/editor2/src/inlay_hint_cache.rs | 4 +- crates/editor2/src/items.rs | 294 +- crates/editor2/src/movement.rs | 2 +- crates/editor2/src/scroll/actions.rs | 266 +- crates/editor2/src/selections_collection.rs | 2 +- crates/editor2/src/test.rs | 2 +- crates/language2/src/buffer.rs | 1 + crates/workspace2/src/workspace2.rs | 15 +- crates/zed2/Cargo.toml | 2 +- 21 files changed, 11815 insertions(+), 11788 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index db8e88cb1c..e08eb58f5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2622,6 +2622,60 @@ dependencies = [ "workspace", ] +[[package]] +name = "editor2" +version = "0.1.0" +dependencies = [ + "aho-corasick", + "anyhow", + "client2", + "clock", + "collections", + "context_menu", + "convert_case 0.6.0", + "copilot2", + "ctor", + "db2", + "drag_and_drop", + "env_logger 0.9.3", + "futures 0.3.28", + "fuzzy2", + "git", + "gpui2", + "indoc", + "itertools 0.10.5", + "language2", + "lazy_static", + "log", + "lsp2", + "multi_buffer", + "ordered-float 2.10.0", + "parking_lot 0.11.2", + "postage", + "project2", + "rand 0.8.5", + "rich_text", + "rpc2", + "schemars", + "serde", + "serde_derive", + "settings2", + "smallvec", + "smol", + "snippet", + "sqlez", + "sum_tree", + "text2", + "theme2", + "tree-sitter", + "tree-sitter-html", + "tree-sitter-rust", + "tree-sitter-typescript", + "unindent", + "util", + "workspace2", +] + [[package]] name = "either" version = "1.9.0" @@ -11071,6 +11125,7 @@ dependencies = [ "copilot2", "ctor", "db2", + "editor2", "env_logger 0.9.3", "feature_flags2", "fs2", diff --git a/crates/editor2/Cargo.toml b/crates/editor2/Cargo.toml index 5113b5e7de..3f94d5cdc0 100644 --- a/crates/editor2/Cargo.toml +++ b/crates/editor2/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "editor" +name = "editor2" version = "0.1.0" edition = "2021" publish = false @@ -23,30 +23,30 @@ test-support = [ ] [dependencies] -client = { path = "../client" } +client = { package = "client2", path = "../client2" } clock = { path = "../clock" } -copilot = { path = "../copilot" } -db = { path = "../db" } +copilot = { package="copilot2", path = "../copilot2" } +db = { package="db2", path = "../db2" } drag_and_drop = { path = "../drag_and_drop" } collections = { path = "../collections" } context_menu = { path = "../context_menu" } -fuzzy = { path = "../fuzzy" } +fuzzy = { package = "fuzzy2", path = "../fuzzy2" } git = { path = "../git" } -gpui = { path = "../gpui" } -language = { path = "../language" } -lsp = { path = "../lsp" } +gpui = { package = "gpui2", path = "../gpui2" } +language = { package = "language2", path = "../language2" } +lsp = { package = "lsp2", path = "../lsp2" } multi_buffer = { path = "../multi_buffer" } -project = { path = "../project" } -rpc = { path = "../rpc" } +project = { package = "project2", path = "../project2" } +rpc = { package = "rpc2", path = "../rpc2" } rich_text = { path = "../rich_text" } -settings = { path = "../settings" } +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" } util = { path = "../util" } sqlez = { path = "../sqlez" } -workspace = { path = "../workspace" } +workspace = { package="workspace2", path = "../workspace2" } aho-corasick = "1.1" anyhow.workspace = true @@ -71,15 +71,15 @@ 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"] } +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 = { path = "../multi_buffer", features = ["test-support"] } ctor.workspace = true diff --git a/crates/editor2/src/blink_manager.rs b/crates/editor2/src/blink_manager.rs index fa5a3af0c6..7d242b6684 100644 --- a/crates/editor2/src/blink_manager.rs +++ b/crates/editor2/src/blink_manager.rs @@ -61,7 +61,7 @@ impl BlinkManager { } fn blink_cursors(&mut self, epoch: usize, cx: &mut ModelContext) { - if settings::get::(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(); @@ -107,7 +107,3 @@ impl BlinkManager { self.visible } } - -impl Entity for BlinkManager { - type Event = (); -} diff --git a/crates/editor2/src/display_map.rs b/crates/editor2/src/display_map.rs index 1d6deb910a..a26b07efec 100644 --- a/crates/editor2/src/display_map.rs +++ b/crates/editor2/src/display_map.rs @@ -12,10 +12,9 @@ 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, + Entity, Hsla, Model, ModelContext, }; use inlay_map::InlayMap; use language::{ @@ -49,12 +48,12 @@ type TextHighlights = TreeMap, Arc<(HighlightStyle, Vec>; pub struct DisplayMap { - buffer: ModelHandle, + buffer: Model, buffer_subscription: BufferSubscription, fold_map: FoldMap, inlay_map: InlayMap, tab_map: TabMap, - wrap_map: ModelHandle, + wrap_map: Model, block_map: BlockMap, text_highlights: TextHighlights, inlay_highlights: InlayHighlights, @@ -67,7 +66,7 @@ impl Entity for DisplayMap { impl DisplayMap { pub fn new( - buffer: ModelHandle, + buffer: Model, font_id: FontId, font_size: f32, wrap_width: Option, @@ -1015,7 +1014,7 @@ pub mod tests { movement, test::{editor_test_context::EditorTestContext, marked_display_snapshot}, }; - use gpui::{color::Color, elements::*, test::observe, AppContext}; + use gpui::{elements::*, test::observe, AppContext, Hsla}; use language::{ language_settings::{AllLanguageSettings, AllLanguageSettingsContent}, Buffer, Language, LanguageConfig, SelectionGoal, diff --git a/crates/editor2/src/display_map/block_map.rs b/crates/editor2/src/display_map/block_map.rs index c07625bf9c..e6830a9039 100644 --- a/crates/editor2/src/display_map/block_map.rs +++ b/crates/editor2/src/display_map/block_map.rs @@ -80,8 +80,8 @@ pub enum BlockStyle { Sticky, } -pub struct BlockContext<'a, 'b, 'c> { - pub view_context: &'c mut ViewContext<'a, 'b, Editor>, +pub struct BlockContext<'a, 'b> { + pub view_context: &'b mut ViewContext<'a, Editor>, pub anchor_x: f32, pub scroll_x: f32, pub gutter_width: f32, @@ -988,680 +988,680 @@ fn offset_for_row(s: &str, target: u32) -> (u32, 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::{elements::Empty, Element}; - use multi_buffer::MultiBuffer; - use rand::prelude::*; - use settings::SettingsStore; - use std::env; - use util::RandomCharIter; +// #[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::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_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::AppContext) { - init_test(cx); +// #[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(); +// 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 text = "aaa\nbbb\nccc\nddd"; +// let text = "aaa\nbbb\nccc\nddd"; - let buffer = MultiBuffer::build_simple(text, cx); - let buffer_snapshot = 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 mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1); +// let buffer = MultiBuffer::build_simple(text, cx); +// let buffer_snapshot = 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 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(|_| Empty::new().into_any_named("block 1")), - }, - 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")), - }, - 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")), - }, - ]); +// 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(|_| Empty::new().into_any_named("block 1")), +// }, +// 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")), +// }, +// 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")), +// }, +// ]); - let snapshot = block_map.read(wraps_snapshot, Default::default()); - assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n"); +// 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::>(); +// 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::>(); - // 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]), - ] - ); +// // 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_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.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.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::>(), - &[ - Some(0), - None, - None, - None, - Some(1), - Some(2), - Some(3), - None, - None, - None - ] - ); +// assert_eq!( +// snapshot.buffer_rows(0).collect::>(), +// &[ +// 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) - }); +// // 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"); - } +// 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::AppContext) { - init_test(cx); +// #[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(); +// 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 text = "one two three\nfour five six\nseven eight"; +// 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 (_, 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 mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1); +// let buffer = MultiBuffer::build_simple(text, 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()); +// let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font_id, 14.0, Some(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(|_| Empty::new().into_any_named("block 1")), - 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")), - height: 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(|_| Empty::new().into_any_named("block 1")), +// 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")), +// 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" - ); - } +// // 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::AppContext, mut rng: StdRng) { - init_test(cx); +// #[gpui::test(iterations = 100)] +// fn test_random_blocks(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 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(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 buffer_start_header_height = rng.gen_range(1..=5); - let excerpt_header_height = rng.gen_range(1..=5); +// let wrap_width = if rng.gen_bool(0.2) { +// None +// } else { +// Some(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 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); +// 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::(); - log::info!("initial buffer text: {:?}", text); - MultiBuffer::build_simple(&text, cx) - } else { - MultiBuffer::build_random(&mut rng, cx) - }; +// let buffer = if rng.gen() { +// let len = rng.gen_range(0..10); +// let text = RandomCharIter::new(&mut rng).take(len).collect::(); +// log::info!("initial buffer text: {:?}", text); +// 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 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 mut block_map = BlockMap::new( - wraps_snapshot, - buffer_start_header_height, - excerpt_header_height, - ); - let mut custom_blocks = Vec::new(); +// let mut buffer_snapshot = 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 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(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 = buffer.read(cx).read(cx); - let position = buffer.anchor_after( - buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left), - ); +// 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(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 = buffer.read(cx).read(cx); +// 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(|_| Empty::new().into_any()), - } - }) - .collect::>(); +// 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(|_| Empty::new().into_any()), +// } +// }) +// .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); - 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); +// 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(), 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 (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 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::>(); - 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 input_buffer_rows = buffer_snapshot.buffer_rows(0).collect::>(); +// 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]; +// 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; - } - } +// 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); +// 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; - } - } - } +// 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::>(); - 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::(); - assert_eq!( - actual_text, expected_text, - "incorrect text starting from row {}", - start_row - ); - assert_eq!( - blocks_snapshot - .buffer_rows(start_row as u32) - .collect::>(), - &expected_buffer_rows[start_row..] - ); - } +// let expected_lines = expected_text.split('\n').collect::>(); +// 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::(); +// assert_eq!( +// actual_text, expected_text, +// "incorrect text starting from row {}", +// start_row +// ); +// assert_eq!( +// blocks_snapshot +// .buffer_rows(start_row as u32) +// .collect::>(), +// &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::>(), - expected_block_positions - ); +// assert_eq!( +// blocks_snapshot +// .blocks_in_range(0..(expected_row_count as u32)) +// .map(|(row, block)| (row, block.clone().into())) +// .collect::>(), +// 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; +// 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 - ); +// 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 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, - ); +// 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); - } +// 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 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 - ); +// 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; - } - } - } +// 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, - }, - } +// #[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, - } - } +// 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, - } - } - } +// fn disposition(&self) -> BlockDisposition { +// match self { +// ExpectedBlock::ExcerptHeader { .. } => BlockDisposition::Above, +// ExpectedBlock::Custom { disposition, .. } => *disposition, +// } +// } +// } - impl From 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, - }, - } - } - } - } +// impl From 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) { - cx.set_global(SettingsStore::test(cx)); - theme::init((), cx); - } +// fn init_test(cx: &mut gpui::AppContext) { +// cx.set_global(SettingsStore::test(cx)); +// theme::init(cx); +// } - impl TransformBlock { - fn as_custom(&self) -> Option<&Block> { - match self { - TransformBlock::Custom(block) => Some(block), - TransformBlock::ExcerptHeader { .. } => None, - } - } - } +// 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) - } - } -} +// impl BlockSnapshot { +// fn to_point(&self, point: BlockPoint, bias: Bias) -> Point { +// self.wrap_snapshot.to_point(self.to_wrap_point(point), bias) +// } +// } +// } diff --git a/crates/editor2/src/display_map/fold_map.rs b/crates/editor2/src/display_map/fold_map.rs index 4636d9a17f..4645f64451 100644 --- a/crates/editor2/src/display_map/fold_map.rs +++ b/crates/editor2/src/display_map/fold_map.rs @@ -3,7 +3,7 @@ use super::{ Highlights, }; use crate::{Anchor, AnchorRangeExt, MultiBufferSnapshot, ToOffset}; -use gpui::{color::Color, fonts::HighlightStyle}; +use gpui::{fonts::HighlightStyle, Hsla}; use language::{Chunk, Edit, Point, TextSummary}; use std::{ any::TypeId, @@ -174,7 +174,7 @@ impl<'a> FoldMapWriter<'a> { pub struct FoldMap { snapshot: FoldSnapshot, - ellipses_color: Option, + ellipses_color: Option, } impl FoldMap { diff --git a/crates/editor2/src/display_map/inlay_map.rs b/crates/editor2/src/display_map/inlay_map.rs index c0c352453b..fbd638429a 100644 --- a/crates/editor2/src/display_map/inlay_map.rs +++ b/crates/editor2/src/display_map/inlay_map.rs @@ -1890,6 +1890,6 @@ mod tests { fn init_test(cx: &mut AppContext) { cx.set_global(SettingsStore::test(cx)); - theme::init((), cx); + theme::init(cx); } } diff --git a/crates/editor2/src/display_map/wrap_map.rs b/crates/editor2/src/display_map/wrap_map.rs index 60337661c1..706e16c24f 100644 --- a/crates/editor2/src/display_map/wrap_map.rs +++ b/crates/editor2/src/display_map/wrap_map.rs @@ -4,9 +4,7 @@ use super::{ Highlights, }; use crate::MultiBufferSnapshot; -use gpui::{ - fonts::FontId, text_layout::LineWrapper, AppContext, Entity, ModelContext, ModelHandle, Task, -}; +use gpui::{AppContext, Entity, Model, ModelContext, Task}; use language::{Chunk, Point}; use lazy_static::lazy_static; use smol::future::yield_now; @@ -27,10 +25,6 @@ pub struct WrapMap { font: (FontId, f32), } -impl Entity for WrapMap { - type Event = (); -} - #[derive(Clone)] pub struct WrapSnapshot { tab_snapshot: TabSnapshot, @@ -78,7 +72,7 @@ impl WrapMap { font_size: f32, wrap_width: Option, cx: &mut AppContext, - ) -> (ModelHandle, WrapSnapshot) { + ) -> (Model, WrapSnapshot) { let handle = cx.add_model(|cx| { let mut this = Self { font: (font_id, font_size), @@ -1019,337 +1013,337 @@ fn consolidate_wrap_edits(edits: &mut Vec) { } } -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap}, - MultiBuffer, - }; - use gpui::test::observe; - use rand::prelude::*; - use settings::SettingsStore; - use smol::stream::StreamExt; - use std::{cmp, env, num::NonZeroU32}; - use text::Rope; +// #[cfg(test)] +// mod tests { +// use super::*; +// use crate::{ +// display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap}, +// MultiBuffer, +// }; +// use gpui::test::observe; +// use rand::prelude::*; +// use settings::SettingsStore; +// use smol::stream::StreamExt; +// use std::{cmp, env, num::NonZeroU32}; +// use text::Rope; - #[gpui::test(iterations = 100)] - async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) { - init_test(cx); +// #[gpui::test(iterations = 100)] +// async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) { +// init_test(cx); - cx.foreground().set_block_on_ticks(0..=50); - let operations = env::var("OPERATIONS") - .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) - .unwrap_or(10); +// cx.foreground().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 mut wrap_width = if rng.gen_bool(0.1) { - None - } else { - Some(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_cache = cx.font_cache().clone(); +// let font_system = cx.platform().fonts(); +// let mut wrap_width = if rng.gen_bool(0.1) { +// None +// } else { +// Some(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; - log::info!("Tab size: {}", tab_size); - log::info!("Wrap width: {:?}", wrap_width); +// 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::(); - 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 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::(); +// 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 = LineWrapper::new(font_id, font_size, font_system); - let unwrapped_text = tabs_snapshot.text(); - let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper); +// let mut line_wrapper = LineWrapper::new(font_id, font_size, font_system); +// 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)); - let mut notifications = observe(&wrap_map, cx); +// let (wrap_map, _) = +// cx.update(|cx| WrapMap::new(tabs_snapshot.clone(), font_id, 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(); - } +// 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 (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 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 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(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()); - }); - } - } +// 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(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()); +// 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)); +// 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()) && 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 - ); +// 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,); - } +// 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); - } - } +// 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, - ) - } - } - } +// 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::(); +// 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::(); - initial_text.replace(old_start..old_end, &new_text); - } - assert_eq!(initial_text.to_string(), snapshot_text.to_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())); - } +// 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.foreground().forbid_parking(); - cx.update(|cx| { - cx.set_global(SettingsStore::test(cx)); - theme::init((), cx); - }); - } +// fn init_test(cx: &mut gpui::TestAppContext) { +// cx.foreground().forbid_parking(); +// cx.update(|cx| { +// cx.set_global(SettingsStore::test(cx)); +// theme::init((), cx); +// }); +// } - fn wrap_text( - unwrapped_text: &str, - wrap_width: Option, - 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') - } +// fn wrap_text( +// unwrapped_text: &str, +// wrap_width: Option, +// 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() - } - } +// 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() - } +// impl WrapSnapshot { +// pub fn text(&self) -> String { +// self.text_chunks(0).collect() +// } - pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator { - self.chunks( - wrap_row..self.max_point().row() + 1, - false, - Highlights::default(), - ) - .map(|h| h.text) - } +// pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator { +// 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; +// 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::(); - 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::>() - .join("\n"); - if end_row <= self.max_point().row() { - expected_text.push('\n'); - } +// let mut expected_text = self.text_chunks(start_row).collect::(); +// 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::>() +// .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::(); - assert_eq!( - expected_text, - actual_text, - "chunks != highlighted_chunks for rows {:?}", - start_row..end_row - ); - } - } - } -} +// let actual_text = self +// .chunks(start_row..end_row, true, Highlights::default()) +// .map(|c| c.text) +// .collect::(); +// assert_eq!( +// expected_text, +// actual_text, +// "chunks != highlighted_chunks for rows {:?}", +// start_row..end_row +// ); +// } +// } +// } +// } diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 4e449bb7f7..196527acdb 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -38,18 +38,9 @@ pub use element::{ use futures::FutureExt; use fuzzy::{StringMatch, StringMatchCandidate}; 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, + serde_json, AnyElement, AppContext, AsyncAppContext, ClipboardItem, + Element, Entity, Hsla, Model, Subscription, Task, View, ViewContext, + WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -59,8 +50,8 @@ 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, DiagnosticSeverity, Documentation, File, IndentKind, + point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, + Completion, CursorShape, Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language, LanguageRegistry, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, Selection, SelectionGoal, TransactionId, }; @@ -551,7 +542,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(Editor::context_menu_last); hover_popover::init(cx); - scroll::actions::init(cx); + /scroll::actions::init(cx); workspace::register_project_item::(cx); workspace::register_followable_item::(cx); @@ -626,8 +617,8 @@ type InlayBackgroundHighlight = (fn(&Theme) -> Color, Vec); pub struct Editor { handle: WeakViewHandle, - buffer: ModelHandle, - display_map: ModelHandle, + buffer: Model, + display_map: Model, pub selections: SelectionsCollection, pub scroll_manager: ScrollManager, columnar_selection_tail: Option, @@ -643,10 +634,10 @@ pub struct Editor { soft_wrap_mode_override: Option, get_field_editor_theme: Option>, override_text_style: Option>, - project: Option>, + project: Option>, collaboration_hub: Option>, focused: bool, - blink_manager: ModelHandle, + blink_manager: Model, pub show_local_selections: bool, mode: EditorMode, show_gutter: bool, @@ -660,7 +651,7 @@ pub struct Editor { mouse_context_menu: ViewHandle, completion_tasks: Vec<(CompletionId, Task>)>, next_completion_id: CompletionId, - available_code_actions: Option<(ModelHandle, Arc<[CodeAction]>)>, + available_code_actions: Option<(Model, Arc<[CodeAction]>)>, code_actions_task: Option>, document_highlights_task: Option>, pending_rename: Option, @@ -678,7 +669,7 @@ pub struct Editor { gutter_hovered: bool, link_go_to_definition_state: LinkGoToDefinitionState, copilot_state: CopilotState, - inlay_hint_cache: InlayHintCache, + // inlay_hint_cache: InlayHintCache, next_inlay_id: usize, _subscriptions: Vec, pixel_position_of_newest_cursor: Option, @@ -851,7 +842,7 @@ enum ContextMenu { impl ContextMenu { fn select_first( &mut self, - project: Option<&ModelHandle>, + project: Option<&Model>, cx: &mut ViewContext, ) -> bool { if self.visible() { @@ -867,7 +858,7 @@ impl ContextMenu { fn select_prev( &mut self, - project: Option<&ModelHandle>, + project: Option<&Model>, cx: &mut ViewContext, ) -> bool { if self.visible() { @@ -883,7 +874,7 @@ impl ContextMenu { fn select_next( &mut self, - project: Option<&ModelHandle>, + project: Option<&Model>, cx: &mut ViewContext, ) -> bool { if self.visible() { @@ -899,7 +890,7 @@ impl ContextMenu { fn select_last( &mut self, - project: Option<&ModelHandle>, + project: Option<&Model>, cx: &mut ViewContext, ) -> bool { if self.visible() { @@ -938,7 +929,7 @@ impl ContextMenu { struct CompletionsMenu { id: CompletionId, initial_position: Anchor, - buffer: ModelHandle, + buffer: Model, completions: Arc>>, match_candidates: Arc<[StringMatchCandidate]>, matches: Arc<[StringMatch]>, @@ -949,7 +940,7 @@ struct CompletionsMenu { impl CompletionsMenu { fn select_first( &mut self, - project: Option<&ModelHandle>, + project: Option<&Model>, cx: &mut ViewContext, ) { self.selected_item = 0; @@ -960,7 +951,7 @@ impl CompletionsMenu { fn select_prev( &mut self, - project: Option<&ModelHandle>, + project: Option<&Model>, cx: &mut ViewContext, ) { if self.selected_item > 0 { @@ -975,7 +966,7 @@ impl CompletionsMenu { fn select_next( &mut self, - project: Option<&ModelHandle>, + project: Option<&Model>, cx: &mut ViewContext, ) { if self.selected_item + 1 < self.matches.len() { @@ -990,7 +981,7 @@ impl CompletionsMenu { fn select_last( &mut self, - project: Option<&ModelHandle>, + project: Option<&Model>, cx: &mut ViewContext, ) { self.selected_item = self.matches.len() - 1; @@ -1001,7 +992,7 @@ impl CompletionsMenu { fn pre_resolve_completion_documentation( &self, - project: Option>, + project: Option>, cx: &mut ViewContext, ) { let settings = settings::get::(cx); @@ -1089,7 +1080,7 @@ impl CompletionsMenu { fn attempt_resolve_selected_completion_documentation( &mut self, - project: Option<&ModelHandle>, + project: Option<&Model>, cx: &mut ViewContext, ) { let settings = settings::get::(cx); @@ -1519,7 +1510,7 @@ impl CompletionsMenu { #[derive(Clone)] struct CodeActionsMenu { actions: Arc<[CodeAction]>, - buffer: ModelHandle, + buffer: Model, selected_item: usize, list: UniformListState, deployed_from_indicator: bool, @@ -1798,7347 +1789,7349 @@ impl InlayHintRefreshReason { } } -impl Editor { - pub fn single_line( - field_editor_style: Option>, - cx: &mut ViewContext, - ) -> Self { - let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new())); - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx) - } - - pub fn multi_line( - field_editor_style: Option>, - cx: &mut ViewContext, - ) -> Self { - let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new())); - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - Self::new(EditorMode::Full, buffer, None, field_editor_style, cx) - } - - pub fn auto_height( - max_lines: usize, - field_editor_style: Option>, - cx: &mut ViewContext, - ) -> Self { - let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new())); - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - Self::new( - EditorMode::AutoHeight { max_lines }, - buffer, - None, - field_editor_style, - cx, - ) - } - - pub fn for_buffer( - buffer: ModelHandle, - project: Option>, - cx: &mut ViewContext, - ) -> Self { - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - Self::new(EditorMode::Full, buffer, project, None, cx) - } - - pub fn for_multibuffer( - buffer: ModelHandle, - project: Option>, - cx: &mut ViewContext, - ) -> Self { - Self::new(EditorMode::Full, buffer, project, None, cx) - } - - pub fn clone(&self, cx: &mut ViewContext) -> Self { - let mut clone = Self::new( - self.mode, - self.buffer.clone(), - self.project.clone(), - self.get_field_editor_theme.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: ModelHandle, - project: Option>, - get_field_editor_theme: Option>, - cx: &mut ViewContext, - ) -> Self { - let editor_view_id = cx.view_id(); - let display_map = cx.add_model(|cx| { - let settings = settings::get::(cx); - let style = build_style(settings, get_field_editor_theme.as_deref(), None, cx); - DisplayMap::new( - buffer.clone(), - style.text.font_id, - style.text.font_size, - None, - 2, - 1, - cx, - ) - }); - - let selections = SelectionsCollection::new(display_map.clone(), buffer.clone()); - - let blink_manager = cx.add_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(Event::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 mut this = Self { - handle: cx.weak_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, - get_field_editor_theme, - collaboration_hub: project.clone().map(|project| Box::new(project) as _), - project, - focused: false, - 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: cx - .add_view(|cx| context_menu::ContextMenu::new(editor_view_id, cx)), - 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, - override_text_style: None, - 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, - _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::(Self::settings_changed), - cx.observe_window_activation(|editor, active, cx| { - 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); - - let editor_created_event = EditorCreated(cx.handle()); - cx.emit_global(editor_created_event); - - if mode == EditorMode::Full { - let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars(); - cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars)); - } - - this.report_editor_event("open", None, cx); - this - } - - pub fn new_file( - workspace: &mut Workspace, - _: &workspace::NewFile, - cx: &mut ViewContext, - ) { - let project = workspace.project().clone(); - if project.read(cx).is_remote() { - cx.propagate_action(); - } else if let Some(buffer) = project - .update(cx, |project, cx| project.create_buffer("", None, cx)) - .log_err() - { - workspace.add_item( - Box::new(cx.add_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, - ) { - let project = workspace.project().clone(); - if project.read(cx).is_remote() { - cx.propagate_action(); - } 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.add_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 { - self.leader_peer_id - } - - pub fn buffer(&self) -> &ModelHandle { - &self.buffer - } - - fn workspace(&self, cx: &AppContext) -> Option> { - self.workspace.as_ref()?.0.upgrade(cx) - } - - 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 - .handle - .upgrade(cx) - .map_or(false, |handle| handle.is_focused(cx)), - } - } - - pub fn language_at<'a, T: ToOffset>( - &self, - point: T, - cx: &'a AppContext, - ) -> Option> { - self.buffer.read(cx).language_at(point, cx) - } - - pub fn file_at<'a, T: ToOffset>(&self, point: T, cx: &'a AppContext) -> Option> { - self.buffer.read(cx).read(cx).file_at(point).cloned() - } - - pub fn active_excerpt( - &self, - cx: &AppContext, - ) -> Option<(ExcerptId, ModelHandle, Range)> { - self.buffer - .read(cx) - .excerpt_containing(self.selections.newest_anchor().head(), cx) - } - - pub fn style(&self, cx: &AppContext) -> EditorStyle { - build_style( - settings::get::(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) { - self.collaboration_hub = Some(hub); - } - - pub fn set_placeholder_text( - &mut self, - placeholder_text: impl Into>, - cx: &mut ViewContext, - ) { - self.placeholder_text = Some(placeholder_text.into()); - cx.notify(); - } - - pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext) { - 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(&self, range: &Range) -> Range { - 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) { - 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( - &mut self, - context: KeymapContext, - cx: &mut ViewContext, - ) { - self.keymap_context_layers - .insert(TypeId::of::(), context); - cx.notify(); - } - - pub fn remove_keymap_context_layer(&mut self, cx: &mut ViewContext) { - self.keymap_context_layers.remove(&TypeId::of::()); - 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; - } - - pub fn set_field_editor_style( - &mut self, - style: Option>, - cx: &mut ViewContext, - ) { - self.get_field_editor_theme = style; - cx.notify(); - } - - fn selections_did_change( - &mut self, - local: bool, - old_cursor_position: &Anchor, - cx: &mut ViewContext, - ) { - if self.focused && 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().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(Event::SelectionsChanged { local }); - cx.notify(); - } - - pub fn change_selections( - &mut self, - autoscroll: Option, - cx: &mut ViewContext, - 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(&mut self, edits: I, cx: &mut ViewContext) - where - I: IntoIterator, T)>, - S: ToOffset, - T: Into>, - { - if self.read_only { - return; - } - - self.buffer - .update(cx, |buffer, cx| buffer.edit(edits, None, cx)); - } - - pub fn edit_with_autoindent(&mut self, edits: I, cx: &mut ViewContext) - where - I: IntoIterator, T)>, - S: ToOffset, - T: Into>, - { - 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( - &mut self, - edits: I, - original_indent_columns: Vec, - cx: &mut ViewContext, - ) where - I: IntoIterator, T)>, - S: ToOffset, - T: Into>, - { - 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.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, - ) { - let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let tail = self.selections.newest::(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, - ) { - if !self.focused { - cx.focus_self(); - } - - 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, - ) { - if !self.focused { - cx.focus_self(); - } - - let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let tail = self.selections.newest::(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: Vector2F, - cx: &mut ViewContext, - ) { - 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 { - 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.columnar_selection_tail.take(); - if self.selections.pending_anchor().is_some() { - let selections = self.selections.all::(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, - ) { - 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::>(); - - 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) { - 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_action(); - } - - pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext) { - let text: Arc = 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::(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::>(); - - 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 && settings::get::(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.transact(cx, |this, cx| { - let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = { - let selections = this.selections.all::(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::(); - - let trailing_whitespace_len = buffer - .chars_at(end) - .take_while(|c| c.is_whitespace() && *c != '\n') - .map(|c| c.len_utf8()) - .sum::(); - - 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) { - 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) { - 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.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, - cx: &mut ViewContext, - ) { - if self.read_only { - return; - } - - let text: Arc = 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::>() - }; - 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) { - if !settings::get::(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) { - let selections = self.selections.all::(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, ®ion.pair.start) { - if buffer.contains_str_at(range.end, ®ion.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>, - buffer: &'a MultiBufferSnapshot, - ) -> impl Iterator, 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 = ®ions[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], - 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 { - 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::(), - ) - } else { - None - } - } - - pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext) { - 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) { - 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.excerpt_visible_offsets(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 { - 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 excerpt_visible_offsets( - &self, - restrict_to_languages: Option<&HashSet>>, - cx: &mut ViewContext<'_, '_, Editor>, - ) -> HashMap, Global, Range)> { - 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 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 { - font_cache: cx.font_cache().clone(), - text_layout_cache: cx.text_layout_cache().clone(), - editor_style: self.style(cx), - } - } - - fn splice_inlay_hints( - &self, - to_remove: Vec, - to_insert: Vec, - cx: &mut ViewContext, - ) { - 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, - ) -> Option>> { - 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()); - }); - } - editor.update(&mut cx, |editor, cx| { - editor.refresh_document_highlights(cx); - })?; - } - Ok(()) - })) - } - - fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext) { - 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 menu = if let Some(completions) = completions.await.log_err() { - 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, - list: Default::default(), - }; - menu.filter(query.as_deref(), cx.background()).await; - if menu.matches.is_empty() { - None - } else { - _ = this.update(&mut cx, |editor, cx| { - menu.pre_resolve_completion_documentation(editor.project.clone(), cx); - }); - Some(menu) - } - } else { - 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.focused && 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.is_empty() { - // 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); - } - } - })?; - - Ok::<_, anyhow::Error>(()) - } - .log_err() - }); - self.completion_tasks.push((id, task)); - } - - pub fn confirm_completion( - &mut self, - action: &ConfirmCompletion, - cx: &mut ViewContext, - ) -> Option>> { - 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::(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::(); - - 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> = 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(Event::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().spawn(async move { - apply_edits.await?; - Ok(()) - })) - } - - pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext) { - 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.focused { - 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(), - list: Default::default(), - deployed_from_indicator, - })); - } - } - })?; - - Ok::<_, anyhow::Error>(()) - }) - .detach_and_log_err(cx); - } - - pub fn confirm_code_action( - workspace: &mut Workspace, - action: &ConfirmCodeAction, - cx: &mut ViewContext, - ) -> Option>> { - let editor = workspace.active_item(cx)?.act_as::(cx)?; - let actions_menu = if let ContextMenu::CodeActions(menu) = - editor.update(cx, |editor, cx| editor.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 apply_code_actions = workspace.project().clone().update(cx, |project, cx| { - project.apply_code_action(buffer, action, true, cx) - }); - let editor = editor.downgrade(); - Some(cx.spawn(|workspace, 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: &WeakViewHandle, - workspace: WeakViewHandle, - transaction: ProjectTransaction, - title: String, - mut cx: AsyncAppContext, - ) -> Result<()> { - let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?; - - let mut entries = transaction.0.into_iter().collect::>(); - entries.sort_unstable_by_key(|(buffer, _)| { - buffer.read_with(&cx, |buffer, _| buffer.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.read_with(&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::(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.add_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::(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.add_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::( - ranges_to_highlight, - |theme| theme.editor.highlighted_line_background, - cx, - ); - }); - })?; - - Ok(()) - } - - fn refresh_code_actions(&mut self, cx: &mut ViewContext) -> 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().timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT).await; - - let actions = project - .update(&mut cx, |project, cx| { - project.code_actions(&start_buffer, start..end, cx) - }) - .await; - - this.update(&mut cx, |this, cx| { - this.available_code_actions = actions.log_err().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) -> 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() - .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT) - .await; - - let highlights = project - .update(&mut cx, |project, cx| { - project.document_highlights(&cursor_buffer, cursor_buffer_position, cx) - }) - .await - .log_err(); - - 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::( - read_ranges, - |theme| theme.editor.document_highlight_read_background, - cx, - ); - this.highlight_background::( - 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, - ) -> 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().timer(COPILOT_DEBOUNCE_TIMEOUT).await; - } - - let completions = copilot - .update(&mut cx, |copilot, cx| { - copilot.completions(&buffer, buffer_position, cx) - }) - .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, - ) -> 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) - }) - .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) { - 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) { - 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_action(); - } - } - } - - fn previous_copilot_suggestion( - &mut self, - _: &copilot::PreviousSuggestion, - cx: &mut ViewContext, - ) { - 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_action(); - } - } - } - - fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext) -> 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(Event::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) -> 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, - ) -> 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) -> Option { - 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) { - 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.copilot_state = Default::default(); - self.discard_copilot_suggestion(cx); - } - - pub fn render_code_actions_indicator( - &self, - style: &EditorStyle, - is_active: bool, - cx: &mut ViewContext, - ) -> Option> { - if self.available_code_actions.is_some() { - enum CodeActions {} - Some( - MouseEventHandler::new::(0, cx, |state, _| { - Svg::new("icons/bolt.svg").with_color( - style - .code_actions - .indicator - .in_state(is_active) - .style_for(state) - .color, - ) - }) - .with_cursor_style(CursorStyle::PointingHand) - .with_padding(Padding::uniform(3.)) - .on_down(MouseButton::Left, |_, this, cx| { - this.toggle_code_actions( - &ToggleCodeActions { - deployed_from_indicator: true, - }, - cx, - ); - }) - .into_any(), - ) - } else { - None - } - } - - pub fn render_fold_indicators( - &self, - fold_data: Vec>, - style: &EditorStyle, - gutter_hovered: bool, - line_height: f32, - gutter_margin: f32, - cx: &mut ViewContext, - ) -> Vec>> { - enum FoldIndicators {} - - let style = style.folds.clone(); - - fold_data - .iter() - .enumerate() - .map(|(ix, fold_data)| { - fold_data - .map(|(fold_status, buffer_row, active)| { - (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| { - MouseEventHandler::new::( - ix as usize, - cx, - |mouse_state, _| { - Svg::new(match fold_status { - FoldStatus::Folded => style.folded_icon.clone(), - FoldStatus::Foldable => style.foldable_icon.clone(), - }) - .with_color( - style - .indicator - .in_state(fold_status == FoldStatus::Folded) - .style_for(mouse_state) - .color, - ) - .constrained() - .with_width(gutter_margin * style.icon_margin_scale) - .aligned() - .constrained() - .with_height(line_height) - .with_width(gutter_margin) - .aligned() - }, - ) - .with_cursor_style(CursorStyle::PointingHand) - .with_padding(Padding::uniform(3.)) - .on_click(MouseButton::Left, { - move |_, editor, cx| match fold_status { - FoldStatus::Folded => { - editor.unfold_at(&UnfoldAt { buffer_row }, cx); - } - FoldStatus::Foldable => { - editor.fold_at(&FoldAt { buffer_row }, cx); - } - } - }) - .into_any() - }) - }) - .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, - cx: &mut ViewContext, - ) -> Option<(DisplayPoint, AnyElement)> { - self.context_menu.read().as_ref().map(|menu| { - menu.render( - cursor_position, - style, - self.workspace.as_ref().map(|(w, _)| w.clone()), - cx, - ) - }) - } - - fn hide_context_menu(&mut self, cx: &mut ViewContext) -> Option { - 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], - snippet: Snippet, - cx: &mut ViewContext, - ) -> Result<()> { - let tabstops = self.buffer.update(cx, |buffer, cx| { - let snippet_text: Arc = 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::>(); - tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot)); - tabstop_ranges - }) - .collect::>() - }); - - 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) -> bool { - self.move_to_snippet_tabstop(Bias::Right, cx) - } - - pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext) -> bool { - self.move_to_snippet_tabstop(Bias::Left, cx) - } - - pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext) -> 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.transact(cx, |this, cx| { - this.select_all(&SelectAll, cx); - this.insert("", cx); - }); - } - - pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext) { - self.transact(cx, |this, cx| { - this.select_autoclose_pair(cx); - let mut selections = this.selections.all::(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.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) { - if self.move_to_prev_snippet_tabstop(cx) { - return; - } - - self.outdent(&Outdent, cx); - } - - pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext) { - 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::())); - 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) { - let mut selections = self.selections.all::(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, - edits: &mut Vec<(Range, 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::(), - )); - - // 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) { - let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = self.selections.all::(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 = "".into(); - buffer.edit( - deletion_ranges - .into_iter() - .map(|range| (range, empty_str.clone())), - None, - cx, - ); - }); - let selections = this.selections.all::(cx); - this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections)); - }); - } - - pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext) { - let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = self.selections.all::(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 = "".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) { - let mut row_ranges = Vec::>::new(); - for selection in self.selections.all::(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.manipulate_lines(cx, |lines| lines.sort()) - } - - pub fn sort_lines_case_insensitive( - &mut self, - _: &SortLinesCaseInsensitive, - cx: &mut ViewContext, - ) { - self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase())) - } - - pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext) { - self.manipulate_lines(cx, |lines| lines.reverse()) - } - - pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext) { - self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng())) - } - - fn manipulate_lines(&mut self, cx: &mut ViewContext, 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::(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::(); - 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.manipulate_text(cx, |text| text.to_uppercase()) - } - - pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext) { - self.manipulate_text(cx, |text| text.to_lowercase()) - } - - pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext) { - 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.manipulate_text(cx, |text| text.to_case(Case::Snake)) - } - - pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext) { - self.manipulate_text(cx, |text| text.to_case(Case::Kebab)) - } - - pub fn convert_to_upper_camel_case( - &mut self, - _: &ConvertToUpperCamelCase, - cx: &mut ViewContext, - ) { - 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.manipulate_text(cx, |text| text.to_case(Case::Camel)) - } - - fn manipulate_text(&mut self, cx: &mut ViewContext, 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::(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::(); - 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) { - let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let buffer = &display_map.buffer_snapshot; - let selections = self.selections.all::(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::(); - 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) { - 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::(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::(); - - 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.start.to_point(&buffer); - let mut end = fold.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) { - 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::(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.start.to_point(&buffer); - let mut end = fold.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) { - 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, 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_point(head, &text_layout_details), - ); - 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::(cx); - this.change_selections(Some(Autoscroll::fit()), cx, |s| { - s.select(selections); - }); - }); - } - - pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext) { - let mut text = String::new(); - let buffer = self.buffer.read(cx).snapshot(cx); - let mut selections = self.selections.all::(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) { - let selections = self.selections.all::(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.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::>() { - let old_selections = this.selections.all::(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::(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) { - 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(Event::Edited); - } - } - - pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext) { - 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(Event::Edited); - } - } - - pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext) { - self.buffer - .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx)); - } - - pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext) { - 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.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.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.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) { - if self.take_rename(true, cx).is_some() { - return; - } - - if matches!(self.mode, EditorMode::SingleLine) { - cx.propagate_action(); - 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) { - if self.take_rename(true, cx).is_some() { - return; - } - - if matches!(self.mode, EditorMode::SingleLine) { - cx.propagate_action(); - 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) { - 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.take_rename(true, cx); - - if self.mode == EditorMode::SingleLine { - cx.propagate_action(); - 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) { - 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_action(); - 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) { - 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) { - 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) { - 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) { - 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) { - 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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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, - ) { - if matches!(self.mode, EditorMode::SingleLine) { - cx.propagate_action(); - 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, - ) { - if matches!(self.mode, EditorMode::SingleLine) { - cx.propagate_action(); - 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, - ) { - if matches!(self.mode, EditorMode::SingleLine) { - cx.propagate_action(); - 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, - ) { - if matches!(self.mode, EditorMode::SingleLine) { - cx.propagate_action(); - 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) { - if matches!(self.mode, EditorMode::SingleLine) { - cx.propagate_action(); - 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) { - let mut selection = self.selections.last::(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) { - if matches!(self.mode, EditorMode::SingleLine) { - cx.propagate_action(); - 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) { - 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, - cx: &mut ViewContext, - ) { - 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) { - let buffer = self.buffer.read(cx).snapshot(cx); - let mut selection = self.selections.first::(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) { - 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) { - let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let mut selections = self.selections.all::(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, - ) { - let mut to_unfold = Vec::new(); - let mut new_selection_ranges = Vec::new(); - { - let selections = self.selections.all::(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.add_selection(true, cx); - } - - pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext) { - self.add_selection(false, cx); - } - - fn add_selection(&mut self, above: bool, cx: &mut ViewContext) { - let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let mut selections = self.selections.all::(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_point(range.start, &text_layout_details); - let end_x = display_map.x_for_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 - { - start..end - } else { - let start_x = display_map.x_for_point(range.start, &text_layout_details); - let end_x = display_map.x_for_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, - cx: &mut ViewContext, - ) -> Result<()> { - fn select_next_match_ranges( - this: &mut Editor, - range: Range, - replace_newest: bool, - auto_scroll: Option, - cx: &mut ViewContext, - ) { - 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::(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::(); - - 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::(); - 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, - ) -> 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) -> 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, - ) -> 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::(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::(); - let query = query.chars().rev().collect::(); - 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::(); - let query = query.chars().rev().collect::(); - 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) { - let text_layout_details = &self.text_layout_details(cx); - self.transact(cx, |this, cx| { - let mut selections = this.selections.all::(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 = "".into(); - let mut suffixes_inserted = Vec::new(); - - fn comment_prefix_range( - snapshot: &MultiBufferSnapshot, - row: u32, - comment_prefix: &str, - comment_prefix_whitespace: &str, - ) -> Range { - 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 { - 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::(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::(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_point(display_point, &text_layout_details), - ); - (display_point, goal) - }) - }); - } - }); - } - - pub fn select_larger_syntax_node( - &mut self, - _: &SelectLargerSyntaxNode, - cx: &mut ViewContext, - ) { - 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::(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::>(); - - 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, - ) { - 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.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.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.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.go_to_diagnostic_impl(Direction::Next, cx) - } - - fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext) { - self.go_to_diagnostic_impl(Direction::Prev, cx) - } - - pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext) { - let buffer = self.buffer.read(cx).snapshot(cx); - let selection = self.selections.newest::(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) { - let snapshot = self - .display_map - .update(cx, |display_map, cx| display_map.snapshot(cx)); - let selection = self.selections.newest::(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) { - let snapshot = self - .display_map - .update(cx, |display_map, cx| display_map.snapshot(cx)); - let selection = self.selections.newest::(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>, - cx: &mut ViewContext, - ) -> 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.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx); - } - - pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext) { - self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx); - } - - pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext) { - self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx); - } - - pub fn go_to_type_definition_split( - &mut self, - _: &GoToTypeDefinitionSplit, - cx: &mut ViewContext, - ) { - 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, - ) { - let Some(workspace) = self.workspace(cx) else { - return; - }; - let buffer = self.buffer.read(cx); - let head = self.selections.newest::(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_labeled("Fetching Definition...", |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, - split: bool, - cx: &mut ViewContext, - ) { - let Some(workspace) = self.workspace(cx) 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: ViewHandle = - 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::() - ) - }) - } - 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::>(); - (title, location_tasks) - }) - .context("location tasks preparation")?; - - let locations = futures::future::join_all(location_tasks) - .await - .into_iter() - .filter_map(|location| location.transpose()) - .collect::>() - .context("location tasks")?; - workspace.update(&mut cx, |workspace, cx| { - Self::open_locations_in_multibuffer( - workspace, locations, replica_id, title, split, cx, - ) - }); - - anyhow::Ok(()) - }) - .detach_and_log_err(cx); - } - } - - fn compute_target_location( - &self, - lsp_location: lsp::Location, - server_id: LanguageServerId, - cx: &mut ViewContext, - ) -> Task>> { - 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( - workspace: &mut Workspace, - _: &FindAllReferences, - cx: &mut ViewContext, - ) -> Option>> { - let active_item = workspace.active_item(cx)?; - let editor_handle = active_item.act_as::(cx)?; - - let editor = editor_handle.read(cx); - let buffer = editor.buffer.read(cx); - let head = editor.selections.newest::(cx).head(); - let (buffer, head) = buffer.text_anchor_for_position(head, cx)?; - let replica_id = editor.replica_id(cx); - - let project = workspace.project().clone(); - let references = project.update(cx, |project, cx| project.references(&buffer, head, cx)); - Some(cx.spawn_labeled( - "Finding All References...", - |workspace, 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::() - ) - }) - .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, - replica_id: ReplicaId, - title: String, - split: bool, - cx: &mut ViewContext, - ) { - // 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.add_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.add_view(|cx| { - Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx) - }); - editor.update(cx, |editor, cx| { - editor.highlight_background::( - 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) -> Option>> { - 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 style = this.style(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 = 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::() - .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.add_view(|cx| { - let mut editor = Editor::single_line(None, cx); - if let Some(old_highlight_id) = old_highlight_id { - editor.override_text_style = - Some(Box::new(move |style| old_highlight_id.style(&style.syntax))); - } - 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::(cx) - .into_iter() - .flat_map(|(_, ranges)| ranges.into_iter()) - .chain( - this.clear_background_highlights::(cx) - .into_iter() - .flat_map(|(_, ranges)| ranges.into_iter()), - ) - .collect(); - - this.highlight_text::( - ranges, - HighlightStyle { - fade_out: Some(style.rename_fade), - ..Default::default() - }, - cx, - ); - cx.focus(&rename_editor); - let block_id = this.insert_blocks( - [BlockProperties { - style: BlockStyle::Flex, - position: range.start.clone(), - height: 1, - render: Arc::new({ - let editor = rename_editor.clone(); - move |cx: &mut BlockContext| { - ChildView::new(&editor, cx) - .contained() - .with_padding_left(cx.anchor_x) - .into_any() - } - }), - 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( - workspace: &mut Workspace, - _: &ConfirmRename, - cx: &mut ViewContext, - ) -> Option>> { - let editor = workspace.active_item(cx)?.act_as::(cx)?; - - let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| { - let rename = editor.take_rename(false, cx)?; - let buffer = editor.buffer.read(cx); - let (start_buffer, start) = - buffer.text_anchor_for_position(rename.range.start.clone(), cx)?; - let (end_buffer, end) = - buffer.text_anchor_for_position(rename.range.end.clone(), cx)?; - if start_buffer == end_buffer { - let new_name = rename.editor.read(cx).text(cx); - Some((start_buffer, start..end, rename.old_name, new_name)) - } else { - None - } - })?; - - let rename = workspace.project().clone().update(cx, |project, cx| { - project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx) - }); - - let editor = editor.downgrade(); - Some(cx.spawn(|workspace, 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, - ) -> Option { - let rename = self.pending_rename.take()?; - self.remove_blocks( - [rename.block_id].into_iter().collect(), - Some(Autoscroll::fit()), - cx, - ); - self.clear_highlights::(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::(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) -> Option>> { - 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: ModelHandle, - trigger: FormatTrigger, - cx: &mut ViewContext, - ) -> Task> { - let buffer = self.buffer().clone(); - let buffers = buffer.read(cx).all_buffers(); - - let mut timeout = cx.background().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(()) - }) - } - - fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext) { - 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) { - cx.show_character_palette(); - } - - fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext) { - 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) -> 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::(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::>(); - 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) { - 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>, - pending_selection: Option>, - cx: &mut ViewContext, - ) { - 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, - update: impl FnOnce(&mut Self, &mut ViewContext), - ) -> Option { - 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.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, - ) -> Option { - 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 { - error!("unexpectedly ended a transaction that wasn't started by this editor"); - } - - cx.emit(Event::Edited); - Some(tx_id) - } else { - None - } - } - - pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext) { - 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) { - 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::(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) { - let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let buffer = &display_map.buffer_snapshot; - let selections = self.selections.all::(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::>(); - - self.unfold_ranges(ranges, true, true, cx); - } - - pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext) { - 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::(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) { - let selections = self.selections.all::(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( - &mut self, - ranges: impl IntoIterator>, - auto_scroll: bool, - cx: &mut ViewContext, - ) { - 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( - &mut self, - ranges: impl IntoIterator>, - inclusive: bool, - auto_scroll: bool, - cx: &mut ViewContext, - ) { - 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 gutter_hover( - &mut self, - GutterHover { hovered }: &GutterHover, - cx: &mut ViewContext, - ) { - self.gutter_hovered = *hovered; - cx.notify(); - } - - pub fn insert_blocks( - &mut self, - blocks: impl IntoIterator>, - autoscroll: Option, - cx: &mut ViewContext, - ) -> Vec { - 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, - autoscroll: Option, - cx: &mut ViewContext, - ) { - 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, - autoscroll: Option, - cx: &mut ViewContext, - ) { - 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 set_text(&mut self, text: impl Into>, cx: &mut ViewContext) { - 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.soft_wrap_mode_override = Some(mode); - cx.notify(); - } - - pub fn set_wrap_width(&self, width: Option, 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) { - 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.show_gutter = show_gutter; - cx.notify(); - } - - pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext) { - self.show_wrap_guides = Some(show_gutter); - cx.notify(); - } - - pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext) { - 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) { - 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) { - 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>) { - self.highlighted_rows = rows; - } - - pub fn highlighted_rows(&self) -> Option> { - self.highlighted_rows.clone() - } - - pub fn highlight_background( - &mut self, - ranges: Vec>, - color_fetcher: fn(&Theme) -> Color, - cx: &mut ViewContext, - ) { - self.background_highlights - .insert(TypeId::of::(), (color_fetcher, ranges)); - cx.notify(); - } - - pub fn highlight_inlay_background( - &mut self, - ranges: Vec, - color_fetcher: fn(&Theme) -> Color, - cx: &mut ViewContext, - ) { - // TODO: no actual highlights happen for inlays currently, find a way to do that - self.inlay_background_highlights - .insert(Some(TypeId::of::()), (color_fetcher, ranges)); - cx.notify(); - } - - pub fn clear_background_highlights( - &mut self, - cx: &mut ViewContext, - ) -> Option { - let text_highlights = self.background_highlights.remove(&TypeId::of::()); - let inlay_highlights = self - .inlay_background_highlights - .remove(&Some(TypeId::of::())); - 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, - ) -> Vec<(Range, Color)> { - 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 = theme::current(cx); - self.background_highlights_in_range(start..end, &snapshot, theme.as_ref()) - } - - fn document_highlights_for_position<'a>( - &'a self, - position: Anchor, - buffer: &'a MultiBufferSnapshot, - ) -> impl 'a + Iterator> { - let read_highlights = self - .background_highlights - .get(&TypeId::of::()) - .map(|h| &h.1); - let write_highlights = self - .background_highlights - .get(&TypeId::of::()) - .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, - display_snapshot: &DisplaySnapshot, - theme: &Theme, - ) -> Vec<(Range, Color)> { - 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( - &self, - search_range: Range, - display_snapshot: &DisplaySnapshot, - count: usize, - ) -> Vec> { - let mut results = Vec::new(); - let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::()) 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, end: Option| { - 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 = None; - let mut end_row: Option = 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( - &mut self, - ranges: Vec>, - style: HighlightStyle, - cx: &mut ViewContext, - ) { - self.display_map.update(cx, |map, _| { - map.highlight_text(TypeId::of::(), ranges, style) - }); - cx.notify(); - } - - pub fn highlight_inlays( - &mut self, - highlights: Vec, - style: HighlightStyle, - cx: &mut ViewContext, - ) { - self.display_map.update(cx, |map, _| { - map.highlight_inlays(TypeId::of::(), highlights, style) - }); - cx.notify(); - } - - pub fn text_highlights<'a, T: 'static>( - &'a self, - cx: &'a AppContext, - ) -> Option<(HighlightStyle, &'a [Range])> { - self.display_map.read(cx).text_highlights(TypeId::of::()) - } - - pub fn clear_highlights(&mut self, cx: &mut ViewContext) { - let cleared = self - .display_map - .update(cx, |map, _| map.clear_highlights(TypeId::of::())); - if cleared { - cx.notify(); - } - } - - pub fn show_local_cursors(&self, cx: &AppContext) -> bool { - self.blink_manager.read(cx).visible() && self.focused - } - - fn on_buffer_changed(&mut self, _: ModelHandle, cx: &mut ViewContext) { - cx.notify(); - } - - fn on_buffer_event( - &mut self, - multibuffer: ModelHandle, - event: &multi_buffer::Event, - cx: &mut ViewContext, - ) { - 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(Event::BufferEdited); - - 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::>(); - if !languages_affected.is_empty() { - self.refresh_inlay_hints( - InlayHintRefreshReason::BufferEdited(languages_affected), - cx, - ); - } - } - } - } - multi_buffer::Event::ExcerptsAdded { - buffer, - predecessor, - excerpts, - } => { - cx.emit(Event::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(Event::ExcerptsRemoved { ids: ids.clone() }) - } - multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed), - multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged), - multi_buffer::Event::Saved => cx.emit(Event::Saved), - multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged), - multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged), - multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged), - multi_buffer::Event::Closed => cx.emit(Event::Closed), - multi_buffer::Event::DiagnosticsUpdated => { - self.refresh_active_diagnostics(cx); - } - _ => {} - }; - } - - fn on_display_map_changed(&mut self, _: ModelHandle, cx: &mut ViewContext) { - cx.notify(); - } - - fn settings_changed(&mut self, cx: &mut ViewContext) { - 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(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext) { - let active_item = workspace.active_item(cx); - let editor_handle = if let Some(editor) = active_item - .as_ref() - .and_then(|item| item.act_as::(cx)) - { - editor - } else { - cx.propagate_action(); - return; - }; - - let editor = editor_handle.read(cx); - let buffer = editor.buffer.read(cx); - if buffer.is_singleton() { - cx.propagate_action(); - return; - } - - let mut new_selections_by_buffer = HashMap::default(); - for selection in editor.selections.all::(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) - } - } - - editor_handle.update(cx, |editor, cx| { - editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx); - }); - let pane = workspace.active_pane().clone(); - pane.update(cx, |pane, _| pane.disable_history()); - - // 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.defer(move |workspace, cx| { - for (buffer, ranges) in new_selections_by_buffer.into_iter() { - let editor = workspace.open_project_item::(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( - workspace: &mut Workspace, - path: ProjectPath, - position: Point, - anchor: language::Anchor, - cx: &mut ViewContext, - ) { - let editor = workspace.open_path(path, None, true, cx); - cx.spawn(|_, mut cx| async move { - let editor = editor - .await? - .downcast::() - .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>> { - let snapshot = self.buffer.read(cx).read(cx); - let (_, ranges) = self.text_highlights::(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, - cx: &AppContext, - ) -> Vec> { - let selections = self.selections.all::(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, - 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 = *settings::get::(cx); - - let event = ClickhouseEvent::Copilot { - suggestion_id, - suggestion_accepted, - file_extension, - }; - telemetry.report_clickhouse_event(event, telemetry_settings); - } - - #[cfg(any(test, feature = "test-support"))] - fn report_editor_event( - &self, - _operation: &'static str, - _file_extension: Option, - _cx: &AppContext, - ) { - } - - #[cfg(not(any(test, feature = "test-support")))] - fn report_editor_event( - &self, - operation: &'static str, - file_extension: Option, - 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::() - .raw_user_settings() - .get("vim_mode") - == Some(&serde_json::Value::Bool(true)); - let telemetry_settings = *settings::get::(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(); - let event = ClickhouseEvent::Editor { - file_extension, - vim_mode, - operation, - copilot_enabled, - copilot_enabled_for_language, - }; - telemetry.report_clickhouse_event(event, telemetry_settings) - } - - /// 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) { - 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 = VecDeque::new(); - - let theme = &theme::current(cx).editor.syntax; - - for chunk in chunks { - let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme)); - 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>, - cx: &mut ViewContext, - ) { - if !self.input_enabled { - cx.emit(Event::InputIgnored { text: text.into() }); - return; - } - if let Some(relative_utf16_range) = relative_utf16_range { - let selections = self.selections.all::(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 - } -} +// impl Editor { +// pub fn single_line( +// field_editor_style: Option>, +// cx: &mut ViewContext, +// ) -> Self { +// let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new())); +// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); +// Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx) +// } + +// pub fn multi_line( +// field_editor_style: Option>, +// cx: &mut ViewContext, +// ) -> Self { +// let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new())); +// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); +// Self::new(EditorMode::Full, buffer, None, field_editor_style, cx) +// } + +// pub fn auto_height( +// max_lines: usize, +// field_editor_style: Option>, +// cx: &mut ViewContext, +// ) -> Self { +// let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new())); +// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); +// Self::new( +// EditorMode::AutoHeight { max_lines }, +// buffer, +// None, +// field_editor_style, +// cx, +// ) +// } + +// pub fn for_buffer( +// buffer: Model, +// project: Option>, +// cx: &mut ViewContext, +// ) -> Self { +// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); +// Self::new(EditorMode::Full, buffer, project, None, cx) +// } + +// pub fn for_multibuffer( +// buffer: Model, +// project: Option>, +// cx: &mut ViewContext, +// ) -> Self { +// Self::new(EditorMode::Full, buffer, project, None, cx) +// } + +// pub fn clone(&self, cx: &mut ViewContext) -> Self { +// let mut clone = Self::new( +// self.mode, +// self.buffer.clone(), +// self.project.clone(), +// self.get_field_editor_theme.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, +// project: Option>, +// get_field_editor_theme: Option>, +// cx: &mut ViewContext, +// ) -> Self { +// let editor_view_id = cx.view_id(); +// let display_map = cx.add_model(|cx| { +// let settings = settings::get::(cx); +// let style = build_style(settings, get_field_editor_theme.as_deref(), None, cx); +// DisplayMap::new( +// buffer.clone(), +// style.text.font_id, +// style.text.font_size, +// None, +// 2, +// 1, +// cx, +// ) +// }); + +// let selections = SelectionsCollection::new(display_map.clone(), buffer.clone()); + +// let blink_manager = cx.add_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(Event::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 mut this = Self { +// handle: cx.weak_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, +// get_field_editor_theme, +// collaboration_hub: project.clone().map(|project| Box::new(project) as _), +// project, +// focused: false, +// 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: cx +// .add_view(|cx| context_menu::ContextMenu::new(editor_view_id, cx)), +// 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, +// override_text_style: None, +// 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, +// _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::(Self::settings_changed), +// cx.observe_window_activation(|editor, active, cx| { +// 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); + +// let editor_created_event = EditorCreated(cx.handle()); +// cx.emit_global(editor_created_event); + +// if mode == EditorMode::Full { +// let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars(); +// cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars)); +// } + +// this.report_editor_event("open", None, cx); +// this +// } + +// pub fn new_file( +// workspace: &mut Workspace, +// _: &workspace::NewFile, +// cx: &mut ViewContext, +// ) { +// let project = workspace.project().clone(); +// if project.read(cx).is_remote() { +// cx.propagate_action(); +// } else if let Some(buffer) = project +// .update(cx, |project, cx| project.create_buffer("", None, cx)) +// .log_err() +// { +// workspace.add_item( +// Box::new(cx.add_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, +// ) { +// let project = workspace.project().clone(); +// if project.read(cx).is_remote() { +// cx.propagate_action(); +// } 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.add_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 { +// self.leader_peer_id +// } + +// pub fn buffer(&self) -> &Model { +// &self.buffer +// } + +// fn workspace(&self, cx: &AppContext) -> Option> { +// self.workspace.as_ref()?.0.upgrade(cx) +// } + +// 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 +// .handle +// .upgrade(cx) +// .map_or(false, |handle| handle.is_focused(cx)), +// } +// } + +// pub fn language_at<'a, T: ToOffset>( +// &self, +// point: T, +// cx: &'a AppContext, +// ) -> Option> { +// self.buffer.read(cx).language_at(point, cx) +// } + +// pub fn file_at<'a, T: ToOffset>(&self, point: T, cx: &'a AppContext) -> Option> { +// self.buffer.read(cx).read(cx).file_at(point).cloned() +// } + +// pub fn active_excerpt( +// &self, +// cx: &AppContext, +// ) -> Option<(ExcerptId, Model, Range)> { +// self.buffer +// .read(cx) +// .excerpt_containing(self.selections.newest_anchor().head(), cx) +// } + +// pub fn style(&self, cx: &AppContext) -> EditorStyle { +// build_style( +// settings::get::(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) { +// self.collaboration_hub = Some(hub); +// } + +// pub fn set_placeholder_text( +// &mut self, +// placeholder_text: impl Into>, +// cx: &mut ViewContext, +// ) { +// self.placeholder_text = Some(placeholder_text.into()); +// cx.notify(); +// } + +// pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext) { +// 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(&self, range: &Range) -> Range { +// 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) { +// 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( +// &mut self, +// context: KeymapContext, +// cx: &mut ViewContext, +// ) { +// self.keymap_context_layers +// .insert(TypeId::of::(), context); +// cx.notify(); +// } + +// pub fn remove_keymap_context_layer(&mut self, cx: &mut ViewContext) { +// self.keymap_context_layers.remove(&TypeId::of::()); +// 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; +// } + +// pub fn set_field_editor_style( +// &mut self, +// style: Option>, +// cx: &mut ViewContext, +// ) { +// self.get_field_editor_theme = style; +// cx.notify(); +// } + +// fn selections_did_change( +// &mut self, +// local: bool, +// old_cursor_position: &Anchor, +// cx: &mut ViewContext, +// ) { +// if self.focused && 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().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(Event::SelectionsChanged { local }); +// cx.notify(); +// } + +// pub fn change_selections( +// &mut self, +// autoscroll: Option, +// cx: &mut ViewContext, +// 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(&mut self, edits: I, cx: &mut ViewContext) +// where +// I: IntoIterator, T)>, +// S: ToOffset, +// T: Into>, +// { +// if self.read_only { +// return; +// } + +// self.buffer +// .update(cx, |buffer, cx| buffer.edit(edits, None, cx)); +// } + +// pub fn edit_with_autoindent(&mut self, edits: I, cx: &mut ViewContext) +// where +// I: IntoIterator, T)>, +// S: ToOffset, +// T: Into>, +// { +// 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( +// &mut self, +// edits: I, +// original_indent_columns: Vec, +// cx: &mut ViewContext, +// ) where +// I: IntoIterator, T)>, +// S: ToOffset, +// T: Into>, +// { +// 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.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, +// ) { +// let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); +// let tail = self.selections.newest::(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, +// ) { +// if !self.focused { +// cx.focus_self(); +// } + +// 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, +// ) { +// if !self.focused { +// cx.focus_self(); +// } + +// let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); +// let tail = self.selections.newest::(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: Vector2F, +// cx: &mut ViewContext, +// ) { +// 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 { +// 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.columnar_selection_tail.take(); +// if self.selections.pending_anchor().is_some() { +// let selections = self.selections.all::(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, +// ) { +// 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::>(); + +// 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) { +// 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_action(); +// } + +// pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext) { +// let text: Arc = 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::(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::>(); + +// 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 && settings::get::(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.transact(cx, |this, cx| { +// let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = { +// let selections = this.selections.all::(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::(); + +// let trailing_whitespace_len = buffer +// .chars_at(end) +// .take_while(|c| c.is_whitespace() && *c != '\n') +// .map(|c| c.len_utf8()) +// .sum::(); + +// 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) { +// 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) { +// 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.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, +// cx: &mut ViewContext, +// ) { +// if self.read_only { +// return; +// } + +// let text: Arc = 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::>() +// }; +// 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) { +// if !settings::get::(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) { +// let selections = self.selections.all::(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, ®ion.pair.start) { +// if buffer.contains_str_at(range.end, ®ion.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>, +// buffer: &'a MultiBufferSnapshot, +// ) -> impl Iterator, 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 = ®ions[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], +// 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 { +// 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::(), +// ) +// } else { +// None +// } +// } + +// pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext) { +// todo!(); +// // self.refresh_inlay_hints( +// // InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled), +// // cx, +// // ); +// } + +// pub fn inlay_hints_enabled(&self) -> bool { +// todo!(); +// self.inlay_hint_cache.enabled +// } + +// fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext) { +// 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.excerpt_visible_offsets(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 { +// 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 excerpt_visible_offsets( +// &self, +// restrict_to_languages: Option<&HashSet>>, +// cx: &mut ViewContext<'_, '_, Editor>, +// ) -> HashMap, Global, Range)> { +// 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 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 { +// font_cache: cx.font_cache().clone(), +// text_layout_cache: cx.text_layout_cache().clone(), +// editor_style: self.style(cx), +// } +// } + +// fn splice_inlay_hints( +// &self, +// to_remove: Vec, +// to_insert: Vec, +// cx: &mut ViewContext, +// ) { +// 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, +// ) -> Option>> { +// 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()); +// }); +// } +// editor.update(&mut cx, |editor, cx| { +// editor.refresh_document_highlights(cx); +// })?; +// } +// Ok(()) +// })) +// } + +// fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext) { +// 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 menu = if let Some(completions) = completions.await.log_err() { +// 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, +// list: Default::default(), +// }; +// menu.filter(query.as_deref(), cx.background()).await; +// if menu.matches.is_empty() { +// None +// } else { +// _ = this.update(&mut cx, |editor, cx| { +// menu.pre_resolve_completion_documentation(editor.project.clone(), cx); +// }); +// Some(menu) +// } +// } else { +// 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.focused && 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.is_empty() { +// // 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); +// } +// } +// })?; + +// Ok::<_, anyhow::Error>(()) +// } +// .log_err() +// }); +// self.completion_tasks.push((id, task)); +// } + +// pub fn confirm_completion( +// &mut self, +// action: &ConfirmCompletion, +// cx: &mut ViewContext, +// ) -> Option>> { +// 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::(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::(); + +// 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> = 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(Event::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().spawn(async move { +// apply_edits.await?; +// Ok(()) +// })) +// } + +// pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext) { +// 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.focused { +// 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(), +// list: Default::default(), +// deployed_from_indicator, +// })); +// } +// } +// })?; + +// Ok::<_, anyhow::Error>(()) +// }) +// .detach_and_log_err(cx); +// } + +// pub fn confirm_code_action( +// workspace: &mut Workspace, +// action: &ConfirmCodeAction, +// cx: &mut ViewContext, +// ) -> Option>> { +// let editor = workspace.active_item(cx)?.act_as::(cx)?; +// let actions_menu = if let ContextMenu::CodeActions(menu) = +// editor.update(cx, |editor, cx| editor.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 apply_code_actions = workspace.project().clone().update(cx, |project, cx| { +// project.apply_code_action(buffer, action, true, cx) +// }); +// let editor = editor.downgrade(); +// Some(cx.spawn(|workspace, 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: &WeakViewHandle, +// workspace: WeakViewHandle, +// transaction: ProjectTransaction, +// title: String, +// mut cx: AsyncAppContext, +// ) -> Result<()> { +// let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?; + +// let mut entries = transaction.0.into_iter().collect::>(); +// entries.sort_unstable_by_key(|(buffer, _)| { +// buffer.read_with(&cx, |buffer, _| buffer.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.read_with(&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::(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.add_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::(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.add_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::( +// ranges_to_highlight, +// |theme| theme.editor.highlighted_line_background, +// cx, +// ); +// }); +// })?; + +// Ok(()) +// } + +// fn refresh_code_actions(&mut self, cx: &mut ViewContext) -> 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().timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT).await; + +// let actions = project +// .update(&mut cx, |project, cx| { +// project.code_actions(&start_buffer, start..end, cx) +// }) +// .await; + +// this.update(&mut cx, |this, cx| { +// this.available_code_actions = actions.log_err().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) -> 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() +// .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT) +// .await; + +// let highlights = project +// .update(&mut cx, |project, cx| { +// project.document_highlights(&cursor_buffer, cursor_buffer_position, cx) +// }) +// .await +// .log_err(); + +// 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::( +// read_ranges, +// |theme| theme.editor.document_highlight_read_background, +// cx, +// ); +// this.highlight_background::( +// 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, +// ) -> 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().timer(COPILOT_DEBOUNCE_TIMEOUT).await; +// } + +// let completions = copilot +// .update(&mut cx, |copilot, cx| { +// copilot.completions(&buffer, buffer_position, cx) +// }) +// .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, +// ) -> 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) +// }) +// .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) { +// 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) { +// 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_action(); +// } +// } +// } + +// fn previous_copilot_suggestion( +// &mut self, +// _: &copilot::PreviousSuggestion, +// cx: &mut ViewContext, +// ) { +// 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_action(); +// } +// } +// } + +// fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext) -> 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(Event::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) -> 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, +// ) -> 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) -> Option { +// 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) { +// 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.copilot_state = Default::default(); +// self.discard_copilot_suggestion(cx); +// } + +// pub fn render_code_actions_indicator( +// &self, +// style: &EditorStyle, +// is_active: bool, +// cx: &mut ViewContext, +// ) -> Option> { +// if self.available_code_actions.is_some() { +// enum CodeActions {} +// Some( +// MouseEventHandler::new::(0, cx, |state, _| { +// Svg::new("icons/bolt.svg").with_color( +// style +// .code_actions +// .indicator +// .in_state(is_active) +// .style_for(state) +// .color, +// ) +// }) +// .with_cursor_style(CursorStyle::PointingHand) +// .with_padding(Padding::uniform(3.)) +// .on_down(MouseButton::Left, |_, this, cx| { +// this.toggle_code_actions( +// &ToggleCodeActions { +// deployed_from_indicator: true, +// }, +// cx, +// ); +// }) +// .into_any(), +// ) +// } else { +// None +// } +// } + +// pub fn render_fold_indicators( +// &self, +// fold_data: Vec>, +// style: &EditorStyle, +// gutter_hovered: bool, +// line_height: f32, +// gutter_margin: f32, +// cx: &mut ViewContext, +// ) -> Vec>> { +// enum FoldIndicators {} + +// let style = style.folds.clone(); + +// fold_data +// .iter() +// .enumerate() +// .map(|(ix, fold_data)| { +// fold_data +// .map(|(fold_status, buffer_row, active)| { +// (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| { +// MouseEventHandler::new::( +// ix as usize, +// cx, +// |mouse_state, _| { +// Svg::new(match fold_status { +// FoldStatus::Folded => style.folded_icon.clone(), +// FoldStatus::Foldable => style.foldable_icon.clone(), +// }) +// .with_color( +// style +// .indicator +// .in_state(fold_status == FoldStatus::Folded) +// .style_for(mouse_state) +// .color, +// ) +// .constrained() +// .with_width(gutter_margin * style.icon_margin_scale) +// .aligned() +// .constrained() +// .with_height(line_height) +// .with_width(gutter_margin) +// .aligned() +// }, +// ) +// .with_cursor_style(CursorStyle::PointingHand) +// .with_padding(Padding::uniform(3.)) +// .on_click(MouseButton::Left, { +// move |_, editor, cx| match fold_status { +// FoldStatus::Folded => { +// editor.unfold_at(&UnfoldAt { buffer_row }, cx); +// } +// FoldStatus::Foldable => { +// editor.fold_at(&FoldAt { buffer_row }, cx); +// } +// } +// }) +// .into_any() +// }) +// }) +// .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, +// cx: &mut ViewContext, +// ) -> Option<(DisplayPoint, AnyElement)> { +// self.context_menu.read().as_ref().map(|menu| { +// menu.render( +// cursor_position, +// style, +// self.workspace.as_ref().map(|(w, _)| w.clone()), +// cx, +// ) +// }) +// } + +// fn hide_context_menu(&mut self, cx: &mut ViewContext) -> Option { +// 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], +// snippet: Snippet, +// cx: &mut ViewContext, +// ) -> Result<()> { +// let tabstops = self.buffer.update(cx, |buffer, cx| { +// let snippet_text: Arc = 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::>(); +// tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot)); +// tabstop_ranges +// }) +// .collect::>() +// }); + +// 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) -> bool { +// self.move_to_snippet_tabstop(Bias::Right, cx) +// } + +// pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext) -> bool { +// self.move_to_snippet_tabstop(Bias::Left, cx) +// } + +// pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext) -> 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.transact(cx, |this, cx| { +// this.select_all(&SelectAll, cx); +// this.insert("", cx); +// }); +// } + +// pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext) { +// self.transact(cx, |this, cx| { +// this.select_autoclose_pair(cx); +// let mut selections = this.selections.all::(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.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) { +// if self.move_to_prev_snippet_tabstop(cx) { +// return; +// } + +// self.outdent(&Outdent, cx); +// } + +// pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext) { +// 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::())); +// 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) { +// let mut selections = self.selections.all::(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, +// edits: &mut Vec<(Range, 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::(), +// )); + +// // 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) { +// let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); +// let selections = self.selections.all::(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 = "".into(); +// buffer.edit( +// deletion_ranges +// .into_iter() +// .map(|range| (range, empty_str.clone())), +// None, +// cx, +// ); +// }); +// let selections = this.selections.all::(cx); +// this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections)); +// }); +// } + +// pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext) { +// let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); +// let selections = self.selections.all::(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 = "".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) { +// let mut row_ranges = Vec::>::new(); +// for selection in self.selections.all::(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.manipulate_lines(cx, |lines| lines.sort()) +// } + +// pub fn sort_lines_case_insensitive( +// &mut self, +// _: &SortLinesCaseInsensitive, +// cx: &mut ViewContext, +// ) { +// self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase())) +// } + +// pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext) { +// self.manipulate_lines(cx, |lines| lines.reverse()) +// } + +// pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext) { +// self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng())) +// } + +// fn manipulate_lines(&mut self, cx: &mut ViewContext, 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::(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::(); +// 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.manipulate_text(cx, |text| text.to_uppercase()) +// } + +// pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext) { +// self.manipulate_text(cx, |text| text.to_lowercase()) +// } + +// pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext) { +// 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.manipulate_text(cx, |text| text.to_case(Case::Snake)) +// } + +// pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext) { +// self.manipulate_text(cx, |text| text.to_case(Case::Kebab)) +// } + +// pub fn convert_to_upper_camel_case( +// &mut self, +// _: &ConvertToUpperCamelCase, +// cx: &mut ViewContext, +// ) { +// 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.manipulate_text(cx, |text| text.to_case(Case::Camel)) +// } + +// fn manipulate_text(&mut self, cx: &mut ViewContext, 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::(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::(); +// 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) { +// let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); +// let buffer = &display_map.buffer_snapshot; +// let selections = self.selections.all::(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::(); +// 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) { +// 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::(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::(); + +// 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.start.to_point(&buffer); +// let mut end = fold.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) { +// 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::(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.start.to_point(&buffer); +// let mut end = fold.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) { +// 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, 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_point(head, &text_layout_details), +// ); +// 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::(cx); +// this.change_selections(Some(Autoscroll::fit()), cx, |s| { +// s.select(selections); +// }); +// }); +// } + +// pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext) { +// let mut text = String::new(); +// let buffer = self.buffer.read(cx).snapshot(cx); +// let mut selections = self.selections.all::(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) { +// let selections = self.selections.all::(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.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::>() { +// let old_selections = this.selections.all::(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::(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) { +// 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(Event::Edited); +// } +// } + +// pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext) { +// 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(Event::Edited); +// } +// } + +// pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext) { +// self.buffer +// .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx)); +// } + +// pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext) { +// 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.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.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.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) { +// if self.take_rename(true, cx).is_some() { +// return; +// } + +// if matches!(self.mode, EditorMode::SingleLine) { +// cx.propagate_action(); +// 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) { +// if self.take_rename(true, cx).is_some() { +// return; +// } + +// if matches!(self.mode, EditorMode::SingleLine) { +// cx.propagate_action(); +// 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) { +// 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.take_rename(true, cx); + +// if self.mode == EditorMode::SingleLine { +// cx.propagate_action(); +// 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) { +// 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_action(); +// 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) { +// 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) { +// 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) { +// 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) { +// 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) { +// 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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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, +// ) { +// if matches!(self.mode, EditorMode::SingleLine) { +// cx.propagate_action(); +// 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, +// ) { +// if matches!(self.mode, EditorMode::SingleLine) { +// cx.propagate_action(); +// 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, +// ) { +// if matches!(self.mode, EditorMode::SingleLine) { +// cx.propagate_action(); +// 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, +// ) { +// if matches!(self.mode, EditorMode::SingleLine) { +// cx.propagate_action(); +// 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) { +// if matches!(self.mode, EditorMode::SingleLine) { +// cx.propagate_action(); +// 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) { +// let mut selection = self.selections.last::(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) { +// if matches!(self.mode, EditorMode::SingleLine) { +// cx.propagate_action(); +// 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) { +// 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, +// cx: &mut ViewContext, +// ) { +// 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) { +// let buffer = self.buffer.read(cx).snapshot(cx); +// let mut selection = self.selections.first::(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) { +// 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) { +// let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); +// let mut selections = self.selections.all::(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, +// ) { +// let mut to_unfold = Vec::new(); +// let mut new_selection_ranges = Vec::new(); +// { +// let selections = self.selections.all::(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.add_selection(true, cx); +// } + +// pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext) { +// self.add_selection(false, cx); +// } + +// fn add_selection(&mut self, above: bool, cx: &mut ViewContext) { +// let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); +// let mut selections = self.selections.all::(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_point(range.start, &text_layout_details); +// let end_x = display_map.x_for_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 +// { +// start..end +// } else { +// let start_x = display_map.x_for_point(range.start, &text_layout_details); +// let end_x = display_map.x_for_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, +// cx: &mut ViewContext, +// ) -> Result<()> { +// fn select_next_match_ranges( +// this: &mut Editor, +// range: Range, +// replace_newest: bool, +// auto_scroll: Option, +// cx: &mut ViewContext, +// ) { +// 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::(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::(); + +// 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::(); +// 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, +// ) -> 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) -> 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, +// ) -> 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::(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::(); +// let query = query.chars().rev().collect::(); +// 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::(); +// let query = query.chars().rev().collect::(); +// 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) { +// let text_layout_details = &self.text_layout_details(cx); +// self.transact(cx, |this, cx| { +// let mut selections = this.selections.all::(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 = "".into(); +// let mut suffixes_inserted = Vec::new(); + +// fn comment_prefix_range( +// snapshot: &MultiBufferSnapshot, +// row: u32, +// comment_prefix: &str, +// comment_prefix_whitespace: &str, +// ) -> Range { +// 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 { +// 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::(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::(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_point(display_point, &text_layout_details), +// ); +// (display_point, goal) +// }) +// }); +// } +// }); +// } + +// pub fn select_larger_syntax_node( +// &mut self, +// _: &SelectLargerSyntaxNode, +// cx: &mut ViewContext, +// ) { +// 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::(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::>(); + +// 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, +// ) { +// 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.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.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.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.go_to_diagnostic_impl(Direction::Next, cx) +// } + +// fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext) { +// self.go_to_diagnostic_impl(Direction::Prev, cx) +// } + +// pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext) { +// let buffer = self.buffer.read(cx).snapshot(cx); +// let selection = self.selections.newest::(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) { +// let snapshot = self +// .display_map +// .update(cx, |display_map, cx| display_map.snapshot(cx)); +// let selection = self.selections.newest::(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) { +// let snapshot = self +// .display_map +// .update(cx, |display_map, cx| display_map.snapshot(cx)); +// let selection = self.selections.newest::(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>, +// cx: &mut ViewContext, +// ) -> 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.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx); +// } + +// pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext) { +// self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx); +// } + +// pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext) { +// self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx); +// } + +// pub fn go_to_type_definition_split( +// &mut self, +// _: &GoToTypeDefinitionSplit, +// cx: &mut ViewContext, +// ) { +// 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, +// ) { +// let Some(workspace) = self.workspace(cx) else { +// return; +// }; +// let buffer = self.buffer.read(cx); +// let head = self.selections.newest::(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_labeled("Fetching Definition...", |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, +// split: bool, +// cx: &mut ViewContext, +// ) { +// let Some(workspace) = self.workspace(cx) 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: ViewHandle = +// 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::() +// ) +// }) +// } +// 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::>(); +// (title, location_tasks) +// }) +// .context("location tasks preparation")?; + +// let locations = futures::future::join_all(location_tasks) +// .await +// .into_iter() +// .filter_map(|location| location.transpose()) +// .collect::>() +// .context("location tasks")?; +// workspace.update(&mut cx, |workspace, cx| { +// Self::open_locations_in_multibuffer( +// workspace, locations, replica_id, title, split, cx, +// ) +// }); + +// anyhow::Ok(()) +// }) +// .detach_and_log_err(cx); +// } +// } + +// fn compute_target_location( +// &self, +// lsp_location: lsp::Location, +// server_id: LanguageServerId, +// cx: &mut ViewContext, +// ) -> Task>> { +// 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( +// workspace: &mut Workspace, +// _: &FindAllReferences, +// cx: &mut ViewContext, +// ) -> Option>> { +// let active_item = workspace.active_item(cx)?; +// let editor_handle = active_item.act_as::(cx)?; + +// let editor = editor_handle.read(cx); +// let buffer = editor.buffer.read(cx); +// let head = editor.selections.newest::(cx).head(); +// let (buffer, head) = buffer.text_anchor_for_position(head, cx)?; +// let replica_id = editor.replica_id(cx); + +// let project = workspace.project().clone(); +// let references = project.update(cx, |project, cx| project.references(&buffer, head, cx)); +// Some(cx.spawn_labeled( +// "Finding All References...", +// |workspace, 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::() +// ) +// }) +// .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, +// replica_id: ReplicaId, +// title: String, +// split: bool, +// cx: &mut ViewContext, +// ) { +// // 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.add_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.add_view(|cx| { +// Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx) +// }); +// editor.update(cx, |editor, cx| { +// editor.highlight_background::( +// 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) -> Option>> { +// 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 style = this.style(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 = 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::() +// .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.add_view(|cx| { +// let mut editor = Editor::single_line(None, cx); +// if let Some(old_highlight_id) = old_highlight_id { +// editor.override_text_style = +// Some(Box::new(move |style| old_highlight_id.style(&style.syntax))); +// } +// 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::(cx) +// .into_iter() +// .flat_map(|(_, ranges)| ranges.into_iter()) +// .chain( +// this.clear_background_highlights::(cx) +// .into_iter() +// .flat_map(|(_, ranges)| ranges.into_iter()), +// ) +// .collect(); + +// this.highlight_text::( +// ranges, +// HighlightStyle { +// fade_out: Some(style.rename_fade), +// ..Default::default() +// }, +// cx, +// ); +// cx.focus(&rename_editor); +// let block_id = this.insert_blocks( +// [BlockProperties { +// style: BlockStyle::Flex, +// position: range.start.clone(), +// height: 1, +// render: Arc::new({ +// let editor = rename_editor.clone(); +// move |cx: &mut BlockContext| { +// ChildView::new(&editor, cx) +// .contained() +// .with_padding_left(cx.anchor_x) +// .into_any() +// } +// }), +// 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( +// workspace: &mut Workspace, +// _: &ConfirmRename, +// cx: &mut ViewContext, +// ) -> Option>> { +// let editor = workspace.active_item(cx)?.act_as::(cx)?; + +// let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| { +// let rename = editor.take_rename(false, cx)?; +// let buffer = editor.buffer.read(cx); +// let (start_buffer, start) = +// buffer.text_anchor_for_position(rename.range.start.clone(), cx)?; +// let (end_buffer, end) = +// buffer.text_anchor_for_position(rename.range.end.clone(), cx)?; +// if start_buffer == end_buffer { +// let new_name = rename.editor.read(cx).text(cx); +// Some((start_buffer, start..end, rename.old_name, new_name)) +// } else { +// None +// } +// })?; + +// let rename = workspace.project().clone().update(cx, |project, cx| { +// project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx) +// }); + +// let editor = editor.downgrade(); +// Some(cx.spawn(|workspace, 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, +// ) -> Option { +// let rename = self.pending_rename.take()?; +// self.remove_blocks( +// [rename.block_id].into_iter().collect(), +// Some(Autoscroll::fit()), +// cx, +// ); +// self.clear_highlights::(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::(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) -> Option>> { +// 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, +// trigger: FormatTrigger, +// cx: &mut ViewContext, +// ) -> Task> { +// let buffer = self.buffer().clone(); +// let buffers = buffer.read(cx).all_buffers(); + +// let mut timeout = cx.background().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(()) +// }) +// } + +// fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext) { +// 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) { +// cx.show_character_palette(); +// } + +// fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext) { +// 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) -> 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::(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::>(); +// 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) { +// 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>, +// pending_selection: Option>, +// cx: &mut ViewContext, +// ) { +// 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, +// update: impl FnOnce(&mut Self, &mut ViewContext), +// ) -> Option { +// 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.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, +// ) -> Option { +// 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 { +// error!("unexpectedly ended a transaction that wasn't started by this editor"); +// } + +// cx.emit(Event::Edited); +// Some(tx_id) +// } else { +// None +// } +// } + +// pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext) { +// 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) { +// 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::(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) { +// let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); +// let buffer = &display_map.buffer_snapshot; +// let selections = self.selections.all::(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::>(); + +// self.unfold_ranges(ranges, true, true, cx); +// } + +// pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext) { +// 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::(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) { +// let selections = self.selections.all::(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( +// &mut self, +// ranges: impl IntoIterator>, +// auto_scroll: bool, +// cx: &mut ViewContext, +// ) { +// 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( +// &mut self, +// ranges: impl IntoIterator>, +// inclusive: bool, +// auto_scroll: bool, +// cx: &mut ViewContext, +// ) { +// 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 gutter_hover( +// &mut self, +// GutterHover { hovered }: &GutterHover, +// cx: &mut ViewContext, +// ) { +// self.gutter_hovered = *hovered; +// cx.notify(); +// } + +// pub fn insert_blocks( +// &mut self, +// blocks: impl IntoIterator>, +// autoscroll: Option, +// cx: &mut ViewContext, +// ) -> Vec { +// 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, +// autoscroll: Option, +// cx: &mut ViewContext, +// ) { +// 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, +// autoscroll: Option, +// cx: &mut ViewContext, +// ) { +// 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 set_text(&mut self, text: impl Into>, cx: &mut ViewContext) { +// 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.soft_wrap_mode_override = Some(mode); +// cx.notify(); +// } + +// pub fn set_wrap_width(&self, width: Option, 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) { +// 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.show_gutter = show_gutter; +// cx.notify(); +// } + +// pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext) { +// self.show_wrap_guides = Some(show_gutter); +// cx.notify(); +// } + +// pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext) { +// 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) { +// 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) { +// 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>) { +// self.highlighted_rows = rows; +// } + +// pub fn highlighted_rows(&self) -> Option> { +// self.highlighted_rows.clone() +// } + +// pub fn highlight_background( +// &mut self, +// ranges: Vec>, +// color_fetcher: fn(&Theme) -> Color, +// cx: &mut ViewContext, +// ) { +// self.background_highlights +// .insert(TypeId::of::(), (color_fetcher, ranges)); +// cx.notify(); +// } + +// pub fn highlight_inlay_background( +// &mut self, +// ranges: Vec, +// color_fetcher: fn(&Theme) -> Color, +// cx: &mut ViewContext, +// ) { +// // TODO: no actual highlights happen for inlays currently, find a way to do that +// self.inlay_background_highlights +// .insert(Some(TypeId::of::()), (color_fetcher, ranges)); +// cx.notify(); +// } + +// pub fn clear_background_highlights( +// &mut self, +// cx: &mut ViewContext, +// ) -> Option { +// let text_highlights = self.background_highlights.remove(&TypeId::of::()); +// let inlay_highlights = self +// .inlay_background_highlights +// .remove(&Some(TypeId::of::())); +// 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, +// ) -> Vec<(Range, Color)> { +// 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 = theme::current(cx); +// self.background_highlights_in_range(start..end, &snapshot, theme.as_ref()) +// } + +// fn document_highlights_for_position<'a>( +// &'a self, +// position: Anchor, +// buffer: &'a MultiBufferSnapshot, +// ) -> impl 'a + Iterator> { +// let read_highlights = self +// .background_highlights +// .get(&TypeId::of::()) +// .map(|h| &h.1); +// let write_highlights = self +// .background_highlights +// .get(&TypeId::of::()) +// .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, +// display_snapshot: &DisplaySnapshot, +// theme: &Theme, +// ) -> Vec<(Range, Color)> { +// 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( +// &self, +// search_range: Range, +// display_snapshot: &DisplaySnapshot, +// count: usize, +// ) -> Vec> { +// let mut results = Vec::new(); +// let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::()) 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, end: Option| { +// 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 = None; +// let mut end_row: Option = 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( +// &mut self, +// ranges: Vec>, +// style: HighlightStyle, +// cx: &mut ViewContext, +// ) { +// self.display_map.update(cx, |map, _| { +// map.highlight_text(TypeId::of::(), ranges, style) +// }); +// cx.notify(); +// } + +// pub fn highlight_inlays( +// &mut self, +// highlights: Vec, +// style: HighlightStyle, +// cx: &mut ViewContext, +// ) { +// self.display_map.update(cx, |map, _| { +// map.highlight_inlays(TypeId::of::(), highlights, style) +// }); +// cx.notify(); +// } + +// pub fn text_highlights<'a, T: 'static>( +// &'a self, +// cx: &'a AppContext, +// ) -> Option<(HighlightStyle, &'a [Range])> { +// self.display_map.read(cx).text_highlights(TypeId::of::()) +// } + +// pub fn clear_highlights(&mut self, cx: &mut ViewContext) { +// let cleared = self +// .display_map +// .update(cx, |map, _| map.clear_highlights(TypeId::of::())); +// if cleared { +// cx.notify(); +// } +// } + +// pub fn show_local_cursors(&self, cx: &AppContext) -> bool { +// self.blink_manager.read(cx).visible() && self.focused +// } + +// fn on_buffer_changed(&mut self, _: Model, cx: &mut ViewContext) { +// cx.notify(); +// } + +// fn on_buffer_event( +// &mut self, +// multibuffer: Model, +// event: &multi_buffer::Event, +// cx: &mut ViewContext, +// ) { +// 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(Event::BufferEdited); + +// 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::>(); +// if !languages_affected.is_empty() { +// self.refresh_inlay_hints( +// InlayHintRefreshReason::BufferEdited(languages_affected), +// cx, +// ); +// } +// } +// } +// } +// multi_buffer::Event::ExcerptsAdded { +// buffer, +// predecessor, +// excerpts, +// } => { +// cx.emit(Event::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(Event::ExcerptsRemoved { ids: ids.clone() }) +// } +// multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed), +// multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged), +// multi_buffer::Event::Saved => cx.emit(Event::Saved), +// multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged), +// multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged), +// multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged), +// multi_buffer::Event::Closed => cx.emit(Event::Closed), +// multi_buffer::Event::DiagnosticsUpdated => { +// self.refresh_active_diagnostics(cx); +// } +// _ => {} +// }; +// } + +// fn on_display_map_changed(&mut self, _: Model, cx: &mut ViewContext) { +// cx.notify(); +// } + +// fn settings_changed(&mut self, cx: &mut ViewContext) { +// 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(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext) { +// let active_item = workspace.active_item(cx); +// let editor_handle = if let Some(editor) = active_item +// .as_ref() +// .and_then(|item| item.act_as::(cx)) +// { +// editor +// } else { +// cx.propagate_action(); +// return; +// }; + +// let editor = editor_handle.read(cx); +// let buffer = editor.buffer.read(cx); +// if buffer.is_singleton() { +// cx.propagate_action(); +// return; +// } + +// let mut new_selections_by_buffer = HashMap::default(); +// for selection in editor.selections.all::(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) +// } +// } + +// editor_handle.update(cx, |editor, cx| { +// editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx); +// }); +// let pane = workspace.active_pane().clone(); +// pane.update(cx, |pane, _| pane.disable_history()); + +// // 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.defer(move |workspace, cx| { +// for (buffer, ranges) in new_selections_by_buffer.into_iter() { +// let editor = workspace.open_project_item::(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( +// workspace: &mut Workspace, +// path: ProjectPath, +// position: Point, +// anchor: language::Anchor, +// cx: &mut ViewContext, +// ) { +// let editor = workspace.open_path(path, None, true, cx); +// cx.spawn(|_, mut cx| async move { +// let editor = editor +// .await? +// .downcast::() +// .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>> { +// let snapshot = self.buffer.read(cx).read(cx); +// let (_, ranges) = self.text_highlights::(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, +// cx: &AppContext, +// ) -> Vec> { +// let selections = self.selections.all::(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, +// 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 = *settings::get::(cx); + +// let event = ClickhouseEvent::Copilot { +// suggestion_id, +// suggestion_accepted, +// file_extension, +// }; +// telemetry.report_clickhouse_event(event, telemetry_settings); +// } + +// #[cfg(any(test, feature = "test-support"))] +// fn report_editor_event( +// &self, +// _operation: &'static str, +// _file_extension: Option, +// _cx: &AppContext, +// ) { +// } + +// #[cfg(not(any(test, feature = "test-support")))] +// fn report_editor_event( +// &self, +// operation: &'static str, +// file_extension: Option, +// 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::() +// .raw_user_settings() +// .get("vim_mode") +// == Some(&serde_json::Value::Bool(true)); +// let telemetry_settings = *settings::get::(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(); +// let event = ClickhouseEvent::Editor { +// file_extension, +// vim_mode, +// operation, +// copilot_enabled, +// copilot_enabled_for_language, +// }; +// telemetry.report_clickhouse_event(event, telemetry_settings) +// } + +// /// 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) { +// 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 = VecDeque::new(); + +// let theme = &theme::current(cx).editor.syntax; + +// for chunk in chunks { +// let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme)); +// 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>, +// cx: &mut ViewContext, +// ) { +// if !self.input_enabled { +// cx.emit(Event::InputIgnored { text: text.into() }); +// return; +// } +// if let Some(relative_utf16_range) = relative_utf16_range { +// let selections = self.selections.all::(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 trait CollaborationHub { fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap; @@ -9148,7 +9141,7 @@ pub trait CollaborationHub { ) -> &'a HashMap; } -impl CollaborationHub for ModelHandle { +impl CollaborationHub for Model { fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap { self.read(cx).collaborators() } @@ -9164,7 +9157,7 @@ impl CollaborationHub for ModelHandle { fn inlay_hint_settings( location: Anchor, snapshot: &MultiBufferSnapshot, - cx: &mut ViewContext<'_, '_, Editor>, + cx: &mut ViewContext<'_, Editor>, ) -> InlayHintSettings { let file = snapshot.file_at(location); let language = snapshot.language_at(location); @@ -9267,7 +9260,7 @@ pub enum Event { text: Arc, }, ExcerptsAdded { - buffer: ModelHandle, + buffer: Model, predecessor: ExcerptId, excerpts: Vec<(ExcerptId, ExcerptRange)>, }, diff --git a/crates/editor2/src/editor_settings.rs b/crates/editor2/src/editor_settings.rs index 75f8b800f9..349d1c62fa 100644 --- a/crates/editor2/src/editor_settings.rs +++ b/crates/editor2/src/editor_settings.rs @@ -1,6 +1,6 @@ +use gpui::Settings; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Setting; #[derive(Deserialize)] pub struct EditorSettings { @@ -47,7 +47,7 @@ pub struct ScrollbarContent { pub selections: Option, } -impl Setting for EditorSettings { +impl Settings for EditorSettings { const KEY: Option<&'static str> = None; type FileContent = EditorSettingsContent; diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 7b1155890f..3583085fcc 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -1,60 +1,14 @@ use super::{ - display_map::{BlockContext, ToDisplayPoint}, - Anchor, DisplayPoint, Editor, EditorMode, EditorSnapshot, SelectPhase, SoftWrap, ToPoint, - MAX_LINE_LEN, + display_map::ToDisplayPoint, DisplayPoint, Editor, EditorSnapshot, ToPoint, MAX_LINE_LEN, }; use crate::{ - display_map::{BlockStyle, DisplaySnapshot, FoldStatus, HighlightedChunk, 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, - }, - 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, - }, + display_map::{BlockStyle, DisplaySnapshot}, mouse_context_menu, EditorSettings, EditorStyle, GutterHover, UnfoldAt, }; -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, -}; -use itertools::Itertools; -use json::json; -use language::{ - language_settings::ShowWhitespaceSetting, Bias, CursorShape, OffsetUtf16, Selection, -}; -use project::{ - project_settings::{GitGutterSetting, ProjectSettings}, - ProjectPath, -}; -use smallvec::SmallVec; -use std::{ - borrow::Cow, - cmp::{self, Ordering}, - fmt::Write, - iter, - ops::Range, - sync::Arc, -}; -use text::Point; -use theme::SelectionStyle; -use workspace::item::Item; +use gpui::{AnyElement, Bounds, Element, Hsla, Line, Pixels, Size, Style, TextRun, TextSystem}; +use language::{CursorShape, Selection}; +use std::{ops::Range, sync::Arc}; +use sum_tree::Bias; enum FoldMarkers {} @@ -129,1695 +83,1695 @@ impl EditorElement { } } - fn attach_mouse_handlers( - position_map: &Arc, - has_popovers: bool, - visible_bounds: RectF, - text_bounds: RectF, - gutter_bounds: RectF, - bounds: RectF, - cx: &mut ViewContext, - ) { - enum EditorElementMouseHandlers {} - let view_id = cx.view_id(); - cx.scene().push_mouse_region( - MouseRegion::new::(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; - } - - 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() - } - } - }), - ); - - enum GutterHandlers {} - let view_id = cx.view_id(); - let region_id = cx.view_id() + 1; - cx.scene().push_mouse_region( - MouseRegion::new::(view_id, region_id, gutter_bounds).on_hover( - |hover, editor: &mut Editor, cx| { - editor.gutter_hover( - &GutterHover { - hovered: hover.started, - }, - cx, - ); - }, - ), - ) - } - - fn mouse_down( - editor: &mut Editor, - MouseButtonEvent { - position, - modifiers: - Modifiers { - shift, - ctrl, - alt, - cmd, - .. - }, - mut click_count, - .. - }: MouseButtonEvent, - position_map: &PositionMap, - text_bounds: RectF, - gutter_bounds: RectF, - cx: &mut EventContext, - ) -> bool { - if gutter_bounds.contains_point(position) { - click_count = 3; // Simulate triple-click when clicking the gutter to select lines - } else if !text_bounds.contains_point(position) { - return false; - } - - let point_for_position = position_map.point_for_position(text_bounds, position); - let position = point_for_position.previous_valid; - if shift && alt { - editor.select( - SelectPhase::BeginColumnar { - position, - goal_column: point_for_position.exact_unclipped.column(), - }, - cx, - ); - } else if shift && !ctrl && !alt && !cmd { - editor.select( - SelectPhase::Extend { - position, - click_count, - }, - cx, - ); - } else { - editor.select( - SelectPhase::Begin { - position, - add: alt, - click_count, - }, - cx, - ); - } - - true - } - - fn mouse_right_down( - editor: &mut Editor, - position: Vector2F, - position_map: &PositionMap, - text_bounds: RectF, - cx: &mut EventContext, - ) -> bool { - if !text_bounds.contains_point(position) { - return false; - } - let point_for_position = position_map.point_for_position(text_bounds, position); - mouse_context_menu::deploy_context_menu( - editor, - position, - point_for_position.previous_valid, - cx, - ); - true - } - - fn mouse_up( - editor: &mut Editor, - position: Vector2F, - cmd: bool, - shift: bool, - alt: bool, - position_map: &PositionMap, - text_bounds: RectF, - cx: &mut EventContext, - ) -> bool { - 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 && cmd && text_bounds.contains_point(position) { - let point = position_map.point_for_position(text_bounds, position); - let could_be_inlay = point.as_valid().is_none(); - if shift || could_be_inlay { - go_to_fetched_type_definition(editor, point, alt, cx); - } else { - go_to_fetched_definition(editor, point, alt, cx); - } - - return true; - } - - end_selection - } - - fn mouse_dragged( - editor: &mut Editor, - MouseMovedEvent { - modifiers: Modifiers { cmd, shift, .. }, - position, - .. - }: MouseMovedEvent, - position_map: &PositionMap, - text_bounds: RectF, - cx: &mut EventContext, - ) -> 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, - )) - } - - let point_for_position = position_map.point_for_position(text_bounds, position); - - 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 - } - } - - fn mouse_moved( - editor: &mut Editor, - MouseMovedEvent { - modifiers: Modifiers { shift, cmd, .. }, - position, - .. - }: MouseMovedEvent, - position_map: &PositionMap, - text_bounds: RectF, - cx: &mut ViewContext, - ) -> 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 - if text_bounds.contains_point(position) { - let point_for_position = position_map.point_for_position(text_bounds, position); - match point_for_position.as_valid() { - Some(point) => { - update_go_to_definition_link( - editor, - Some(GoToDefinitionTrigger::Text(point)), - cmd, - shift, - cx, - ); - hover_at(editor, Some(point), cx); - } - None => { - update_inlay_link_and_hover_points( - &position_map.snapshot, - point_for_position, - editor, - cmd, - shift, - cx, - ); - } - } - } else { - update_go_to_definition_link(editor, None, cmd, shift, cx); - hover_at(editor, None, cx); - } - - true - } - - fn scroll( - editor: &mut Editor, - position: Vector2F, - mut delta: Vector2F, - precise: bool, - position_map: &PositionMap, - bounds: RectF, - cx: &mut ViewContext, - ) -> bool { - if !bounds.contains_point(position) { - return false; - } - - let line_height = position_map.line_height; - let max_glyph_width = position_map.em_width; - - 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 - }; - - 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); - editor.scroll(scroll_position, axis, cx); - - true - } - - fn paint_background( - &self, - gutter_bounds: RectF, - text_bounds: RectF, - layout: &LayoutState, - cx: &mut ViewContext, - ) { - let bounds = gutter_bounds.union_rect(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(), - }); - - 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 = vec2f( - bounds.origin_x(), - bounds.origin_y() + (layout.position_map.line_height * *start_row as f32) - - scroll_top, - ); - let size = vec2f( - bounds.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(), - }); - } - } - - if let Some(highlighted_rows) = &layout.highlighted_rows { - let origin = vec2f( - bounds.origin_x(), - bounds.origin_y() - + (layout.position_map.line_height * highlighted_rows.start as f32) - - scroll_top, - ); - let size = vec2f( - bounds.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 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 { - self.style.active_wrap_guide - } else { - self.style.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(), - }); - } - } - } - - fn paint_gutter( - &mut self, - bounds: RectF, - visible_bounds: RectF, - layout: &mut LayoutState, - editor: &mut Editor, - cx: &mut ViewContext, - ) { - 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!( - settings::get::(cx).git.git_gutter, - Some(GitGutterSetting::TrackedFiles) - ); - - if show_gutter { - Self::paint_diff_hunks(bounds, layout, cx); - } - - for (ix, line) in layout.line_number_layouts.iter().enumerate() { - if let Some(line) = line { - let line_origin = bounds.origin() - + vec2f( - bounds.width() - line.width() - layout.gutter_padding, - ix as f32 * line_height - (scroll_top % line_height), - ); - - line.paint(line_origin, visible_bounds, line_height, cx); - } - } - - for (ix, fold_indicator) in layout.fold_indicators.iter_mut().enumerate() { - if let Some(indicator) = fold_indicator.as_mut() { - let position = vec2f( - bounds.width() - layout.gutter_padding, - ix as f32 * line_height - (scroll_top % line_height), - ); - let centering_offset = vec2f( - (layout.gutter_padding + layout.gutter_margin - indicator.size().x()) / 2., - (line_height - indicator.size().y()) / 2., - ); - - let indicator_origin = bounds.origin() + position + centering_offset; - - indicator.paint(indicator_origin, visible_bounds, editor, cx); - } - } - - if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() { - let mut x = 0.; - let mut y = *row as f32 * line_height - scroll_top; - x += ((layout.gutter_padding + layout.gutter_margin) - indicator.size().x()) / 2.; - y += (line_height - indicator.size().y()) / 2.; - indicator.paint(bounds.origin() + vec2f(x, y), visible_bounds, editor, cx); - } - } - - fn paint_diff_hunks(bounds: RectF, layout: &mut LayoutState, cx: &mut ViewContext) { - let diff_style = &theme::current(cx).editor.diff.clone(); - 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 = diff_style.removed_width_em * line_height; - let highlight_origin = bounds.origin() + vec2f(-width, start_y); - let highlight_size = vec2f(width * 2., end_y - start_y); - let highlight_bounds = RectF::new(highlight_origin, highlight_size); - - cx.scene().push_quad(Quad { - bounds: highlight_bounds, - background: Some(diff_style.modified), - border: Border::new(0., Color::transparent_black()).into(), - corner_radii: (1. * line_height).into(), - }); - - continue; - } - - DisplayDiffHunk::Unfolded { - display_row_range, - status, - } => (display_row_range, status), - }; - - let color = match status { - DiffHunkStatus::Added => diff_style.inserted, - DiffHunkStatus::Modified => diff_style.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 = diff_style.removed_width_em * line_height; - let highlight_origin = bounds.origin() + vec2f(-width, start_y); - let highlight_size = vec2f(width * 2., end_y - start_y); - let highlight_bounds = RectF::new(highlight_origin, highlight_size); - - cx.scene().push_quad(Quad { - bounds: highlight_bounds, - background: Some(diff_style.deleted), - border: Border::new(0., Color::transparent_black()).into(), - corner_radii: (1. * line_height).into(), - }); - - 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 = diff_style.width_em * line_height; - let highlight_origin = bounds.origin() + vec2f(-width, start_y); - let highlight_size = vec2f(width * 2., end_y - start_y); - let highlight_bounds = RectF::new(highlight_origin, highlight_size); - - cx.scene().push_quad(Quad { - bounds: highlight_bounds, - background: Some(color), - border: Border::new(0., Color::transparent_black()).into(), - corner_radii: (diff_style.corner_radius * line_height).into(), - }); - } - } - - fn paint_text( - &mut self, - bounds: RectF, - visible_bounds: RectF, - layout: &mut LayoutState, - editor: &mut Editor, - cx: &mut ViewContext, - ) { - let style = &self.style; - let scroll_position = layout.position_map.snapshot.scroll_position(); - let start_row = layout.visible_display_row_range.start; - let scroll_top = scroll_position.y() * layout.position_map.line_height; - let max_glyph_width = layout.position_map.em_width; - let scroll_left = scroll_position.x() * max_glyph_width; - let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.); - let line_end_overshoot = 0.15 * layout.position_map.line_height; - let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces; - - cx.scene().push_layer(Some(bounds)); - - cx.scene().push_cursor_region(CursorRegion { - bounds, - style: if !editor.link_go_to_definition_state.definitions.is_empty() { - CursorStyle::PointingHand - } else { - CursorStyle::IBeam - }, - }); - - let fold_corner_radius = - self.style.folds.ellipses.corner_radius_factor * layout.position_map.line_height; - for (id, range, color) in layout.fold_ranges.iter() { - self.paint_highlighted_range( - range.clone(), - *color, - fold_corner_radius, - fold_corner_radius * 2., - layout, - content_origin, - scroll_top, - scroll_left, - bounds, - cx, - ); - - for bound in range_to_bounds( - &range, - content_origin, - scroll_left, - scroll_top, - &layout.visible_display_row_range, - line_end_overshoot, - &layout.position_map, - ) { - cx.scene().push_cursor_region(CursorRegion { - bounds: bound, - style: CursorStyle::PointingHand, - }); - - let display_row = range.start.row(); - - let buffer_row = DisplayPoint::new(display_row, 0) - .to_point(&layout.position_map.snapshot.display_snapshot) - .row; - - let view_id = cx.view_id(); - cx.scene().push_mouse_region( - MouseRegion::new::(view_id, *id as usize, bound) - .on_click(MouseButton::Left, move |_, editor: &mut Editor, cx| { - editor.unfold_at(&UnfoldAt { buffer_row }, cx) - }) - .with_notify_on_hover(true) - .with_notify_on_click(true), - ) - } - } - - for (range, color) in &layout.highlighted_ranges { - self.paint_highlighted_range( - range.clone(), - *color, - 0., - line_end_overshoot, - layout, - content_origin, - scroll_top, - scroll_left, - 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; 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, - scroll_top, - scroll_left, - bounds, - cx, - ); - - if selection.is_local && !selection.range.is_empty() { - invisible_display_ranges.push(selection.range.clone()); - } - if !selection.is_local || editor.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 == 0.0 { - 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, _)| { - let font_id = - cursor_row_layout.font_for_index(cursor_column)?; - let text = character.to_string(); - - Some(cx.text_layout_cache().layout_str( - &text, - cursor_row_layout.font_size(), - &[( - text.chars().count(), - RunStyle { - font_id, - color: style.background, - underline: Default::default(), - }, - )], - )) - }) - } else { - None - }; - - let x = cursor_character_x - scroll_left; - let y = cursor_position.row() as f32 * layout.position_map.line_height - - scroll_top; - if selection.is_newest { - editor.pixel_position_of_newest_cursor = Some(vec2f( - bounds.origin_x() + x + block_width / 2., - bounds.origin_y() + y + layout.position_map.line_height / 2., - )); - } - cursors.push(Cursor { - color: selection_style.cursor, - block_width, - origin: vec2f(x, y), - line_height: layout.position_map.line_height, - shape: selection.cursor_shape, - block_text, - }); - } - } - } - } - - if let Some(visible_text_bounds) = bounds.intersection(visible_bounds) { - 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, - scroll_top, - content_origin, - scroll_left, - visible_text_bounds, - whitespace_setting, - &invisible_display_ranges, - visible_bounds, - cx, - ) - } - } - - cx.scene().push_layer(Some(bounds)); - for cursor in cursors { - cursor.paint(content_origin, cx); - } - cx.scene().pop_layer(); - - if let Some((position, context_menu)) = layout.context_menu.as_mut() { - cx.scene().push_stacking_context(None, None); - 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) - scroll_left; - let y = (position.row() + 1) as f32 * layout.position_map.line_height - scroll_top; - let mut list_origin = content_origin + vec2f(x, y); - let list_width = context_menu.size().x(); - let list_height = context_menu.size().y(); - - // 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.window_size().x() { - list_origin.set_x((cx.window_size().x() - list_width).max(0.)); - } - - if list_origin.y() + list_height > bounds.max_y() { - list_origin.set_y(list_origin.y() - layout.position_map.line_height - list_height); - } - - context_menu.paint( - list_origin, - RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor - editor, - cx, - ); - - cx.scene().pop_stacking_context(); - } - - if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() { - cx.scene().push_stacking_context(None, None); - - // 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].size(); - let height_to_reserve = first_size.y() - + 1.5 * MIN_POPOVER_LINE_HEIGHT as f32 * layout.position_map.line_height; - - // Compute Hovered Point - let x = hovered_row_layout.x_for_index(position.column() as usize) - scroll_left; - let y = position.row() as f32 * layout.position_map.line_height - scroll_top; - let hovered_point = content_origin + vec2f(x, y); - - if hovered_point.y() - height_to_reserve > 0.0 { - // There is enough space above. Render popovers above the hovered point - let mut current_y = hovered_point.y(); - for hover_popover in hover_popovers { - let size = hover_popover.size(); - let mut popover_origin = vec2f(hovered_point.x(), current_y - size.y()); - - let x_out_of_bounds = bounds.max_x() - (popover_origin.x() + size.x()); - if x_out_of_bounds < 0.0 { - popover_origin.set_x(popover_origin.x() + x_out_of_bounds); - } - - hover_popover.paint( - popover_origin, - RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor - editor, - 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 hover_popover in hover_popovers { - let size = hover_popover.size(); - let mut popover_origin = vec2f(hovered_point.x(), current_y); - - let x_out_of_bounds = bounds.max_x() - (popover_origin.x() + size.x()); - if x_out_of_bounds < 0.0 { - popover_origin.set_x(popover_origin.x() + x_out_of_bounds); - } - - hover_popover.paint( - popover_origin, - RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor - editor, - cx, - ); - - current_y = popover_origin.y() + size.y() + HOVER_POPOVER_GAP; - } - } - - cx.scene().pop_stacking_context(); - } - - cx.scene().pop_layer(); - } - - fn scrollbar_left(&self, bounds: &RectF) -> f32 { - bounds.max_x() - self.style.theme.scrollbar.width - } - - fn paint_scrollbar( - &mut self, - bounds: RectF, - layout: &mut LayoutState, - editor: &Editor, - cx: &mut ViewContext, - ) { - enum ScrollbarMouseHandlers {} - if layout.mode != EditorMode::Full { - return; - } - - let style = &self.style.theme.scrollbar; - - let top = bounds.min_y(); - let bottom = bounds.max_y(); - let right = bounds.max_x(); - let left = self.scrollbar_left(&bounds); - let row_range = &layout.scrollbar_row_range; - let max_row = layout.max_row as f32 + (row_range.end - row_range.start); - - let mut height = bounds.height(); - let mut first_row_y_offset = 0.0; - - // Impose a minimum height on the scrollbar thumb - let row_height = height / max_row; - let min_thumb_height = - style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size); - 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| -> f32 { 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 = RectF::from_points(vec2f(left, top), vec2f(right, bottom)); - let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom)); - - if layout.show_scrollbars { - cx.scene().push_quad(Quad { - bounds: track_bounds, - border: style.track.border.into(), - background: style.track.background_color, - ..Default::default() - }); - let scrollbar_settings = settings::get::(cx).scrollbar; - let theme = theme::current(cx); - let scrollbar_theme = &theme.editor.scrollbar; - if layout.is_singleton && scrollbar_settings.selections { - let start_anchor = Anchor::min(); - let end_anchor = Anchor::max(); - let color = scrollbar_theme.selections; - let border = Border { - width: 1., - color: style.thumb.border.color, - overlay: false, - top: false, - right: true, - bottom: false, - left: true, - }; - let mut push_region = |start: DisplayPoint, end: DisplayPoint| { - let start_y = y_for_row(start.row() as f32); - let mut end_y = y_for_row(end.row() as f32); - if end_y - start_y < 1. { - end_y = start_y + 1.; - } - let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y)); - - cx.scene().push_quad(Quad { - bounds, - background: Some(color), - border: border.into(), - corner_radii: style.thumb.corner_radii.into(), - }) - }; - let background_ranges = editor - .background_highlight_row_ranges::( - start_anchor..end_anchor, - &layout.position_map.snapshot, - 50000, - ); - for row in background_ranges { - let start = row.start(); - let end = row.end(); - push_region(*start, *end); - } - } - - if layout.is_singleton && scrollbar_settings.git_diff { - let diff_style = scrollbar_theme.git.clone(); - 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 < 1. { - end_y = start_y + 1.; - } - let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y)); - - let color = match hunk.status() { - DiffHunkStatus::Added => diff_style.inserted, - DiffHunkStatus::Modified => diff_style.modified, - DiffHunkStatus::Removed => diff_style.deleted, - }; - - let border = Border { - width: 1., - color: style.thumb.border.color, - overlay: false, - top: false, - right: true, - bottom: false, - left: true, - }; - - cx.scene().push_quad(Quad { - bounds, - background: Some(color), - border: border.into(), - corner_radii: style.thumb.corner_radii.into(), - }) - } - } - - cx.scene().push_quad(Quad { - bounds: thumb_bounds, - border: style.thumb.border.into(), - background: style.thumb.background_color, - corner_radii: style.thumb.corner_radii.into(), - }); - } - - cx.scene().push_cursor_region(CursorRegion { - bounds: track_bounds, - style: CursorStyle::Arrow, - }); - let region_id = cx.view_id(); - cx.scene().push_mouse_region( - MouseRegion::new::(region_id, region_id, track_bounds) - .on_move(move |event, editor: &mut Editor, cx| { - if event.pressed_button.is_none() { - editor.scroll_manager.show_scrollbar(cx); - } - }) - .on_down(MouseButton::Left, { - let row_range = row_range.clone(); - move |event, editor: &mut Editor, 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.set_y(top_row as f32); - editor.set_scroll_position(position, cx); - } else { - editor.scroll_manager.show_scrollbar(cx); - } - } - }) - .on_drag(MouseButton::Left, { - move |event, editor: &mut Editor, cx| { - if event.end { - return; - } - - let y = event.prev_mouse_position.y(); - let new_y = event.position.y(); - if thumb_top < y && y < thumb_bottom { - let mut position = editor.scroll_position(cx); - position.set_y(position.y() + (new_y - y) * (max_row as f32) / height); - if position.y() < 0.0 { - position.set_y(0.); - } - editor.set_scroll_position(position, cx); - } - } - }), - ); - } - - #[allow(clippy::too_many_arguments)] - fn paint_highlighted_range( - &self, - range: Range, - color: Color, - corner_radius: f32, - line_end_overshoot: f32, - layout: &LayoutState, - content_origin: Vector2F, - scroll_top: f32, - scroll_left: f32, - bounds: RectF, - cx: &mut ViewContext, - ) { - 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 - - scroll_top, - 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) - - scroll_left - } else { - content_origin.x() - scroll_left - }, - end_x: if row == range.end.row() { - content_origin.x() - + line_layout.x_for_index(range.end.column() as usize) - - scroll_left - } else { - content_origin.x() + line_layout.width() + line_end_overshoot - - scroll_left - }, - } - }) - .collect(), - }; - - highlighted_range.paint(bounds, cx); - } - } - - fn paint_blocks( - &mut self, - bounds: RectF, - visible_bounds: RectF, - layout: &mut LayoutState, - editor: &mut Editor, - cx: &mut ViewContext, - ) { - 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 block in &mut layout.blocks { - let mut origin = bounds.origin() - + vec2f( - 0., - block.row as f32 * layout.position_map.line_height - scroll_top, - ); - if !matches!(block.style, BlockStyle::Sticky) { - origin += vec2f(-scroll_left, 0.); - } - block.element.paint(origin, visible_bounds, editor, cx); - } - } - - fn column_pixels(&self, column: usize, cx: &ViewContext) -> f32 { - let style = &self.style; - - cx.text_layout_cache() - .layout_str( - " ".repeat(column).as_str(), - style.text.font_size, - &[( - column, - RunStyle { - font_id: style.text.font_id, - color: Color::black(), - underline: Default::default(), - }, - )], - ) - .width() - } - - fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext) -> f32 { - let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1; - self.column_pixels(digit_count, cx) - } + // fn attach_mouse_handlers( + // position_map: &Arc, + // has_popovers: bool, + // visible_bounds: Bounds, + // text_bounds: Bounds, + // gutter_bounds: Bounds, + // bounds: Bounds, + // cx: &mut ViewContext, + // ) { + // enum EditorElementMouseHandlers {} + // let view_id = cx.view_id(); + // cx.scene().push_mouse_region( + // MouseRegion::new::(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; + // } + + // 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() + // } + // } + // }), + // ); + + // enum GutterHandlers {} + // let view_id = cx.view_id(); + // let region_id = cx.view_id() + 1; + // cx.scene().push_mouse_region( + // MouseRegion::new::(view_id, region_id, gutter_bounds).on_hover( + // |hover, editor: &mut Editor, cx| { + // editor.gutter_hover( + // &GutterHover { + // hovered: hover.started, + // }, + // cx, + // ); + // }, + // ), + // ) + // } + + // fn mouse_down( + // editor: &mut Editor, + // MouseButtonEvent { + // position, + // modifiers: + // Modifiers { + // shift, + // ctrl, + // alt, + // cmd, + // .. + // }, + // mut click_count, + // .. + // }: MouseButtonEvent, + // position_map: &PositionMap, + // text_bounds: Bounds, + // gutter_bounds: Bounds, + // cx: &mut EventContext, + // ) -> bool { + // if gutter_bounds.contains_point(position) { + // click_count = 3; // Simulate triple-click when clicking the gutter to select lines + // } else if !text_bounds.contains_point(position) { + // return false; + // } + + // let point_for_position = position_map.point_for_position(text_bounds, position); + // let position = point_for_position.previous_valid; + // if shift && alt { + // editor.select( + // SelectPhase::BeginColumnar { + // position, + // goal_column: point_for_position.exact_unclipped.column(), + // }, + // cx, + // ); + // } else if shift && !ctrl && !alt && !cmd { + // editor.select( + // SelectPhase::Extend { + // position, + // click_count, + // }, + // cx, + // ); + // } else { + // editor.select( + // SelectPhase::Begin { + // position, + // add: alt, + // click_count, + // }, + // cx, + // ); + // } + + // true + // } + + // fn mouse_right_down( + // editor: &mut Editor, + // position: gpui::Point, + // position_map: &PositionMap, + // text_bounds: Bounds, + // cx: &mut EventContext, + // ) -> bool { + // if !text_bounds.contains_point(position) { + // return false; + // } + // let point_for_position = position_map.point_for_position(text_bounds, position); + // mouse_context_menu::deploy_context_menu( + // editor, + // position, + // point_for_position.previous_valid, + // cx, + // ); + // true + // } + + // fn mouse_up( + // editor: &mut Editor, + // position: gpui::Point, + // cmd: bool, + // shift: bool, + // alt: bool, + // position_map: &PositionMap, + // text_bounds: Bounds, + // cx: &mut EventContext, + // ) -> bool { + // 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 && cmd && text_bounds.contains_point(position) { + // let point = position_map.point_for_position(text_bounds, position); + // let could_be_inlay = point.as_valid().is_none(); + // if shift || could_be_inlay { + // go_to_fetched_type_definition(editor, point, alt, cx); + // } else { + // go_to_fetched_definition(editor, point, alt, cx); + // } + + // return true; + // } + + // end_selection + // } + + // fn mouse_dragged( + // editor: &mut Editor, + // MouseMovedEvent { + // modifiers: Modifiers { cmd, shift, .. }, + // position, + // .. + // }: MouseMovedEvent, + // position_map: &PositionMap, + // text_bounds: Bounds, + // cx: &mut EventContext, + // ) -> 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 = gpui::Point::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, + // )) + // } + + // let point_for_position = position_map.point_for_position(text_bounds, position); + + // 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::zero(), position_map.scroll_max), + // }, + // cx, + // ); + // hover_at(editor, point, cx); + // true + // } else { + // hover_at(editor, point, cx); + // false + // } + // } + + // fn mouse_moved( + // editor: &mut Editor, + // MouseMovedEvent { + // modifiers: Modifiers { shift, cmd, .. }, + // position, + // .. + // }: MouseMovedEvent, + // position_map: &PositionMap, + // text_bounds: Bounds, + // cx: &mut ViewContext, + // ) -> 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 + // if text_bounds.contains_point(position) { + // let point_for_position = position_map.point_for_position(text_bounds, position); + // match point_for_position.as_valid() { + // Some(point) => { + // update_go_to_definition_link( + // editor, + // Some(GoToDefinitionTrigger::Text(point)), + // cmd, + // shift, + // cx, + // ); + // hover_at(editor, Some(point), cx); + // } + // None => { + // update_inlay_link_and_hover_points( + // &position_map.snapshot, + // point_for_position, + // editor, + // cmd, + // shift, + // cx, + // ); + // } + // } + // } else { + // update_go_to_definition_link(editor, None, cmd, shift, cx); + // hover_at(editor, None, cx); + // } + + // true + // } + + // fn scroll( + // editor: &mut Editor, + // position: gpui::Point, + // mut delta: gpui::Point, + // precise: bool, + // position_map: &PositionMap, + // bounds: Bounds, + // cx: &mut ViewContext, + // ) -> bool { + // if !bounds.contains_point(position) { + // return false; + // } + + // let line_height = position_map.line_height; + // let max_glyph_width = position_map.em_width; + + // 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 + // }; + + // 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(gpui::Point::zero(), position_map.scroll_max); + // editor.scroll(scroll_position, axis, cx); + + // true + // } + + // fn paint_background( + // &self, + // gutter_bounds: Bounds, + // text_bounds: Bounds, + // layout: &LayoutState, + // cx: &mut ViewContext, + // ) { + // let bounds = gutter_bounds.union_rect(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(), + // }); + + // 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 = vec2f( + // bounds.origin_x(), + // bounds.origin_y() + (layout.position_map.line_height * *start_row as f32) + // - scroll_top, + // ); + // let size = vec2f( + // bounds.width(), + // layout.position_map.line_height * (end_row - start_row + 1) as f32, + // ); + // cx.scene().push_quad(Quad { + // bounds: Bounds::new(origin, size), + // background: Some(self.style.active_line_background), + // border: Border::default().into(), + // corner_radii: Default::default(), + // }); + // } + // } + + // if let Some(highlighted_rows) = &layout.highlighted_rows { + // let origin = vec2f( + // bounds.origin_x(), + // bounds.origin_y() + // + (layout.position_map.line_height * highlighted_rows.start as f32) + // - scroll_top, + // ); + // let size = vec2f( + // bounds.width(), + // layout.position_map.line_height * highlighted_rows.len() as f32, + // ); + // cx.scene().push_quad(Quad { + // bounds: Bounds::new(origin, size), + // background: Some(self.style.highlighted_line_background), + // border: Border::default().into(), + // corner_radii: Default::default(), + // }); + // } + + // 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 { + // self.style.active_wrap_guide + // } else { + // self.style.wrap_guide + // }; + // cx.scene().push_quad(Quad { + // bounds: Bounds::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(), + // }); + // } + // } + // } + + // fn paint_gutter( + // &mut self, + // bounds: Bounds, + // visible_bounds: Bounds, + // layout: &mut LayoutState, + // editor: &mut Editor, + // cx: &mut ViewContext, + // ) { + // 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!( + // settings::get::(cx).git.git_gutter, + // Some(GitGutterSetting::TrackedFiles) + // ); + + // if show_gutter { + // Self::paint_diff_hunks(bounds, layout, cx); + // } + + // for (ix, line) in layout.line_number_layouts.iter().enumerate() { + // if let Some(line) = line { + // let line_origin = bounds.origin() + // + vec2f( + // bounds.width() - line.width() - layout.gutter_padding, + // ix as f32 * line_height - (scroll_top % line_height), + // ); + + // line.paint(line_origin, visible_bounds, line_height, cx); + // } + // } + + // for (ix, fold_indicator) in layout.fold_indicators.iter_mut().enumerate() { + // if let Some(indicator) = fold_indicator.as_mut() { + // let position = vec2f( + // bounds.width() - layout.gutter_padding, + // ix as f32 * line_height - (scroll_top % line_height), + // ); + // let centering_offset = vec2f( + // (layout.gutter_padding + layout.gutter_margin - indicator.size().x()) / 2., + // (line_height - indicator.size().y()) / 2., + // ); + + // let indicator_origin = bounds.origin() + position + centering_offset; + + // indicator.paint(indicator_origin, visible_bounds, editor, cx); + // } + // } + + // if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() { + // let mut x = 0.; + // let mut y = *row as f32 * line_height - scroll_top; + // x += ((layout.gutter_padding + layout.gutter_margin) - indicator.size().x()) / 2.; + // y += (line_height - indicator.size().y()) / 2.; + // indicator.paint(bounds.origin() + vec2f(x, y), visible_bounds, editor, cx); + // } + // } + + // fn paint_diff_hunks(bounds: Bounds, layout: &mut LayoutState, cx: &mut ViewContext) { + // let diff_style = &theme::current(cx).editor.diff.clone(); + // 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 = diff_style.removed_width_em * line_height; + // let highlight_origin = bounds.origin() + vec2f(-width, start_y); + // let highlight_size = vec2f(width * 2., end_y - start_y); + // let highlight_bounds = Bounds::new(highlight_origin, highlight_size); + + // cx.scene().push_quad(Quad { + // bounds: highlight_bounds, + // background: Some(diff_style.modified), + // border: Border::new(0., Color::transparent_black()).into(), + // corner_radii: (1. * line_height).into(), + // }); + + // continue; + // } + + // DisplayDiffHunk::Unfolded { + // display_row_range, + // status, + // } => (display_row_range, status), + // }; + + // let color = match status { + // DiffHunkStatus::Added => diff_style.inserted, + // DiffHunkStatus::Modified => diff_style.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 = diff_style.removed_width_em * line_height; + // let highlight_origin = bounds.origin() + vec2f(-width, start_y); + // let highlight_size = vec2f(width * 2., end_y - start_y); + // let highlight_bounds = Bounds::new(highlight_origin, highlight_size); + + // cx.scene().push_quad(Quad { + // bounds: highlight_bounds, + // background: Some(diff_style.deleted), + // border: Border::new(0., Color::transparent_black()).into(), + // corner_radii: (1. * line_height).into(), + // }); + + // 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 = diff_style.width_em * line_height; + // let highlight_origin = bounds.origin() + vec2f(-width, start_y); + // let highlight_size = vec2f(width * 2., end_y - start_y); + // let highlight_bounds = Bounds::new(highlight_origin, highlight_size); + + // cx.scene().push_quad(Quad { + // bounds: highlight_bounds, + // background: Some(color), + // border: Border::new(0., Color::transparent_black()).into(), + // corner_radii: (diff_style.corner_radius * line_height).into(), + // }); + // } + // } + + // fn paint_text( + // &mut self, + // bounds: Bounds, + // visible_bounds: Bounds, + // layout: &mut LayoutState, + // editor: &mut Editor, + // cx: &mut ViewContext, + // ) { + // let style = &self.style; + // let scroll_position = layout.position_map.snapshot.scroll_position(); + // let start_row = layout.visible_display_row_range.start; + // let scroll_top = scroll_position.y() * layout.position_map.line_height; + // let max_glyph_width = layout.position_map.em_width; + // let scroll_left = scroll_position.x() * max_glyph_width; + // let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.); + // let line_end_overshoot = 0.15 * layout.position_map.line_height; + // let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces; + + // cx.scene().push_layer(Some(bounds)); + + // cx.scene().push_cursor_region(CursorRegion { + // bounds, + // style: if !editor.link_go_to_definition_state.definitions.is_empty() { + // CursorStyle::PointingHand + // } else { + // CursorStyle::IBeam + // }, + // }); + + // let fold_corner_radius = + // self.style.folds.ellipses.corner_radius_factor * layout.position_map.line_height; + // for (id, range, color) in layout.fold_ranges.iter() { + // self.paint_highlighted_range( + // range.clone(), + // *color, + // fold_corner_radius, + // fold_corner_radius * 2., + // layout, + // content_origin, + // scroll_top, + // scroll_left, + // bounds, + // cx, + // ); + + // for bound in range_to_bounds( + // &range, + // content_origin, + // scroll_left, + // scroll_top, + // &layout.visible_display_row_range, + // line_end_overshoot, + // &layout.position_map, + // ) { + // cx.scene().push_cursor_region(CursorRegion { + // bounds: bound, + // style: CursorStyle::PointingHand, + // }); + + // let display_row = range.start.row(); + + // let buffer_row = DisplayPoint::new(display_row, 0) + // .to_point(&layout.position_map.snapshot.display_snapshot) + // .row; + + // let view_id = cx.view_id(); + // cx.scene().push_mouse_region( + // MouseRegion::new::(view_id, *id as usize, bound) + // .on_click(MouseButton::Left, move |_, editor: &mut Editor, cx| { + // editor.unfold_at(&UnfoldAt { buffer_row }, cx) + // }) + // .with_notify_on_hover(true) + // .with_notify_on_click(true), + // ) + // } + // } + + // for (range, color) in &layout.highlighted_ranges { + // self.paint_highlighted_range( + // range.clone(), + // *color, + // 0., + // line_end_overshoot, + // layout, + // content_origin, + // scroll_top, + // scroll_left, + // 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; 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, + // scroll_top, + // scroll_left, + // bounds, + // cx, + // ); + + // if selection.is_local && !selection.range.is_empty() { + // invisible_display_ranges.push(selection.range.clone()); + // } + // if !selection.is_local || editor.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 == 0.0 { + // 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, _)| { + // let font_id = + // cursor_row_layout.font_for_index(cursor_column)?; + // let text = character.to_string(); + + // Some(cx.text_layout_cache().layout_str( + // &text, + // cursor_row_layout.font_size(), + // &[( + // text.chars().count(), + // RunStyle { + // font_id, + // color: style.background, + // underline: Default::default(), + // }, + // )], + // )) + // }) + // } else { + // None + // }; + + // let x = cursor_character_x - scroll_left; + // let y = cursor_position.row() as f32 * layout.position_map.line_height + // - scroll_top; + // if selection.is_newest { + // editor.pixel_position_of_newest_cursor = Some(vec2f( + // bounds.origin_x() + x + block_width / 2., + // bounds.origin_y() + y + layout.position_map.line_height / 2., + // )); + // } + // cursors.push(Cursor { + // color: selection_style.cursor, + // block_width, + // origin: vec2f(x, y), + // line_height: layout.position_map.line_height, + // shape: selection.cursor_shape, + // block_text, + // }); + // } + // } + // } + // } + + // if let Some(visible_text_bounds) = bounds.intersection(visible_bounds) { + // 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, + // scroll_top, + // content_origin, + // scroll_left, + // visible_text_bounds, + // whitespace_setting, + // &invisible_display_ranges, + // visible_bounds, + // cx, + // ) + // } + // } + + // cx.scene().push_layer(Some(bounds)); + // for cursor in cursors { + // cursor.paint(content_origin, cx); + // } + // cx.scene().pop_layer(); + + // if let Some((position, context_menu)) = layout.context_menu.as_mut() { + // cx.scene().push_stacking_context(None, None); + // 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) - scroll_left; + // let y = (position.row() + 1) as f32 * layout.position_map.line_height - scroll_top; + // let mut list_origin = content_origin + vec2f(x, y); + // let list_width = context_menu.size().x(); + // let list_height = context_menu.size().y(); + + // // 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.window_size().x() { + // list_origin.set_x((cx.window_size().x() - list_width).max(0.)); + // } + + // if list_origin.y() + list_height > bounds.max_y() { + // list_origin.set_y(list_origin.y() - layout.position_map.line_height - list_height); + // } + + // context_menu.paint( + // list_origin, + // Bounds::from_points(gpui::Point::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor + // editor, + // cx, + // ); + + // cx.scene().pop_stacking_context(); + // } + + // if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() { + // cx.scene().push_stacking_context(None, None); + + // // 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].size(); + // let height_to_reserve = first_size.y() + // + 1.5 * MIN_POPOVER_LINE_HEIGHT as f32 * layout.position_map.line_height; + + // // Compute Hovered Point + // let x = hovered_row_layout.x_for_index(position.column() as usize) - scroll_left; + // let y = position.row() as f32 * layout.position_map.line_height - scroll_top; + // let hovered_point = content_origin + vec2f(x, y); + + // if hovered_point.y() - height_to_reserve > 0.0 { + // // There is enough space above. Render popovers above the hovered point + // let mut current_y = hovered_point.y(); + // for hover_popover in hover_popovers { + // let size = hover_popover.size(); + // let mut popover_origin = vec2f(hovered_point.x(), current_y - size.y()); + + // let x_out_of_bounds = bounds.max_x() - (popover_origin.x() + size.x()); + // if x_out_of_bounds < 0.0 { + // popover_origin.set_x(popover_origin.x() + x_out_of_bounds); + // } + + // hover_popover.paint( + // popover_origin, + // Bounds::from_points(gpui::Point::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor + // editor, + // 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 hover_popover in hover_popovers { + // let size = hover_popover.size(); + // let mut popover_origin = vec2f(hovered_point.x(), current_y); + + // let x_out_of_bounds = bounds.max_x() - (popover_origin.x() + size.x()); + // if x_out_of_bounds < 0.0 { + // popover_origin.set_x(popover_origin.x() + x_out_of_bounds); + // } + + // hover_popover.paint( + // popover_origin, + // Bounds::from_points(gpui::Point::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor + // editor, + // cx, + // ); + + // current_y = popover_origin.y() + size.y() + HOVER_POPOVER_GAP; + // } + // } + + // cx.scene().pop_stacking_context(); + // } + + // cx.scene().pop_layer(); + // } + + // fn scrollbar_left(&self, bounds: &Bounds) -> f32 { + // bounds.max_x() - self.style.theme.scrollbar.width + // } + + // fn paint_scrollbar( + // &mut self, + // bounds: Bounds, + // layout: &mut LayoutState, + // editor: &Editor, + // cx: &mut ViewContext, + // ) { + // enum ScrollbarMouseHandlers {} + // if layout.mode != EditorMode::Full { + // return; + // } + + // let style = &self.style.theme.scrollbar; + + // let top = bounds.min_y(); + // let bottom = bounds.max_y(); + // let right = bounds.max_x(); + // let left = self.scrollbar_left(&bounds); + // let row_range = &layout.scrollbar_row_range; + // let max_row = layout.max_row as f32 + (row_range.end - row_range.start); + + // let mut height = bounds.height(); + // let mut first_row_y_offset = 0.0; + + // // Impose a minimum height on the scrollbar thumb + // let row_height = height / max_row; + // let min_thumb_height = + // style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size); + // 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| -> f32 { 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_points(vec2f(left, top), vec2f(right, bottom)); + // let thumb_bounds = Bounds::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom)); + + // if layout.show_scrollbars { + // cx.scene().push_quad(Quad { + // bounds: track_bounds, + // border: style.track.border.into(), + // background: style.track.background_color, + // ..Default::default() + // }); + // let scrollbar_settings = settings::get::(cx).scrollbar; + // let theme = theme::current(cx); + // let scrollbar_theme = &theme.editor.scrollbar; + // if layout.is_singleton && scrollbar_settings.selections { + // let start_anchor = Anchor::min(); + // let end_anchor = Anchor::max(); + // let color = scrollbar_theme.selections; + // let border = Border { + // width: 1., + // color: style.thumb.border.color, + // overlay: false, + // top: false, + // right: true, + // bottom: false, + // left: true, + // }; + // let mut push_region = |start: DisplayPoint, end: DisplayPoint| { + // let start_y = y_for_row(start.row() as f32); + // let mut end_y = y_for_row(end.row() as f32); + // if end_y - start_y < 1. { + // end_y = start_y + 1.; + // } + // let bounds = Bounds::from_points(vec2f(left, start_y), vec2f(right, end_y)); + + // cx.scene().push_quad(Quad { + // bounds, + // background: Some(color), + // border: border.into(), + // corner_radii: style.thumb.corner_radii.into(), + // }) + // }; + // let background_ranges = editor + // .background_highlight_row_ranges::( + // start_anchor..end_anchor, + // &layout.position_map.snapshot, + // 50000, + // ); + // for row in background_ranges { + // let start = row.start(); + // let end = row.end(); + // push_region(*start, *end); + // } + // } + + // if layout.is_singleton && scrollbar_settings.git_diff { + // let diff_style = scrollbar_theme.git.clone(); + // 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 < 1. { + // end_y = start_y + 1.; + // } + // let bounds = Bounds::from_points(vec2f(left, start_y), vec2f(right, end_y)); + + // let color = match hunk.status() { + // DiffHunkStatus::Added => diff_style.inserted, + // DiffHunkStatus::Modified => diff_style.modified, + // DiffHunkStatus::Removed => diff_style.deleted, + // }; + + // let border = Border { + // width: 1., + // color: style.thumb.border.color, + // overlay: false, + // top: false, + // right: true, + // bottom: false, + // left: true, + // }; + + // cx.scene().push_quad(Quad { + // bounds, + // background: Some(color), + // border: border.into(), + // corner_radii: style.thumb.corner_radii.into(), + // }) + // } + // } + + // cx.scene().push_quad(Quad { + // bounds: thumb_bounds, + // border: style.thumb.border.into(), + // background: style.thumb.background_color, + // corner_radii: style.thumb.corner_radii.into(), + // }); + // } + + // cx.scene().push_cursor_region(CursorRegion { + // bounds: track_bounds, + // style: CursorStyle::Arrow, + // }); + // let region_id = cx.view_id(); + // cx.scene().push_mouse_region( + // MouseRegion::new::(region_id, region_id, track_bounds) + // .on_move(move |event, editor: &mut Editor, cx| { + // if event.pressed_button.is_none() { + // editor.scroll_manager.show_scrollbar(cx); + // } + // }) + // .on_down(MouseButton::Left, { + // let row_range = row_range.clone(); + // move |event, editor: &mut Editor, 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.set_y(top_row as f32); + // editor.set_scroll_position(position, cx); + // } else { + // editor.scroll_manager.show_scrollbar(cx); + // } + // } + // }) + // .on_drag(MouseButton::Left, { + // move |event, editor: &mut Editor, cx| { + // if event.end { + // return; + // } + + // let y = event.prev_mouse_position.y(); + // let new_y = event.position.y(); + // if thumb_top < y && y < thumb_bottom { + // let mut position = editor.scroll_position(cx); + // position.set_y(position.y() + (new_y - y) * (max_row as f32) / height); + // if position.y() < 0.0 { + // position.set_y(0.); + // } + // editor.set_scroll_position(position, cx); + // } + // } + // }), + // ); + // } + + // #[allow(clippy::too_many_arguments)] + // fn paint_highlighted_range( + // &self, + // range: Range, + // color: Color, + // corner_radius: f32, + // line_end_overshoot: f32, + // layout: &LayoutState, + // content_origin: gpui::Point, + // scroll_top: f32, + // scroll_left: f32, + // bounds: Bounds, + // cx: &mut ViewContext, + // ) { + // 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 + // - scroll_top, + // 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) + // - scroll_left + // } else { + // content_origin.x() - scroll_left + // }, + // end_x: if row == range.end.row() { + // content_origin.x() + // + line_layout.x_for_index(range.end.column() as usize) + // - scroll_left + // } else { + // content_origin.x() + line_layout.width() + line_end_overshoot + // - scroll_left + // }, + // } + // }) + // .collect(), + // }; + + // highlighted_range.paint(bounds, cx); + // } + // } + + // fn paint_blocks( + // &mut self, + // bounds: Bounds, + // visible_bounds: Bounds, + // layout: &mut LayoutState, + // editor: &mut Editor, + // cx: &mut ViewContext, + // ) { + // 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 block in &mut layout.blocks { + // let mut origin = bounds.origin() + // + vec2f( + // 0., + // block.row as f32 * layout.position_map.line_height - scroll_top, + // ); + // if !matches!(block.style, BlockStyle::Sticky) { + // origin += vec2f(-scroll_left, 0.); + // } + // block.element.paint(origin, visible_bounds, editor, cx); + // } + // } + + // fn column_pixels(&self, column: usize, cx: &ViewContext) -> f32 { + // let style = &self.style; + + // cx.text_layout_cache() + // .layout_str( + // " ".repeat(column).as_str(), + // style.text.font_size, + // &[( + // column, + // RunStyle { + // font_id: style.text.font_id, + // color: Color::black(), + // underline: Default::default(), + // }, + // )], + // ) + // .width() + // } + + // fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext) -> f32 { + // 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, - snapshot: &EditorSnapshot, - ) -> Vec { - let buffer_snapshot = &snapshot.buffer_snapshot; + // fn layout_git_gutters( + // &self, + // display_rows: Range, + // snapshot: &EditorSnapshot, + // ) -> Vec { + // 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; + // 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() - } + // 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, - relative_to: Option, - ) -> HashMap { - let mut relative_rows: HashMap = Default::default(); - let Some(relative_to) = relative_to else { - return relative_rows; - }; + // fn calculate_relative_line_numbers( + // &self, + // snapshot: &EditorSnapshot, + // rows: &Range, + // relative_to: Option, + // ) -> HashMap { + // let mut relative_rows: HashMap = 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 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::>(); + // let buffer_rows = snapshot + // .buffer_rows(start) + // .take(1 + (end - start) as usize) + // .collect::>(); - 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; - } + // 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; - } - } + // 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 - } + // relative_rows + // } - fn layout_line_numbers( - &self, - rows: Range, - active_rows: &BTreeMap, - newest_selection_head: DisplayPoint, - is_singleton: bool, - snapshot: &EditorSnapshot, - cx: &ViewContext, - ) -> ( - Vec>, - Vec>, - ) { - let style = &self.style; - let include_line_numbers = snapshot.mode == EditorMode::Full; - let mut line_number_layouts = Vec::with_capacity(rows.len()); - let mut fold_statuses = Vec::with_capacity(rows.len()); - let mut line_number = String::new(); - let is_relative = settings::get::(cx).relative_line_numbers; - let relative_to = if is_relative { - Some(newest_selection_head.row()) - } else { - None - }; + // fn layout_line_numbers( + // &self, + // rows: Range, + // active_rows: &BTreeMap, + // newest_selection_head: DisplayPoint, + // is_singleton: bool, + // snapshot: &EditorSnapshot, + // cx: &ViewContext, + // ) -> ( + // Vec>, + // Vec>, + // ) { + // let style = &self.style; + // let include_line_numbers = snapshot.mode == EditorMode::Full; + // let mut line_number_layouts = Vec::with_capacity(rows.len()); + // let mut fold_statuses = Vec::with_capacity(rows.len()); + // let mut line_number = String::new(); + // let is_relative = settings::get::(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); + // 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, style.line_number_active) - } else { - (false, style.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(); - line_number_layouts.push(Some(cx.text_layout_cache().layout_str( - &line_number, - style.text.font_size, - &[( - line_number.len(), - RunStyle { - font_id: style.text.font_id, - color, - underline: Default::default(), - }, - )], - ))); - 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); - line_number_layouts.push(None); - } - } + // 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, style.line_number_active) + // } else { + // (false, style.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(); + // line_number_layouts.push(Some(cx.text_layout_cache().layout_str( + // &line_number, + // style.text.font_size, + // &[( + // line_number.len(), + // RunStyle { + // font_id: style.text.font_id, + // color, + // underline: Default::default(), + // }, + // )], + // ))); + // 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); + // line_number_layouts.push(None); + // } + // } - (line_number_layouts, fold_statuses) - } + // (line_number_layouts, fold_statuses) + // } - fn layout_lines( - &mut self, - rows: Range, - line_number_layouts: &[Option], - snapshot: &EditorSnapshot, - cx: &ViewContext, - ) -> Vec { - if rows.start >= rows.end { - return Vec::new(); - } + // fn layout_lines( + // &mut self, + // rows: Range, + // line_number_layouts: &[Option], + // snapshot: &EditorSnapshot, + // cx: &ViewContext, + // ) -> Vec { + // if rows.start >= rows.end { + // return Vec::new(); + // } - // When the editor is empty and unfocused, then show the placeholder. - if snapshot.is_empty() { - let placeholder_style = self - .style - .placeholder_text - .as_ref() - .unwrap_or(&self.style.text); - 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 - .map(|line| { - cx.text_layout_cache().layout_str( - line, - placeholder_style.font_size, - &[( - line.len(), - RunStyle { - font_id: placeholder_style.font_id, - color: placeholder_style.color, - underline: Default::default(), - }, - )], - ) - }) - .map(|line| LineWithInvisibles { - line, - invisibles: Vec::new(), - }) - .collect() - } else { - let style = &self.style; - let chunks = snapshot.highlighted_chunks(rows.clone(), true, style); + // // When the editor is empty and unfocused, then show the placeholder. + // if snapshot.is_empty() { + // let placeholder_style = self + // .style + // .placeholder_text + // .as_ref() + // .unwrap_or(&self.style.text); + // 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 + // .map(|line| { + // cx.text_layout_cache().layout_str( + // line, + // placeholder_style.font_size, + // &[( + // line.len(), + // RunStyle { + // font_id: placeholder_style.font_id, + // color: placeholder_style.color, + // underline: Default::default(), + // }, + // )], + // ) + // }) + // .map(|line| LineWithInvisibles { + // line, + // invisibles: Vec::new(), + // }) + // .collect() + // } else { + // let style = &self.style; + // let chunks = snapshot.highlighted_chunks(rows.clone(), true, style); - LineWithInvisibles::from_chunks( - chunks, - &style.text, - cx.text_layout_cache(), - cx.font_cache(), - MAX_LINE_LEN, - rows.len() as usize, - line_number_layouts, - snapshot.mode, - ) - } - } + // LineWithInvisibles::from_chunks( + // chunks, + // &style.text, + // cx.text_layout_cache(), + // cx.font_cache(), + // MAX_LINE_LEN, + // rows.len() as usize, + // line_number_layouts, + // snapshot.mode, + // ) + // } + // } - #[allow(clippy::too_many_arguments)] - fn layout_blocks( - &mut self, - rows: Range, - snapshot: &EditorSnapshot, - editor_width: f32, - scroll_width: f32, - gutter_padding: f32, - gutter_width: f32, - em_width: f32, - text_x: f32, - line_height: f32, - style: &EditorStyle, - line_layouts: &[LineWithInvisibles], - editor: &mut Editor, - cx: &mut ViewContext, - ) -> (f32, Vec) { - let mut block_id = 0; - let scroll_x = snapshot.scroll_anchor.offset.x(); - let (fixed_blocks, non_fixed_blocks) = snapshot - .blocks_in_range(rows.clone()) - .partition::, _>(|(_, block)| match block { - TransformBlock::ExcerptHeader { .. } => false, - TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed, - }); - let mut render_block = |block: &TransformBlock, width: f32, block_id: usize| { - 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.text_layout_cache()) - .x_for_index(align_to.column() as usize) - }; + // #[allow(clippy::too_many_arguments)] + // fn layout_blocks( + // &mut self, + // rows: Range, + // snapshot: &EditorSnapshot, + // editor_width: f32, + // scroll_width: f32, + // gutter_padding: f32, + // gutter_width: f32, + // em_width: f32, + // text_x: f32, + // line_height: f32, + // style: &EditorStyle, + // line_layouts: &[LineWithInvisibles], + // editor: &mut Editor, + // cx: &mut ViewContext, + // ) -> (f32, Vec) { + // let mut block_id = 0; + // let scroll_x = snapshot.scroll_anchor.offset.x(); + // let (fixed_blocks, non_fixed_blocks) = snapshot + // .blocks_in_range(rows.clone()) + // .partition::, _>(|(_, block)| match block { + // TransformBlock::ExcerptHeader { .. } => false, + // TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed, + // }); + // let mut render_block = |block: &TransformBlock, width: f32, block_id: usize| { + // 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.text_layout_cache()) + // .x_for_index(align_to.column() as usize) + // }; - block.render(&mut BlockContext { - view_context: cx, - anchor_x, - gutter_padding, - line_height, - scroll_x, - gutter_width, - em_width, - block_id, - }) - } - TransformBlock::ExcerptHeader { - id, - buffer, - range, - starts_new_buffer, - .. - } => { - let tooltip_style = theme::current(cx).tooltip.clone(); - let include_root = editor - .project - .as_ref() - .map(|project| project.read(cx).visible_worktrees(cx).count() > 1) - .unwrap_or_default(); - let jump_icon = 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); + // block.render(&mut BlockContext { + // view_context: cx, + // anchor_x, + // gutter_padding, + // line_height, + // scroll_x, + // gutter_width, + // em_width, + // block_id, + // }) + // } + // TransformBlock::ExcerptHeader { + // id, + // buffer, + // range, + // starts_new_buffer, + // .. + // } => { + // let tooltip_style = theme::current(cx).tooltip.clone(); + // let include_root = editor + // .project + // .as_ref() + // .map(|project| project.read(cx).visible_worktrees(cx).count() > 1) + // .unwrap_or_default(); + // let jump_icon = 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); - enum JumpIcon {} - MouseEventHandler::new::((*id).into(), cx, |state, _| { - let style = style.jump_icon.style_for(state); - Svg::new("icons/arrow_up_right.svg") - .with_color(style.color) - .constrained() - .with_width(style.icon_width) - .aligned() - .contained() - .with_style(style.container) - .constrained() - .with_width(style.button_width) - .with_height(style.button_width) - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, editor, cx| { - if let Some(workspace) = editor - .workspace - .as_ref() - .and_then(|(workspace, _)| workspace.upgrade(cx)) - { - workspace.update(cx, |workspace, cx| { - Editor::jump( - workspace, - jump_path.clone(), - jump_position, - jump_anchor, - cx, - ); - }); - } - }) - .with_tooltip::( - (*id).into(), - "Jump to Buffer".to_string(), - Some(Box::new(crate::OpenExcerpts)), - tooltip_style.clone(), - cx, - ) - .aligned() - .flex_float() - }); + // enum JumpIcon {} + // MouseEventHandler::new::((*id).into(), cx, |state, _| { + // let style = style.jump_icon.style_for(state); + // Svg::new("icons/arrow_up_right.svg") + // .with_color(style.color) + // .constrained() + // .with_width(style.icon_width) + // .aligned() + // .contained() + // .with_style(style.container) + // .constrained() + // .with_width(style.button_width) + // .with_height(style.button_width) + // }) + // .with_cursor_style(CursorStyle::PointingHand) + // .on_click(MouseButton::Left, move |_, editor, cx| { + // if let Some(workspace) = editor + // .workspace + // .as_ref() + // .and_then(|(workspace, _)| workspace.upgrade(cx)) + // { + // workspace.update(cx, |workspace, cx| { + // Editor::jump( + // workspace, + // jump_path.clone(), + // jump_position, + // jump_anchor, + // cx, + // ); + // }); + // } + // }) + // .with_tooltip::( + // (*id).into(), + // "Jump to Buffer".to_string(), + // Some(Box::new(crate::OpenExcerpts)), + // tooltip_style.clone(), + // cx, + // ) + // .aligned() + // .flex_float() + // }); - if *starts_new_buffer { - let editor_font_size = style.text.font_size; - let style = &style.diagnostic_path_header; - let font_size = (style.text_scale_factor * editor_font_size).round(); + // if *starts_new_buffer { + // let editor_font_size = style.text.font_size; + // let style = &style.diagnostic_path_header; + // let font_size = (style.text_scale_factor * editor_font_size).round(); - 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| p.to_string_lossy().to_string() + "/"); - } + // 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| p.to_string_lossy().to_string() + "/"); + // } - Flex::row() - .with_child( - Label::new( - filename.unwrap_or_else(|| "untitled".to_string()), - style.filename.text.clone().with_font_size(font_size), - ) - .contained() - .with_style(style.filename.container) - .aligned(), - ) - .with_children(parent_path.map(|path| { - Label::new(path, style.path.text.clone().with_font_size(font_size)) - .contained() - .with_style(style.path.container) - .aligned() - })) - .with_children(jump_icon) - .contained() - .with_style(style.container) - .with_padding_left(gutter_padding) - .with_padding_right(gutter_padding) - .expanded() - .into_any_named("path header block") - } else { - let text_style = style.text.clone(); - Flex::row() - .with_child(Label::new("⋯", text_style)) - .with_children(jump_icon) - .contained() - .with_padding_left(gutter_padding) - .with_padding_right(gutter_padding) - .expanded() - .into_any_named("collapsed context") - } - } - }; + // Flex::row() + // .with_child( + // Label::new( + // filename.unwrap_or_else(|| "untitled".to_string()), + // style.filename.text.clone().with_font_size(font_size), + // ) + // .contained() + // .with_style(style.filename.container) + // .aligned(), + // ) + // .with_children(parent_path.map(|path| { + // Label::new(path, style.path.text.clone().with_font_size(font_size)) + // .contained() + // .with_style(style.path.container) + // .aligned() + // })) + // .with_children(jump_icon) + // .contained() + // .with_style(style.container) + // .with_padding_left(gutter_padding) + // .with_padding_right(gutter_padding) + // .expanded() + // .into_any_named("path header block") + // } else { + // let text_style = style.text.clone(); + // Flex::row() + // .with_child(Label::new("⋯", text_style)) + // .with_children(jump_icon) + // .contained() + // .with_padding_left(gutter_padding) + // .with_padding_right(gutter_padding) + // .expanded() + // .into_any_named("collapsed context") + // } + // } + // }; - element.layout( - SizeConstraint { - min: Vector2F::zero(), - max: vec2f(width, block.height() as f32 * line_height), - }, - editor, - cx, - ); - element - }; + // element.layout( + // SizeConstraint { + // min: gpui::Point::zero(), + // max: vec2f(width, block.height() as f32 * line_height), + // }, + // editor, + // cx, + // ); + // element + // }; - let mut fixed_block_max_width = 0f32; - let mut blocks = Vec::new(); - for (row, block) in fixed_blocks { - let element = render_block(block, f32::INFINITY, block_id); - block_id += 1; - fixed_block_max_width = fixed_block_max_width.max(element.size().x() + em_width); - blocks.push(BlockLayout { - row, - element, - 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 element = render_block(block, width, block_id); - block_id += 1; - blocks.push(BlockLayout { - row, - element, - style, - }); - } - ( - scroll_width.max(fixed_block_max_width - gutter_width), - blocks, - ) - } + // let mut fixed_block_max_width = 0f32; + // let mut blocks = Vec::new(); + // for (row, block) in fixed_blocks { + // let element = render_block(block, f32::INFINITY, block_id); + // block_id += 1; + // fixed_block_max_width = fixed_block_max_width.max(element.size().x() + em_width); + // blocks.push(BlockLayout { + // row, + // element, + // 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 element = render_block(block, width, block_id); + // block_id += 1; + // blocks.push(BlockLayout { + // row, + // element, + // style, + // }); + // } + // ( + // scroll_width.max(fixed_block_max_width - gutter_width), + // blocks, + // ) + // } } #[derive(Debug)] @@ -1826,194 +1780,194 @@ pub struct LineWithInvisibles { invisibles: Vec, } -impl LineWithInvisibles { - fn from_chunks<'a>( - chunks: impl Iterator>, - text_style: &TextStyle, - text_layout_cache: &TextLayoutCache, - font_cache: &Arc, - max_line_len: usize, - max_line_count: usize, - line_number_layouts: &[Option], - editor_mode: EditorMode, - ) -> Vec { - 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; - 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 { - layouts.push(Self { - line: text_layout_cache.layout_str(&line, text_style.font_size, &styles), - invisibles: invisibles.drain(..).collect(), - }); +// impl LineWithInvisibles { +// fn from_chunks<'a>( +// chunks: impl Iterator>, +// text_style: &TextStyle, +// text_layout_cache: &TextLayoutCache, +// font_cache: &Arc, +// max_line_len: usize, +// max_line_count: usize, +// line_number_layouts: &[Option], +// editor_mode: EditorMode, +// ) -> Vec { +// 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; +// 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 { +// layouts.push(Self { +// line: text_layout_cache.layout_str(&line, text_style.font_size, &styles), +// 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; - } - } +// 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 { - text_style - .clone() - .highlight(style, font_cache) - .map(Cow::Owned) - .unwrap_or_else(|_| Cow::Borrowed(text_style)) - } else { - Cow::Borrowed(text_style) - }; +// if !line_chunk.is_empty() && !line_exceeded_max_len { +// let text_style = if let Some(style) = highlighted_chunk.style { +// text_style +// .clone() +// .highlight(style, font_cache) +// .map(Cow::Owned) +// .unwrap_or_else(|_| Cow::Borrowed(text_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; - } +// 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(( - line_chunk.len(), - RunStyle { - font_id: text_style.font_id, - color: text_style.color, - underline: text_style.underline, - }, - )); +// styles.push(( +// line_chunk.len(), +// RunStyle { +// font_id: text_style.font_id, +// color: text_style.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, - }), - ) - } - } +// 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); - } - } - } +// line.push_str(line_chunk); +// } +// } +// } - layouts - } +// layouts +// } - fn draw( - &self, - layout: &LayoutState, - row: u32, - scroll_top: f32, - content_origin: Vector2F, - scroll_left: f32, - visible_text_bounds: RectF, - whitespace_setting: ShowWhitespaceSetting, - selection_ranges: &[Range], - visible_bounds: RectF, - cx: &mut ViewContext, - ) { - let line_height = layout.position_map.line_height; - let line_y = row as f32 * line_height - scroll_top; +// fn draw( +// &self, +// layout: &LayoutState, +// row: u32, +// scroll_top: f32, +// content_origin: gpui::Point, +// scroll_left: f32, +// visible_text_bounds: Bounds, +// whitespace_setting: ShowWhitespaceSetting, +// selection_ranges: &[Range], +// visible_bounds: Bounds, +// cx: &mut ViewContext, +// ) { +// let line_height = layout.position_map.line_height; +// let line_y = row as f32 * line_height - scroll_top; - self.line.paint( - content_origin + vec2f(-scroll_left, line_y), - visible_text_bounds, - line_height, - cx, - ); +// self.line.paint( +// content_origin + vec2f(-scroll_left, line_y), +// visible_text_bounds, +// line_height, +// cx, +// ); - self.draw_invisibles( - &selection_ranges, - layout, - content_origin, - scroll_left, - line_y, - row, - visible_bounds, - line_height, - whitespace_setting, - cx, - ); - } +// self.draw_invisibles( +// &selection_ranges, +// layout, +// content_origin, +// scroll_left, +// line_y, +// row, +// visible_bounds, +// line_height, +// whitespace_setting, +// cx, +// ); +// } - fn draw_invisibles( - &self, - selection_ranges: &[Range], - layout: &LayoutState, - content_origin: Vector2F, - scroll_left: f32, - line_y: f32, - row: u32, - visible_bounds: RectF, - line_height: f32, - whitespace_setting: ShowWhitespaceSetting, - cx: &mut ViewContext, - ) { - let allowed_invisibles_regions = match whitespace_setting { - ShowWhitespaceSetting::None => return, - ShowWhitespaceSetting::Selection => Some(selection_ranges), - ShowWhitespaceSetting::All => None, - }; +// fn draw_invisibles( +// &self, +// selection_ranges: &[Range], +// layout: &LayoutState, +// content_origin: gpui::Point, +// scroll_left: f32, +// line_y: f32, +// row: u32, +// visible_bounds: Bounds, +// line_height: f32, +// whitespace_setting: ShowWhitespaceSetting, +// cx: &mut ViewContext, +// ) { +// 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), - }; +// 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(0.0) / 2.0; - let origin = content_origin + vec2f(-scroll_left + x_offset + invisible_offset, line_y); +// let x_offset = self.line.x_for_index(token_offset); +// let invisible_offset = +// (layout.position_map.em_width - invisible_symbol.width()).max(0.0) / 2.0; +// let origin = content_origin + vec2f(-scroll_left + x_offset + invisible_offset, 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, visible_bounds, line_height, cx); - } - } -} +// 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, visible_bounds, line_height, cx); +// } +// } +// } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Invisible { @@ -2022,640 +1976,688 @@ enum Invisible { } impl Element for EditorElement { - type LayoutState = LayoutState; - type PaintState = (); + type ElementState = (); + + fn id(&self) -> Option { + None + } + + fn initialize( + &mut self, + view_state: &mut Editor, + element_state: Option, + cx: &mut gpui::ViewContext, + ) -> Self::ElementState { + () + } fn layout( &mut self, - constraint: SizeConstraint, - editor: &mut Editor, - cx: &mut ViewContext, - ) -> (Vector2F, Self::LayoutState) { - let mut size = constraint.max; - if size.x().is_infinite() { - unimplemented!("we don't yet handle an infinite width constraint on buffer elements"); - } - - let snapshot = editor.snapshot(cx); - let style = self.style.clone(); - - let line_height = (style.text.font_size * style.line_height_scalar).round(); - - let gutter_padding; - let gutter_width; - let gutter_margin; - if snapshot.show_gutter { - let em_width = style.text.em_width(cx.font_cache()); - gutter_padding = (em_width * style.gutter_padding_factor).round(); - gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0; - gutter_margin = -style.text.descent(cx.font_cache()); - } else { - gutter_padding = 0.0; - gutter_width = 0.0; - gutter_margin = 0.0; - }; - - let text_width = size.x() - gutter_width; - let em_width = style.text.em_width(cx.font_cache()); - let em_advance = style.text.em_advance(cx.font_cache()); - let overscroll = vec2f(em_width, 0.); - let snapshot = { - editor.set_visible_line_count(size.y() / line_height, cx); - - let editor_width = text_width - gutter_margin - overscroll.x() - 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(); - - let scroll_height = (snapshot.max_point().row() + 1) as f32 * line_height; - if let EditorMode::AutoHeight { max_lines } = snapshot.mode { - size.set_y( - scroll_height - .min(constraint.max_along(Axis::Vertical)) - .max(constraint.min_along(Axis::Vertical)) - .max(line_height) - .min(line_height * max_lines as f32), - ) - } else if let EditorMode::SingleLine = snapshot.mode { - size.set_y(line_height.max(constraint.min_along(Axis::Vertical))) - } else if size.y().is_infinite() { - size.set_y(scroll_height); - } - let gutter_size = vec2f(gutter_width, size.y()); - let text_size = vec2f(text_width, size.y()); - - let autoscroll_horizontally = editor.autoscroll_vertically(size.y(), 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 = size.y() / 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<(SelectionStyle, Vec)> = Vec::new(); - let mut active_rows = BTreeMap::new(); - let mut fold_ranges = Vec::new(); - let is_singleton = editor.is_singleton(cx); - - let highlighted_rows = editor.highlighted_rows(); - let theme = theme::current(cx); - let highlighted_ranges = editor.background_highlights_in_range( - start_anchor..end_anchor, - &snapshot.display_snapshot, - theme.as_ref(), - ); - - fold_ranges.extend( - snapshot - .folds_in_range(start_anchor..end_anchor) - .map(|anchor| { - let start = anchor.start.to_point(&snapshot.buffer_snapshot); - ( - start.row, - start.to_display_point(&snapshot.display_snapshot) - ..anchor.end.to_display_point(&snapshot), - ) - }), - ); - - let mut newest_selection_head = None; - - if editor.show_local_selections { - let mut local_selections: Vec> = 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.selection, 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 = - style.selection_style_for_room_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 { - style.selection_style_for_room_participant(participant_index.0) - } else { - style.absent_selection - }; - - // 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 = &settings::get::(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 fold_ranges: Vec<(BufferRow, Range, Color)> = fold_ranges - .into_iter() - .map(|(id, fold)| { - let color = self - .style - .folds - .ellipses - .background - .style_for(&mut cx.mouse_state::(id as usize)) - .color; - - (id, fold, color) - }) - .collect(); - - let head_for_relative = newest_selection_head.unwrap_or_else(|| { - let newest = editor.selections.newest::(cx); - SelectionLayout::new( - newest, - editor.selections.line_mode, - editor.cursor_shape, - &snapshot.display_snapshot, - true, - true, - ) - .head - }); - - let (line_number_layouts, fold_statuses) = self.layout_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 = 0.0; - let line_layouts = - self.layout_lines(start_row..end_row, &line_number_layouts, &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 style = self.style.clone(); - let longest_line_width = layout_line( - snapshot.longest_row(), - &snapshot, - &style, - cx.text_layout_cache(), - ) - .width(); - let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.x(); - let em_width = style.text.em_width(cx.font_cache()); - let (scroll_width, blocks) = self.layout_blocks( - start_row..end_row, - &snapshot, - size.x(), - scroll_width, - gutter_padding, - gutter_width, - em_width, - gutter_width + gutter_margin, - line_height, - &style, - &line_layouts, - editor, - cx, - ); - - let scroll_max = vec2f( - ((scroll_width - text_size.x()) / 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.x(), - scroll_width, - em_width, - &line_layouts, - cx, - ) - } else { - false - }; - - if clamped || autoscrolled { - snapshot = editor.snapshot(cx); - } - - let style = editor.style(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() { - context_menu = - editor.render_context_menu(newest_selection_head, style.clone(), 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(|indicator| (newest_selection_head.row(), indicator)); - } - } - - let visible_rows = start_row..start_row + line_layouts.len() as u32; - let mut hover = editor.hover_state.render( - &snapshot, - &style, - visible_rows, - editor.workspace.as_ref().map(|(w, _)| w.clone()), - cx, - ); - let mode = editor.mode; - - let mut fold_indicators = editor.render_fold_indicators( - fold_statuses, - &style, - editor.gutter_hovered, - line_height, - gutter_margin, - cx, - ); - - if let Some((_, context_menu)) = context_menu.as_mut() { - context_menu.layout( - SizeConstraint { - min: Vector2F::zero(), - max: vec2f( - cx.window_size().x() * 0.7, - (12. * line_height).min((size.y() - line_height) / 2.), - ), - }, - editor, - cx, - ); - } - - if let Some((_, indicator)) = code_actions_indicator.as_mut() { - indicator.layout( - SizeConstraint::strict_along( - Axis::Vertical, - line_height * style.code_actions.vertical_scale, - ), - editor, - cx, - ); - } - - for fold_indicator in fold_indicators.iter_mut() { - if let Some(indicator) = fold_indicator.as_mut() { - indicator.layout( - SizeConstraint::strict_along( - Axis::Vertical, - line_height * style.code_actions.vertical_scale, - ), - editor, - cx, - ); - } - } - - if let Some((_, hover_popovers)) = hover.as_mut() { - for hover_popover in hover_popovers.iter_mut() { - hover_popover.layout( - SizeConstraint { - min: Vector2F::zero(), - max: vec2f( - (120. * em_width) // Default size - .min(size.x() / 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(size.y() / 2.) // Shrink to half of the editor height - .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines - ), - }, - editor, - cx, - ); - } - } - - let invisible_symbol_font_size = self.style.text.font_size / 2.0; - let invisible_symbol_style = RunStyle { - color: self.style.whitespace, - font_id: self.style.text.font_id, - underline: Default::default(), - }; - - ( - size, - LayoutState { - mode, - position_map: Arc::new(PositionMap { - size, - scroll_max, - line_layouts, - line_height, - em_width, - em_advance, - snapshot, - }), - 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, - fold_ranges, - line_number_layouts, - display_hunks, - blocks, - selections, - context_menu, - code_actions_indicator, - fold_indicators, - tab_invisible: cx.text_layout_cache().layout_str( - "→", - invisible_symbol_font_size, - &[("→".len(), invisible_symbol_style)], - ), - space_invisible: cx.text_layout_cache().layout_str( - "•", - invisible_symbol_font_size, - &[("•".len(), invisible_symbol_style)], - ), - hover_popovers: hover, - }, - ) + view_state: &mut Editor, + element_state: &mut Self::ElementState, + cx: &mut gpui::ViewContext, + ) -> gpui::LayoutId { + cx.request_layout(Style::default().size_full(), None) } fn paint( &mut self, - bounds: RectF, - visible_bounds: RectF, - layout: &mut Self::LayoutState, - editor: &mut Editor, - cx: &mut ViewContext, - ) -> Self::PaintState { - let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); - cx.scene().push_layer(Some(visible_bounds)); + bounds: Bounds, + view_state: &mut Editor, + element_state: &mut Self::ElementState, + cx: &mut gpui::ViewContext, + ) { + let text_style = cx.text_style(); - let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size); - let text_bounds = RectF::new( - bounds.origin() + vec2f(layout.gutter_size.x(), 0.0), - layout.text_size, + let layout_text = cx.text_system().layout_text( + "hello world", + text_style.font_size, + &[TextRun { + len: "hello world".len(), + font: text_style.font, + color: text_style.color, + underline: text_style.underline, + }], + None, ); - - Self::attach_mouse_handlers( - &layout.position_map, - layout.hover_popovers.is_some(), - visible_bounds, - text_bounds, - gutter_bounds, - bounds, - cx, - ); - - self.paint_background(gutter_bounds, text_bounds, layout, cx); - if layout.gutter_size.x() > 0. { - self.paint_gutter(gutter_bounds, visible_bounds, layout, editor, cx); - } - self.paint_text(text_bounds, visible_bounds, layout, editor, cx); - - cx.scene().push_layer(Some(bounds)); - if !layout.blocks.is_empty() { - self.paint_blocks(bounds, visible_bounds, layout, editor, cx); - } - self.paint_scrollbar(bounds, layout, &editor, cx); - cx.scene().pop_layer(); - cx.scene().pop_layer(); - } - - fn rect_for_text_range( - &self, - range_utf16: Range, - bounds: RectF, - _: RectF, - layout: &Self::LayoutState, - _: &Self::PaintState, - _: &Editor, - _: &ViewContext, - ) -> Option { - let text_bounds = RectF::new( - bounds.origin() + vec2f(layout.gutter_size.x(), 0.0), - layout.text_size, - ); - let content_origin = text_bounds.origin() + vec2f(layout.gutter_margin, 0.); - let scroll_position = layout.position_map.snapshot.scroll_position(); - let start_row = scroll_position.y() as u32; - let scroll_top = scroll_position.y() * layout.position_map.line_height; - let scroll_left = scroll_position.x() * layout.position_map.em_width; - - let range_start = OffsetUtf16(range_utf16.start) - .to_display_point(&layout.position_map.snapshot.display_snapshot); - if range_start.row() < start_row { - return None; - } - - let line = &layout - .position_map - .line_layouts - .get((range_start.row() - start_row) as usize)? - .line; - let range_start_x = line.x_for_index(range_start.column() as usize); - let range_start_y = range_start.row() as f32 * layout.position_map.line_height; - Some(RectF::new( - content_origin - + vec2f( - range_start_x, - range_start_y + layout.position_map.line_height, - ) - - vec2f(scroll_left, scroll_top), - vec2f( - layout.position_map.em_width, - layout.position_map.line_height, - ), - )) - } - - fn debug( - &self, - bounds: RectF, - _: &Self::LayoutState, - _: &Self::PaintState, - _: &Editor, - _: &ViewContext, - ) -> json::Value { - json!({ - "type": "BufferElement", - "bounds": bounds.to_json() - }) } } +// impl EditorElement { +// type LayoutState = LayoutState; +// type PaintState = (); + +// fn layout( +// &mut self, +// constraint: SizeConstraint, +// editor: &mut Editor, +// cx: &mut ViewContext, +// ) -> (gpui::Point, Self::LayoutState) { +// let mut size = constraint.max; +// if size.x().is_infinite() { +// unimplemented!("we don't yet handle an infinite width constraint on buffer elements"); +// } + +// let snapshot = editor.snapshot(cx); +// let style = self.style.clone(); + +// let line_height = (style.text.font_size * style.line_height_scalar).round(); + +// let gutter_padding; +// let gutter_width; +// let gutter_margin; +// if snapshot.show_gutter { +// let em_width = style.text.em_width(cx.font_cache()); +// gutter_padding = (em_width * style.gutter_padding_factor).round(); +// gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0; +// gutter_margin = -style.text.descent(cx.font_cache()); +// } else { +// gutter_padding = 0.0; +// gutter_width = 0.0; +// gutter_margin = 0.0; +// }; + +// let text_width = size.x() - gutter_width; +// let em_width = style.text.em_width(cx.font_cache()); +// let em_advance = style.text.em_advance(cx.font_cache()); +// let overscroll = vec2f(em_width, 0.); +// let snapshot = { +// editor.set_visible_line_count(size.y() / line_height, cx); + +// let editor_width = text_width - gutter_margin - overscroll.x() - 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(); + +// let scroll_height = (snapshot.max_point().row() + 1) as f32 * line_height; +// if let EditorMode::AutoHeight { max_lines } = snapshot.mode { +// size.set_y( +// scroll_height +// .min(constraint.max_along(Axis::Vertical)) +// .max(constraint.min_along(Axis::Vertical)) +// .max(line_height) +// .min(line_height * max_lines as f32), +// ) +// } else if let EditorMode::SingleLine = snapshot.mode { +// size.set_y(line_height.max(constraint.min_along(Axis::Vertical))) +// } else if size.y().is_infinite() { +// size.set_y(scroll_height); +// } +// let gutter_size = vec2f(gutter_width, size.y()); +// let text_size = vec2f(text_width, size.y()); + +// let autoscroll_horizontally = editor.autoscroll_vertically(size.y(), 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 = size.y() / 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<(SelectionStyle, Vec)> = Vec::new(); +// let mut active_rows = BTreeMap::new(); +// let mut fold_ranges = Vec::new(); +// let is_singleton = editor.is_singleton(cx); + +// let highlighted_rows = editor.highlighted_rows(); +// let theme = theme::current(cx); +// let highlighted_ranges = editor.background_highlights_in_range( +// start_anchor..end_anchor, +// &snapshot.display_snapshot, +// theme.as_ref(), +// ); + +// fold_ranges.extend( +// snapshot +// .folds_in_range(start_anchor..end_anchor) +// .map(|anchor| { +// let start = anchor.start.to_point(&snapshot.buffer_snapshot); +// ( +// start.row, +// start.to_display_point(&snapshot.display_snapshot) +// ..anchor.end.to_display_point(&snapshot), +// ) +// }), +// ); + +// let mut newest_selection_head = None; + +// if editor.show_local_selections { +// let mut local_selections: Vec> = 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.selection, 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 = +// style.selection_style_for_room_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 { +// style.selection_style_for_room_participant(participant_index.0) +// } else { +// style.absent_selection +// }; + +// // 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 = &settings::get::(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 fold_ranges: Vec<(BufferRow, Range, Color)> = fold_ranges +// .into_iter() +// .map(|(id, fold)| { +// let color = self +// .style +// .folds +// .ellipses +// .background +// .style_for(&mut cx.mouse_state::(id as usize)) +// .color; + +// (id, fold, color) +// }) +// .collect(); + +// let head_for_relative = newest_selection_head.unwrap_or_else(|| { +// let newest = editor.selections.newest::(cx); +// SelectionLayout::new( +// newest, +// editor.selections.line_mode, +// editor.cursor_shape, +// &snapshot.display_snapshot, +// true, +// true, +// ) +// .head +// }); + +// let (line_number_layouts, fold_statuses) = self.layout_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 = 0.0; +// let line_layouts = +// self.layout_lines(start_row..end_row, &line_number_layouts, &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 style = self.style.clone(); +// let longest_line_width = layout_line( +// snapshot.longest_row(), +// &snapshot, +// &style, +// cx.text_layout_cache(), +// ) +// .width(); +// let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.x(); +// let em_width = style.text.em_width(cx.font_cache()); +// let (scroll_width, blocks) = self.layout_blocks( +// start_row..end_row, +// &snapshot, +// size.x(), +// scroll_width, +// gutter_padding, +// gutter_width, +// em_width, +// gutter_width + gutter_margin, +// line_height, +// &style, +// &line_layouts, +// editor, +// cx, +// ); + +// let scroll_max = vec2f( +// ((scroll_width - text_size.x()) / 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.x(), +// scroll_width, +// em_width, +// &line_layouts, +// cx, +// ) +// } else { +// false +// }; + +// if clamped || autoscrolled { +// snapshot = editor.snapshot(cx); +// } + +// let style = editor.style(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() { +// context_menu = +// editor.render_context_menu(newest_selection_head, style.clone(), 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(|indicator| (newest_selection_head.row(), indicator)); +// } +// } + +// let visible_rows = start_row..start_row + line_layouts.len() as u32; +// let mut hover = editor.hover_state.render( +// &snapshot, +// &style, +// visible_rows, +// editor.workspace.as_ref().map(|(w, _)| w.clone()), +// cx, +// ); +// let mode = editor.mode; + +// let mut fold_indicators = editor.render_fold_indicators( +// fold_statuses, +// &style, +// editor.gutter_hovered, +// line_height, +// gutter_margin, +// cx, +// ); + +// if let Some((_, context_menu)) = context_menu.as_mut() { +// context_menu.layout( +// SizeConstraint { +// min: gpui::Point::zero(), +// max: vec2f( +// cx.window_size().x() * 0.7, +// (12. * line_height).min((size.y() - line_height) / 2.), +// ), +// }, +// editor, +// cx, +// ); +// } + +// if let Some((_, indicator)) = code_actions_indicator.as_mut() { +// indicator.layout( +// SizeConstraint::strict_along( +// Axis::Vertical, +// line_height * style.code_actions.vertical_scale, +// ), +// editor, +// cx, +// ); +// } + +// for fold_indicator in fold_indicators.iter_mut() { +// if let Some(indicator) = fold_indicator.as_mut() { +// indicator.layout( +// SizeConstraint::strict_along( +// Axis::Vertical, +// line_height * style.code_actions.vertical_scale, +// ), +// editor, +// cx, +// ); +// } +// } + +// if let Some((_, hover_popovers)) = hover.as_mut() { +// for hover_popover in hover_popovers.iter_mut() { +// hover_popover.layout( +// SizeConstraint { +// min: gpui::Point::zero(), +// max: vec2f( +// (120. * em_width) // Default size +// .min(size.x() / 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(size.y() / 2.) // Shrink to half of the editor height +// .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines +// ), +// }, +// editor, +// cx, +// ); +// } +// } + +// let invisible_symbol_font_size = self.style.text.font_size / 2.0; +// let invisible_symbol_style = RunStyle { +// color: self.style.whitespace, +// font_id: self.style.text.font_id, +// underline: Default::default(), +// }; + +// ( +// size, +// LayoutState { +// mode, +// position_map: Arc::new(PositionMap { +// size, +// scroll_max, +// line_layouts, +// line_height, +// em_width, +// em_advance, +// snapshot, +// }), +// 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, +// fold_ranges, +// line_number_layouts, +// display_hunks, +// blocks, +// selections, +// context_menu, +// code_actions_indicator, +// fold_indicators, +// tab_invisible: cx.text_layout_cache().layout_str( +// "→", +// invisible_symbol_font_size, +// &[("→".len(), invisible_symbol_style)], +// ), +// space_invisible: cx.text_layout_cache().layout_str( +// "•", +// invisible_symbol_font_size, +// &[("•".len(), invisible_symbol_style)], +// ), +// hover_popovers: hover, +// }, +// ) +// } + +// fn paint( +// &mut self, +// bounds: Bounds, +// visible_bounds: Bounds, +// layout: &mut Self::LayoutState, +// editor: &mut Editor, +// cx: &mut ViewContext, +// ) -> Self::PaintState { +// let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); +// cx.scene().push_layer(Some(visible_bounds)); + +// let gutter_bounds = Bounds::new(bounds.origin(), layout.gutter_size); +// let text_bounds = Bounds::new( +// bounds.origin() + vec2f(layout.gutter_size.x(), 0.0), +// layout.text_size, +// ); + +// Self::attach_mouse_handlers( +// &layout.position_map, +// layout.hover_popovers.is_some(), +// visible_bounds, +// text_bounds, +// gutter_bounds, +// bounds, +// cx, +// ); + +// self.paint_background(gutter_bounds, text_bounds, layout, cx); +// if layout.gutter_size.x() > 0. { +// self.paint_gutter(gutter_bounds, visible_bounds, layout, editor, cx); +// } +// self.paint_text(text_bounds, visible_bounds, layout, editor, cx); + +// cx.scene().push_layer(Some(bounds)); +// if !layout.blocks.is_empty() { +// self.paint_blocks(bounds, visible_bounds, layout, editor, cx); +// } +// self.paint_scrollbar(bounds, layout, &editor, cx); +// cx.scene().pop_layer(); +// cx.scene().pop_layer(); +// } + +// fn rect_for_text_range( +// &self, +// range_utf16: Range, +// bounds: Bounds, +// _: Bounds, +// layout: &Self::LayoutState, +// _: &Self::PaintState, +// _: &Editor, +// _: &ViewContext, +// ) -> Option> { +// let text_bounds = Bounds::new( +// bounds.origin() + vec2f(layout.gutter_size.x(), 0.0), +// layout.text_size, +// ); +// let content_origin = text_bounds.origin() + vec2f(layout.gutter_margin, 0.); +// let scroll_position = layout.position_map.snapshot.scroll_position(); +// let start_row = scroll_position.y() as u32; +// let scroll_top = scroll_position.y() * layout.position_map.line_height; +// let scroll_left = scroll_position.x() * layout.position_map.em_width; + +// let range_start = OffsetUtf16(range_utf16.start) +// .to_display_point(&layout.position_map.snapshot.display_snapshot); +// if range_start.row() < start_row { +// return None; +// } + +// let line = &layout +// .position_map +// .line_layouts +// .get((range_start.row() - start_row) as usize)? +// .line; +// let range_start_x = line.x_for_index(range_start.column() as usize); +// let range_start_y = range_start.row() as f32 * layout.position_map.line_height; +// Some(Bounds::new( +// content_origin +// + vec2f( +// range_start_x, +// range_start_y + layout.position_map.line_height, +// ) +// - vec2f(scroll_left, scroll_top), +// vec2f( +// layout.position_map.em_width, +// layout.position_map.line_height, +// ), +// )) +// } + +// fn debug( +// &self, +// bounds: Bounds, +// _: &Self::LayoutState, +// _: &Self::PaintState, +// _: &Editor, +// _: &ViewContext, +// ) -> json::Value { +// json!({ +// "type": "BufferElement", +// "bounds": bounds.to_json() +// }) +// } +// } + type BufferRow = u32; -pub struct LayoutState { - position_map: Arc, - gutter_size: Vector2F, - gutter_padding: f32, - gutter_margin: f32, - text_size: Vector2F, - mode: EditorMode, - wrap_guides: SmallVec<[(f32, bool); 2]>, - visible_display_row_range: Range, - active_rows: BTreeMap, - highlighted_rows: Option>, - line_number_layouts: Vec>, - display_hunks: Vec, - blocks: Vec, - highlighted_ranges: Vec<(Range, Color)>, - fold_ranges: Vec<(BufferRow, Range, Color)>, - selections: Vec<(SelectionStyle, Vec)>, - scrollbar_row_range: Range, - show_scrollbars: bool, - is_singleton: bool, - max_row: u32, - context_menu: Option<(DisplayPoint, AnyElement)>, - code_actions_indicator: Option<(u32, AnyElement)>, - hover_popovers: Option<(DisplayPoint, Vec>)>, - fold_indicators: Vec>>, - tab_invisible: Line, - space_invisible: Line, -} +// pub struct LayoutState { +// position_map: Arc, +// gutter_size: gpui::Point, +// gutter_padding: f32, +// gutter_margin: f32, +// text_size: gpui::Point, +// mode: EditorMode, +// wrap_guides: SmallVec<[(f32, bool); 2]>, +// visible_display_row_range: Range, +// active_rows: BTreeMap, +// highlighted_rows: Option>, +// line_number_layouts: Vec>, +// display_hunks: Vec, +// blocks: Vec, +// highlighted_ranges: Vec<(Range, Color)>, +// fold_ranges: Vec<(BufferRow, Range, Color)>, +// selections: Vec<(SelectionStyle, Vec)>, +// scrollbar_row_range: Range, +// show_scrollbars: bool, +// is_singleton: bool, +// max_row: u32, +// context_menu: Option<(DisplayPoint, AnyElement)>, +// code_actions_indicator: Option<(u32, AnyElement)>, +// hover_popovers: Option<(DisplayPoint, Vec>)>, +// fold_indicators: Vec>>, +// tab_invisible: Line, +// space_invisible: Line, +// } struct PositionMap { - size: Vector2F, - line_height: f32, - scroll_max: Vector2F, - em_width: f32, - em_advance: f32, + size: Size, + line_height: Pixels, + scroll_max: Size, + em_width: Pixels, + em_advance: Pixels, line_layouts: Vec, snapshot: EditorSnapshot, } @@ -2689,7 +2691,11 @@ impl PointForPosition { } impl PositionMap { - fn point_for_position(&self, text_bounds: RectF, position: Vector2F) -> PointForPosition { + fn point_for_position( + &self, + text_bounds: Bounds, + position: gpui::Point, + ) -> PointForPosition { let scroll_position = self.snapshot.scroll_position(); let position = position - text_bounds.origin(); let y = position.y().max(0.0).min(self.size.y()); @@ -2734,8 +2740,8 @@ fn layout_line( row: u32, snapshot: &EditorSnapshot, style: &EditorStyle, - layout_cache: &TextLayoutCache, -) -> text_layout::Line { + text_system: &TextSystem, +) -> Line { let mut line = snapshot.line(row); if line.len() > MAX_LINE_LEN { @@ -2747,103 +2753,101 @@ fn layout_line( line.truncate(len); } - layout_cache.layout_str( + text_system.layout_str( &line, style.text.font_size, - &[( - snapshot.line_len(row) as usize, - RunStyle { - font_id: style.text.font_id, - color: Color::black(), - underline: Default::default(), - }, - )], + &[TextRun { + len: snapshot.line_len(row) as usize, + font: style.text.font.clone(), + color: Hsla::black(), + underline: Default::default(), + }], ) } #[derive(Debug)] pub struct Cursor { - origin: Vector2F, - block_width: f32, - line_height: f32, - color: Color, + origin: gpui::Point, + block_width: Pixels, + line_height: Pixels, + color: Hsla, shape: CursorShape, block_text: Option, } impl Cursor { - pub fn new( - origin: Vector2F, - block_width: f32, - line_height: f32, - color: Color, - shape: CursorShape, - block_text: Option, - ) -> Cursor { - Cursor { - origin, - block_width, - line_height, - color, - shape, - block_text, - } - } + // pub fn new( + // origin: gpui::Point, + // block_width: f32, + // line_height: f32, + // color: Color, + // shape: CursorShape, + // block_text: Option, + // ) -> Cursor { + // Cursor { + // origin, + // block_width, + // line_height, + // color, + // shape, + // block_text, + // } + // } - pub fn bounding_rect(&self, origin: Vector2F) -> RectF { - RectF::new( - self.origin + origin, - vec2f(self.block_width, self.line_height), - ) - } + // pub fn bounding_rect(&self, origin: gpui::Point) -> Bounds { + // Bounds::new( + // self.origin + origin, + // vec2f(self.block_width, self.line_height), + // ) + // } - pub fn paint(&self, origin: Vector2F, cx: &mut WindowContext) { - let bounds = match self.shape { - CursorShape::Bar => RectF::new(self.origin + origin, vec2f(2.0, self.line_height)), - CursorShape::Block | CursorShape::Hollow => RectF::new( - self.origin + origin, - vec2f(self.block_width, self.line_height), - ), - CursorShape::Underscore => RectF::new( - self.origin + origin + Vector2F::new(0.0, self.line_height - 2.0), - vec2f(self.block_width, 2.0), - ), - }; + // pub fn paint(&self, origin: gpui::Point, cx: &mut WindowContext) { + // let bounds = match self.shape { + // CursorShape::Bar => Bounds::new(self.origin + origin, vec2f(2.0, self.line_height)), + // CursorShape::Block | CursorShape::Hollow => Bounds::new( + // self.origin + origin, + // vec2f(self.block_width, self.line_height), + // ), + // CursorShape::Underscore => Bounds::new( + // self.origin + origin + gpui::Point::new(0.0, self.line_height - 2.0), + // vec2f(self.block_width, 2.0), + // ), + // }; - //Draw background or border quad - if matches!(self.shape, CursorShape::Hollow) { - cx.scene().push_quad(Quad { - bounds, - background: None, - border: Border::all(1., self.color).into(), - corner_radii: Default::default(), - }); - } else { - cx.scene().push_quad(Quad { - bounds, - background: Some(self.color), - border: Default::default(), - corner_radii: Default::default(), - }); - } + // //Draw background or border quad + // if matches!(self.shape, CursorShape::Hollow) { + // cx.scene().push_quad(Quad { + // bounds, + // background: None, + // border: Border::all(1., self.color).into(), + // corner_radii: Default::default(), + // }); + // } else { + // cx.scene().push_quad(Quad { + // bounds, + // background: Some(self.color), + // border: Default::default(), + // corner_radii: Default::default(), + // }); + // } - if let Some(block_text) = &self.block_text { - block_text.paint(self.origin + origin, bounds, self.line_height, cx); - } - } + // if let Some(block_text) = &self.block_text { + // block_text.paint(self.origin + origin, bounds, self.line_height, cx); + // } + // } - pub fn shape(&self) -> CursorShape { - self.shape - } + // pub fn shape(&self) -> CursorShape { + // self.shape + // } } #[derive(Debug)] pub struct HighlightedRange { - pub start_y: f32, - pub line_height: f32, + pub start_y: Pixels, + pub line_height: Pixels, pub lines: Vec, - pub color: Color, - pub corner_radius: f32, + pub color: Hsla, + pub corner_radius: Pixels, } #[derive(Debug)] @@ -2853,178 +2857,178 @@ pub struct HighlightedRangeLine { } impl HighlightedRange { - pub fn paint(&self, bounds: RectF, 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); - } - } + // pub fn paint(&self, bounds: Bounds, 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: f32, - lines: &[HighlightedRangeLine], - bounds: RectF, - cx: &mut WindowContext, - ) { - if lines.is_empty() { - return; - } + // fn paint_lines( + // &self, + // start_y: f32, + // lines: &[HighlightedRangeLine], + // bounds: Bounds, + // cx: &mut WindowContext, + // ) { + // if lines.is_empty() { + // return; + // } - let mut path = PathBuilder::new(); - let first_line = lines.first().unwrap(); - let last_line = lines.last().unwrap(); + // let mut path = PathBuilder::new(); + // let first_line = lines.first().unwrap(); + // let last_line = lines.last().unwrap(); - let first_top_left = vec2f(first_line.start_x, start_y); - let first_top_right = vec2f(first_line.end_x, start_y); + // let first_top_left = vec2f(first_line.start_x, start_y); + // let first_top_right = vec2f(first_line.end_x, start_y); - let curve_height = vec2f(0., self.corner_radius); - let curve_width = |start_x: f32, end_x: f32| { - let max = (end_x - start_x) / 2.; - let width = if max < self.corner_radius { - max - } else { - self.corner_radius - }; + // let curve_height = vec2f(0., self.corner_radius); + // let curve_width = |start_x: f32, end_x: f32| { + // let max = (end_x - start_x) / 2.; + // let width = if max < self.corner_radius { + // max + // } else { + // self.corner_radius + // }; - vec2f(width, 0.) - }; + // vec2f(width, 0.) + // }; - let top_curve_width = curve_width(first_line.start_x, first_line.end_x); - path.reset(first_top_right - top_curve_width); - path.curve_to(first_top_right + curve_height, first_top_right); + // let top_curve_width = curve_width(first_line.start_x, first_line.end_x); + // path.reset(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 = vec2f(line.end_x, start_y + (ix + 1) as f32 * self.line_height); + // let mut iter = lines.iter().enumerate().peekable(); + // while let Some((ix, line)) = iter.next() { + // let bottom_right = vec2f(line.end_x, start_y + (ix + 1) as f32 * self.line_height); - if let Some((_, next_line)) = iter.peek() { - let next_top_right = vec2f(next_line.end_x, bottom_right.y()); + // if let Some((_, next_line)) = iter.peek() { + // let next_top_right = vec2f(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 > 0. { - path.curve_to(bottom_right - curve_width, bottom_right); - } - path.line_to(next_top_right + curve_width); - if self.corner_radius > 0. { - 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 > 0. { - path.curve_to(bottom_right + curve_width, bottom_right); - } - path.line_to(next_top_right - curve_width); - if self.corner_radius > 0. { - 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 > 0. { - path.curve_to(bottom_right - curve_width, bottom_right); - } + // 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 > 0. { + // path.curve_to(bottom_right - curve_width, bottom_right); + // } + // path.line_to(next_top_right + curve_width); + // if self.corner_radius > 0. { + // 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 > 0. { + // path.curve_to(bottom_right + curve_width, bottom_right); + // } + // path.line_to(next_top_right - curve_width); + // if self.corner_radius > 0. { + // 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 > 0. { + // path.curve_to(bottom_right - curve_width, bottom_right); + // } - let bottom_left = vec2f(line.start_x, bottom_right.y()); - path.line_to(bottom_left + curve_width); - if self.corner_radius > 0. { - path.curve_to(bottom_left - curve_height, bottom_left); - } - } - } + // let bottom_left = vec2f(line.start_x, bottom_right.y()); + // path.line_to(bottom_left + curve_width); + // if self.corner_radius > 0. { + // 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 = vec2f(last_line.start_x, start_y + self.line_height); - path.line_to(second_top_left + curve_height); - if self.corner_radius > 0. { - path.curve_to(second_top_left + curve_width, second_top_left); - } - let first_bottom_left = vec2f(first_line.start_x, second_top_left.y()); - path.line_to(first_bottom_left - curve_width); - if self.corner_radius > 0. { - path.curve_to(first_bottom_left - curve_height, first_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 = vec2f(last_line.start_x, start_y + self.line_height); + // path.line_to(second_top_left + curve_height); + // if self.corner_radius > 0. { + // path.curve_to(second_top_left + curve_width, second_top_left); + // } + // let first_bottom_left = vec2f(first_line.start_x, second_top_left.y()); + // path.line_to(first_bottom_left - curve_width); + // if self.corner_radius > 0. { + // path.curve_to(first_bottom_left - curve_height, first_bottom_left); + // } + // } - path.line_to(first_top_left + curve_height); - if self.corner_radius > 0. { - path.curve_to(first_top_left + top_curve_width, first_top_left); - } - path.line_to(first_top_right - top_curve_width); + // path.line_to(first_top_left + curve_height); + // if self.corner_radius > 0. { + // path.curve_to(first_top_left + top_curve_width, first_top_left); + // } + // path.line_to(first_top_right - top_curve_width); - cx.scene().push_path(path.build(self.color, Some(bounds))); - } + // cx.scene().push_path(path.build(self.color, Some(bounds))); + // } } -fn range_to_bounds( - range: &Range, - content_origin: Vector2F, - scroll_left: f32, - scroll_top: f32, - visible_row_range: &Range, - line_end_overshoot: f32, - position_map: &PositionMap, -) -> impl Iterator { - let mut bounds: SmallVec<[RectF; 1]> = SmallVec::new(); +// fn range_to_bounds( +// range: &Range, +// content_origin: gpui::Point, +// scroll_left: f32, +// scroll_top: f32, +// visible_row_range: &Range, +// line_end_overshoot: f32, +// position_map: &PositionMap, +// ) -> impl Iterator> { +// let mut bounds: SmallVec<[Bounds; 1]> = SmallVec::new(); - if range.start == range.end { - return bounds.into_iter(); - } +// if range.start == range.end { +// return bounds.into_iter(); +// } - let start_row = visible_row_range.start; - let end_row = visible_row_range.end; +// let start_row = visible_row_range.start; +// let end_row = visible_row_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 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 first_y = - content_origin.y() + row_range.start as f32 * position_map.line_height - scroll_top; +// let first_y = +// content_origin.y() + row_range.start as f32 * position_map.line_height - scroll_top; - for (idx, row) in row_range.enumerate() { - let line_layout = &position_map.line_layouts[(row - start_row) as usize].line; +// for (idx, row) in row_range.enumerate() { +// let line_layout = &position_map.line_layouts[(row - start_row) as usize].line; - let start_x = if row == range.start.row() { - content_origin.x() + line_layout.x_for_index(range.start.column() as usize) - - scroll_left - } else { - content_origin.x() - scroll_left - }; +// let start_x = if row == range.start.row() { +// content_origin.x() + line_layout.x_for_index(range.start.column() as usize) +// - scroll_left +// } else { +// content_origin.x() - scroll_left +// }; - let end_x = if row == range.end.row() { - content_origin.x() + line_layout.x_for_index(range.end.column() as usize) - scroll_left - } else { - content_origin.x() + line_layout.width() + line_end_overshoot - scroll_left - }; +// let end_x = if row == range.end.row() { +// content_origin.x() + line_layout.x_for_index(range.end.column() as usize) - scroll_left +// } else { +// content_origin.x() + line_layout.width() + line_end_overshoot - scroll_left +// }; - bounds.push(RectF::from_points( - vec2f(start_x, first_y + position_map.line_height * idx as f32), - vec2f(end_x, first_y + position_map.line_height * (idx + 1) as f32), - )) - } +// bounds.push(Bounds::from_points( +// vec2f(start_x, first_y + position_map.line_height * idx as f32), +// vec2f(end_x, first_y + position_map.line_height * (idx + 1) as f32), +// )) +// } - bounds.into_iter() -} +// bounds.into_iter() +// } pub fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 { delta.powf(1.5) / 100.0 @@ -3034,445 +3038,445 @@ fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 { delta.powf(1.2) / 300.0 } -#[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; +// #[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_layout_line_numbers(cx: &mut TestAppContext) { - init_test(cx, |_| {}); - let editor = cx - .add_window(|cx| { - let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); - Editor::new(EditorMode::Full, buffer, None, None, cx) - }) - .root(cx); - let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); +// #[gpui::test] +// fn test_layout_line_numbers(cx: &mut TestAppContext) { +// init_test(cx, |_| {}); +// let editor = cx +// .add_window(|cx| { +// let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); +// Editor::new(EditorMode::Full, buffer, None, None, cx) +// }) +// .root(cx); +// let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); - let layouts = editor.update(cx, |editor, cx| { - let snapshot = editor.snapshot(cx); - element - .layout_line_numbers( - 0..6, - &Default::default(), - DisplayPoint::new(0, 0), - false, - &snapshot, - cx, - ) - .0 - }); - assert_eq!(layouts.len(), 6); +// let layouts = editor.update(cx, |editor, cx| { +// let snapshot = editor.snapshot(cx); +// element +// .layout_line_numbers( +// 0..6, +// &Default::default(), +// DisplayPoint::new(0, 0), +// false, +// &snapshot, +// cx, +// ) +// .0 +// }); +// assert_eq!(layouts.len(), 6); - let relative_rows = editor.update(cx, |editor, cx| { - let snapshot = editor.snapshot(cx); - element.calculate_relative_line_numbers(&snapshot, &(0..6), Some(3)) - }); - 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); +// let relative_rows = editor.update(cx, |editor, cx| { +// let snapshot = editor.snapshot(cx); +// element.calculate_relative_line_numbers(&snapshot, &(0..6), Some(3)) +// }); +// 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 = editor.update(cx, |editor, cx| { - let snapshot = editor.snapshot(cx); +// // works if cursor is before screen +// let relative_rows = editor.update(cx, |editor, cx| { +// let snapshot = editor.snapshot(cx); - element.calculate_relative_line_numbers(&snapshot, &(3..6), Some(1)) - }); - assert_eq!(relative_rows.len(), 3); - assert_eq!(relative_rows[&3], 2); - assert_eq!(relative_rows[&4], 3); - assert_eq!(relative_rows[&5], 4); +// element.calculate_relative_line_numbers(&snapshot, &(3..6), Some(1)) +// }); +// 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 = editor.update(cx, |editor, cx| { - let snapshot = editor.snapshot(cx); +// // works if cursor is after screen +// let relative_rows = editor.update(cx, |editor, cx| { +// let snapshot = editor.snapshot(cx); - element.calculate_relative_line_numbers(&snapshot, &(0..3), Some(6)) - }); - assert_eq!(relative_rows.len(), 3); - assert_eq!(relative_rows[&0], 5); - assert_eq!(relative_rows[&1], 4); - assert_eq!(relative_rows[&2], 3); - } +// element.calculate_relative_line_numbers(&snapshot, &(0..3), Some(6)) +// }); +// 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, |_| {}); +// #[gpui::test] +// async fn test_vim_visual_selections(cx: &mut TestAppContext) { +// init_test(cx, |_| {}); - let editor = cx - .add_window(|cx| { - let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx); - Editor::new(EditorMode::Full, buffer, None, None, cx) - }) - .root(cx); - let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); - let (_, state) = editor.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), - ]); - }); - element.layout( - SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)), - editor, - cx, - ) - }); - 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) - ); +// let editor = cx +// .add_window(|cx| { +// let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx); +// Editor::new(EditorMode::Full, buffer, None, None, cx) +// }) +// .root(cx); +// let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); +// let (_, state) = editor.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), +// ]); +// }); +// element.layout( +// SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)), +// editor, +// cx, +// ) +// }); +// 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)); +// // 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)); +// // 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![0, 3, 5, 6] - ); +// // active lines does not include 1 (even though the range of the selection does) +// assert_eq!( +// state.active_rows.keys().cloned().collect::>(), +// 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 editor = 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, None, cx) - }) - .root(cx); - let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); - let (_, state) = editor.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), - ]); - }); - element.layout( - SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)), - editor, - cx, - ) - }); +// // 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 editor = 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, None, cx) +// }) +// .root(cx); +// let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); +// let (_, state) = editor.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), +// ]); +// }); +// element.layout( +// SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)), +// editor, +// cx, +// ) +// }); - assert_eq!(state.selections.len(), 1); - let local_selections = &state.selections[0].1; - assert_eq!(local_selections.len(), 2); +// 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 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)); - } +// // 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, |_| {}); +// #[gpui::test] +// fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) { +// init_test(cx, |_| {}); - let editor = cx - .add_window(|cx| { - let buffer = MultiBuffer::build_simple("", cx); - Editor::new(EditorMode::Full, buffer, None, None, cx) - }) - .root(cx); +// let editor = cx +// .add_window(|cx| { +// let buffer = MultiBuffer::build_simple("", cx); +// Editor::new(EditorMode::Full, buffer, None, None, cx) +// }) +// .root(cx); - editor.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(|_| Empty::new().into_any()), - }], - None, - cx, - ); +// editor.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(|_| Empty::new().into_any()), +// }], +// None, +// cx, +// ); - // Blur the editor so that it displays placeholder text. - cx.blur(); - }); +// // Blur the editor so that it displays placeholder text. +// cx.blur(); +// }); - let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); - let (size, mut state) = editor.update(cx, |editor, cx| { - element.layout( - SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)), - editor, - cx, - ) - }); +// let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); +// let (size, mut state) = editor.update(cx, |editor, cx| { +// element.layout( +// SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)), +// editor, +// cx, +// ) +// }); - assert_eq!(state.position_map.line_layouts.len(), 4); - assert_eq!( - state - .line_number_layouts - .iter() - .map(Option::is_some) - .collect::>(), - &[false, false, false, true] - ); +// assert_eq!(state.position_map.line_layouts.len(), 4); +// assert_eq!( +// state +// .line_number_layouts +// .iter() +// .map(Option::is_some) +// .collect::>(), +// &[false, false, false, true] +// ); - // Don't panic. - let bounds = RectF::new(Default::default(), size); - editor.update(cx, |editor, cx| { - element.paint(bounds, bounds, &mut state, editor, cx); - }); - } +// // Don't panic. +// let bounds = Bounds::new(Default::default(), size); +// editor.update(cx, |editor, cx| { +// element.paint(bounds, bounds, &mut state, editor, cx); +// }); +// } - #[gpui::test] - fn test_all_invisibles_drawing(cx: &mut TestAppContext) { - const TAB_SIZE: u32 = 4; +// #[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}'" - ); +// 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); - }); +// 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, 500.0); +// let actual_invisibles = +// collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, 500.0); - assert_eq!(expected_invisibles, actual_invisibles); - } +// 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); - }); +// #[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", - 500.0, - ); - assert!(invisibles.is_empty(), - "For editor mode {editor_mode_without_invisibles:?} no invisibles was expected but got {invisibles:?}"); - } - } +// 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", +// 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::>(); - 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:?}"); +// #[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::>(); +// 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, |_| {}); +// 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); - }); +// // 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, editor_width); +// let actual_invisibles = +// collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, 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:?}" - ); +// // 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; - } - } +// editor_width += resize_step; +// } +// } - fn collect_invisibles_from_new_editor( - cx: &mut TestAppContext, - editor_mode: EditorMode, - input_text: &str, - editor_width: f32, - ) -> Vec { - info!( - "Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'" - ); - let editor = cx - .add_window(|cx| { - let buffer = MultiBuffer::build_simple(&input_text, cx); - Editor::new(editor_mode, buffer, None, None, cx) - }) - .root(cx); +// fn collect_invisibles_from_new_editor( +// cx: &mut TestAppContext, +// editor_mode: EditorMode, +// input_text: &str, +// editor_width: f32, +// ) -> Vec { +// info!( +// "Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'" +// ); +// let editor = cx +// .add_window(|cx| { +// let buffer = MultiBuffer::build_simple(&input_text, cx); +// Editor::new(editor_mode, buffer, None, None, cx) +// }) +// .root(cx); - let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); - let (_, layout_state) = editor.update(cx, |editor, cx| { - editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx); - editor.set_wrap_width(Some(editor_width), cx); +// let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); +// let (_, layout_state) = editor.update(cx, |editor, cx| { +// editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx); +// editor.set_wrap_width(Some(editor_width), cx); - element.layout( - SizeConstraint::new(vec2f(editor_width, 500.), vec2f(editor_width, 500.)), - editor, - cx, - ) - }); +// element.layout( +// SizeConstraint::new(vec2f(editor_width, 500.), vec2f(editor_width, 500.)), +// editor, +// cx, +// ) +// }); - layout_state - .position_map - .line_layouts - .iter() - .map(|line_with_invisibles| &line_with_invisibles.invisibles) - .flatten() - .cloned() - .collect() - } -} +// layout_state +// .position_map +// .line_layouts +// .iter() +// .map(|line_with_invisibles| &line_with_invisibles.invisibles) +// .flatten() +// .cloned() +// .collect() +// } +// } diff --git a/crates/editor2/src/hover_popover.rs b/crates/editor2/src/hover_popover.rs index 5b3985edf9..89946638b1 100644 --- a/crates/editor2/src/hover_popover.rs +++ b/crates/editor2/src/hover_popover.rs @@ -9,7 +9,7 @@ use gpui::{ actions, elements::{Flex, MouseEventHandler, Padding, ParentElement, Text}, platform::{CursorStyle, MouseButton}, - AnyElement, AppContext, Element, ModelHandle, Task, ViewContext, WeakViewHandle, + AnyElement, AppContext, Element, Model, Task, ViewContext, WeakViewHandle, }; use language::{ markdown, Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry, ParsedMarkdown, diff --git a/crates/editor2/src/inlay_hint_cache.rs b/crates/editor2/src/inlay_hint_cache.rs index 6b2712e7bf..c5f090084e 100644 --- a/crates/editor2/src/inlay_hint_cache.rs +++ b/crates/editor2/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}; @@ -3244,7 +3244,7 @@ all hints should be invalidated and requeried for all of its visible excerpts" cx.update(|cx| { cx.set_global(SettingsStore::test(cx)); - theme::init((), cx); + theme::init(cx); client::init_settings(cx); language::init(cx); Project::init_settings(cx); diff --git a/crates/editor2/src/items.rs b/crates/editor2/src/items.rs index c87606070e..2d249f0374 100644 --- a/crates/editor2/src/items.rs +++ b/crates/editor2/src/items.rs @@ -7,10 +7,8 @@ use anyhow::{Context, 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, + point, AnyElement, AppContext, AsyncAppContext, Entity, Model, Pixels, Subscription, Task, + View, ViewContext, WeakView, }; use language::{ proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point, @@ -49,12 +47,12 @@ impl FollowableItem for Editor { } fn from_state_proto( - pane: ViewHandle, - workspace: ViewHandle, + pane: View, + workspace: View, remote_id: ViewId, state: &mut Option, cx: &mut AppContext, - ) -> Option>>> { + ) -> Option>>> { let project = workspace.read(cx).project().to_owned(); let Some(proto::view::Variant::Editor(_)) = state else { return None; @@ -286,7 +284,7 @@ impl FollowableItem for Editor { fn apply_update_proto( &mut self, - project: &ModelHandle, + project: &Model, message: update_view::Variant, cx: &mut ViewContext, ) -> Task> { @@ -312,8 +310,8 @@ impl FollowableItem for Editor { } async fn update_editor_from_message( - this: WeakViewHandle, - project: ModelHandle, + this: WeakView, + project: Model, message: proto::update_view::Editor, cx: &mut AsyncAppContext, ) -> Result<()> { @@ -353,7 +351,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 +428,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, ); @@ -573,29 +571,26 @@ impl Item for Editor { } } - fn tab_content( - &self, - detail: Option, - style: &theme::Tab, - cx: &AppContext, - ) -> AnyElement { - 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(), - ) - })) - .align_children_center() - .into_any() + fn tab_content(&self, detail: Option, cx: &AppContext) -> AnyElement { + todo!(); + + // 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(), + // ) + // })) + // .align_children_center() + // .into_any() } fn for_each_project_item(&self, cx: &AppContext, f: &mut dyn FnMut(usize, &dyn project::Item)) { @@ -646,11 +641,7 @@ impl Item for Editor { } } - fn save( - &mut self, - project: ModelHandle, - cx: &mut ViewContext, - ) -> Task> { + fn save(&mut self, project: Model, cx: &mut ViewContext) -> Task> { 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(); @@ -690,7 +681,7 @@ impl Item for Editor { fn save_as( &mut self, - project: ModelHandle, + project: Model, abs_path: PathBuf, cx: &mut ViewContext, ) -> Task> { @@ -710,11 +701,7 @@ impl Item for Editor { }) } - fn reload( - &mut self, - project: ModelHandle, - cx: &mut ViewContext, - ) -> Task> { + fn reload(&mut self, project: Model, cx: &mut ViewContext) -> Task> { let buffer = self.buffer().clone(); let buffers = self.buffer.read(cx).all_buffers(); let reload_buffers = @@ -761,11 +748,11 @@ impl Item for Editor { result } - fn as_searchable(&self, handle: &ViewHandle) -> Option> { + fn as_searchable(&self, handle: &View) -> Option> { Some(Box::new(handle.clone())) } - fn pixel_position_of_cursor(&self, _: &AppContext) -> Option { + fn pixel_position_of_cursor(&self, _: &AppContext) -> Option> { self.pixel_position_of_newest_cursor } @@ -773,35 +760,36 @@ impl Item for Editor { ToolbarItemLocation::PrimaryLeft { flex: None } } - fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option> { - 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)?; - let buffer = multibuffer.buffer(buffer_id)?; + fn breadcrumbs(&self, cx: &AppContext) -> Option> { + todo!(); + // 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)?; + // 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 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) + // 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) { @@ -810,7 +798,7 @@ impl Item for Editor { self.workspace = Some((workspace.weak_handle(), workspace.database_id())); fn serialize( - buffer: ModelHandle, + buffer: Model, workspace_id: WorkspaceId, item_id: ItemId, cx: &mut AppContext, @@ -847,12 +835,12 @@ impl Item for Editor { } fn deserialize( - project: ModelHandle, - _workspace: WeakViewHandle, + project: Model, + _workspace: WeakView, workspace_id: workspace::WorkspaceId, item_id: ItemId, cx: &mut ViewContext, - ) -> Task>> { + ) -> Task>> { 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 @@ -894,8 +882,8 @@ impl ProjectItem for Editor { type Item = Buffer; fn for_project_item( - project: ModelHandle, - buffer: ModelHandle, + project: Model, + buffer: Model, cx: &mut ViewContext, ) -> Self { Self::for_buffer(buffer, Some(project), cx) @@ -1134,89 +1122,89 @@ pub struct CursorPosition { _observe_active_editor: Option, } -impl Default for CursorPosition { - fn default() -> Self { - Self::new() - } -} +// 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, - } - } +// impl CursorPosition { +// pub fn new() -> Self { +// Self { +// position: None, +// selected_count: 0, +// _observe_active_editor: None, +// } +// } - fn update_position(&mut self, editor: ViewHandle, cx: &mut ViewContext) { - let editor = editor.read(cx); - let buffer = editor.buffer().read(cx).snapshot(cx); +// fn update_position(&mut self, editor: View, cx: &mut ViewContext) { +// let editor = editor.read(cx); +// let buffer = editor.buffer().read(cx).snapshot(cx); - self.selected_count = 0; - let mut last_selection: Option> = None; - for selection in editor.selections.all::(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)); +// self.selected_count = 0; +// let mut last_selection: Option> = None; +// for selection in editor.selections.all::(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(); - } -} +// cx.notify(); +// } +// } -impl Entity for CursorPosition { - type Event = (); -} +// impl Entity for CursorPosition { +// type Event = (); +// } -impl View for CursorPosition { - fn ui_name() -> &'static str { - "CursorPosition" - } +// impl View for CursorPosition { +// fn ui_name() -> &'static str { +// "CursorPosition" +// } - fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - if let Some(position) = self.position { - let theme = &theme::current(cx).workspace.status_bar; - 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(); - } - Label::new(text, theme.cursor_position.clone()).into_any() - } else { - Empty::new().into_any() - } - } -} +// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { +// if let Some(position) = self.position { +// let theme = &theme::current(cx).workspace.status_bar; +// 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(); +// } +// Label::new(text, theme.cursor_position.clone()).into_any() +// } else { +// Empty::new().into_any() +// } +// } +// } -impl StatusItemView for CursorPosition { - fn set_active_pane_item( - &mut self, - active_pane_item: Option<&dyn ItemHandle>, - cx: &mut ViewContext, - ) { - if let Some(editor) = active_pane_item.and_then(|item| item.act_as::(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; - } +// impl StatusItemView for CursorPosition { +// fn set_active_pane_item( +// &mut self, +// active_pane_item: Option<&dyn ItemHandle>, +// cx: &mut ViewContext, +// ) { +// if let Some(editor) = active_pane_item.and_then(|item| item.act_as::(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(); - } -} +// cx.notify(); +// } +// } fn path_for_buffer<'a>( - buffer: &ModelHandle, + buffer: &Model, height: usize, include_filename: bool, cx: &'a AppContext, diff --git a/crates/editor2/src/movement.rs b/crates/editor2/src/movement.rs index 332eb3c1c5..05b9b039c4 100644 --- a/crates/editor2/src/movement.rs +++ b/crates/editor2/src/movement.rs @@ -919,7 +919,7 @@ mod tests { fn init_test(cx: &mut gpui::AppContext) { cx.set_global(SettingsStore::test(cx)); - theme::init((), cx); + theme::init(cx); language::init(cx); crate::init(cx); Project::init_settings(cx); diff --git a/crates/editor2/src/scroll/actions.rs b/crates/editor2/src/scroll/actions.rs index 82c2e10589..e943bed767 100644 --- a/crates/editor2/src/scroll/actions.rs +++ b/crates/editor2/src/scroll/actions.rs @@ -1,152 +1,148 @@ -use gpui::{actions, geometry::vector::Vector2F, AppContext, Axis, ViewContext}; -use language::Bias; +use gpui::AppContext; -use crate::{Editor, EditorMode}; - -use super::{autoscroll::Autoscroll, scroll_amount::ScrollAmount, ScrollAnchor}; - -actions!( - editor, - [ - LineDown, - LineUp, - HalfPageDown, - HalfPageUp, - PageDown, - PageUp, - NextScreen, - ScrollCursorTop, - ScrollCursorCenter, - ScrollCursorBottom, - ] -); +// 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) - }); + /// todo!() + // 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) + // }); } -impl Editor { - pub fn next_screen(&mut self, _: &NextScreen, cx: &mut ViewContext) -> Option<()> { - if self.take_rename(true, cx).is_some() { - return None; - } +// impl Editor { +// pub fn next_screen(&mut self, _: &NextScreen, cx: &mut ViewContext) -> Option<()> { +// if self.take_rename(true, cx).is_some() { +// return None; +// } - if self.mouse_context_menu.read(cx).visible() { - return None; - } +// if self.mouse_context_menu.read(cx).visible() { +// return None; +// } - if matches!(self.mode, EditorMode::SingleLine) { - cx.propagate_action(); - return None; - } - self.request_autoscroll(Autoscroll::Next, cx); - Some(()) - } +// if matches!(self.mode, EditorMode::SingleLine) { +// cx.propagate_action(); +// return None; +// } +// self.request_autoscroll(Autoscroll::Next, cx); +// Some(()) +// } - pub fn scroll( - &mut self, - scroll_position: Vector2F, - axis: Option, - cx: &mut ViewContext, - ) { - self.scroll_manager.update_ongoing_scroll(axis); - self.set_scroll_position(scroll_position, cx); - } +// pub fn scroll( +// &mut self, +// scroll_position: Vector2F, +// axis: Option, +// cx: &mut ViewContext, +// ) { +// self.scroll_manager.update_ongoing_scroll(axis); +// self.set_scroll_position(scroll_position, cx); +// } - fn scroll_cursor_top(editor: &mut Editor, _: &ScrollCursorTop, cx: &mut ViewContext) { - let snapshot = editor.snapshot(cx).display_snapshot; - let scroll_margin_rows = editor.vertical_scroll_margin() as u32; +// fn scroll_cursor_top(editor: &mut Editor, _: &ScrollCursorTop, cx: &mut ViewContext) { +// let snapshot = editor.snapshot(cx).display_snapshot; +// let scroll_margin_rows = editor.vertical_scroll_margin() as u32; - let mut new_screen_top = editor.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); +// let mut new_screen_top = editor.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( - ScrollAnchor { - anchor: new_anchor, - offset: Default::default(), - }, - cx, - ) - } +// editor.set_scroll_anchor( +// ScrollAnchor { +// anchor: new_anchor, +// offset: Default::default(), +// }, +// cx, +// ) +// } - fn scroll_cursor_center( - editor: &mut Editor, - _: &ScrollCursorCenter, - cx: &mut ViewContext, - ) { - let snapshot = editor.snapshot(cx).display_snapshot; - let visible_rows = if let Some(visible_rows) = editor.visible_line_count() { - visible_rows as u32 - } else { - return; - }; +// fn scroll_cursor_center( +// editor: &mut Editor, +// _: &ScrollCursorCenter, +// cx: &mut ViewContext, +// ) { +// let snapshot = editor.snapshot(cx).display_snapshot; +// let visible_rows = if let Some(visible_rows) = editor.visible_line_count() { +// visible_rows as u32 +// } else { +// return; +// }; - let mut new_screen_top = editor.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); +// let mut new_screen_top = editor.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( - ScrollAnchor { - anchor: new_anchor, - offset: Default::default(), - }, - cx, - ) - } +// editor.set_scroll_anchor( +// ScrollAnchor { +// anchor: new_anchor, +// offset: Default::default(), +// }, +// cx, +// ) +// } - fn scroll_cursor_bottom( - editor: &mut Editor, - _: &ScrollCursorBottom, - cx: &mut ViewContext, - ) { - 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() { - visible_rows as u32 - } else { - return; - }; +// fn scroll_cursor_bottom( +// editor: &mut Editor, +// _: &ScrollCursorBottom, +// cx: &mut ViewContext, +// ) { +// 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() { +// visible_rows as u32 +// } else { +// return; +// }; - let mut new_screen_top = editor.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); +// let mut new_screen_top = editor.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); - editor.set_scroll_anchor( - ScrollAnchor { - anchor: new_anchor, - offset: Default::default(), - }, - cx, - ) - } -} +// editor.set_scroll_anchor( +// ScrollAnchor { +// anchor: new_anchor, +// offset: Default::default(), +// }, +// cx, +// ) +// } +// } diff --git a/crates/editor2/src/selections_collection.rs b/crates/editor2/src/selections_collection.rs index 4b2dc855c3..151a27c901 100644 --- a/crates/editor2/src/selections_collection.rs +++ b/crates/editor2/src/selections_collection.rs @@ -6,7 +6,7 @@ use std::{ }; use collections::HashMap; -use gpui::{AppContext, ModelHandle}; +use gpui::{AppContext, Model}; use itertools::Itertools; use language::{Bias, Point, Selection, SelectionGoal, TextDimension, ToPoint}; use util::post_inc; diff --git a/crates/editor2/src/test.rs b/crates/editor2/src/test.rs index 08cc533d62..45a61e58de 100644 --- a/crates/editor2/src/test.rs +++ b/crates/editor2/src/test.rs @@ -6,7 +6,7 @@ use crate::{ DisplayPoint, Editor, EditorMode, MultiBuffer, }; -use gpui::{ModelHandle, ViewContext}; +use gpui::{Model, ViewContext}; use project::Project; use util::test::{marked_text_offsets, marked_text_ranges}; diff --git a/crates/language2/src/buffer.rs b/crates/language2/src/buffer.rs index 36c1f39e1c..b19cf97354 100644 --- a/crates/language2/src/buffer.rs +++ b/crates/language2/src/buffer.rs @@ -1,6 +1,7 @@ pub use crate::{ diagnostic_set::DiagnosticSet, highlight_map::{HighlightId, HighlightMap}, + markdown::ParsedMarkdown, proto, BracketPair, Grammar, Language, LanguageConfig, LanguageRegistry, PLAIN_TEXT, }; use crate::{ diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 6d29751073..bf4ed73c27 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -13,9 +13,12 @@ mod status_bar; mod toolbar; mod workspace_settings; -use crate::persistence::model::{ - DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup, - SerializedWorkspace, +pub use crate::persistence::{ + model::{ + DockData, DockStructure, ItemId, SerializedItem, SerializedPane, SerializedPaneGroup, + SerializedWorkspace, + }, + WorkspaceDb, }; use anyhow::{anyhow, Context as _, Result}; use call2::ActiveCall; @@ -44,15 +47,13 @@ use node_runtime::NodeRuntime; use notifications::{simple_message_notification::MessageNotification, NotificationHandle}; pub use pane::*; pub use pane_group::*; -use persistence::{ - model::{ItemId, WorkspaceLocation}, - DB, -}; +use persistence::{model::WorkspaceLocation, DB}; use postage::stream::Stream; use project2::{Project, ProjectEntryId, ProjectPath, Worktree}; use serde::Deserialize; use settings2::Settings; use status_bar::StatusBar; +pub use status_bar::StatusItemView; use std::{ any::TypeId, borrow::Cow, diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index 47aafe85fa..d510de24ee 100644 --- a/crates/zed2/Cargo.toml +++ b/crates/zed2/Cargo.toml @@ -34,7 +34,7 @@ copilot = { package = "copilot2", path = "../copilot2" } # copilot_button = { path = "../copilot_button" } # diagnostics = { path = "../diagnostics" } db = { package = "db2", path = "../db2" } -# editor = { path = "../editor" } +editor = { package="editor2", path = "../editor2" } # feedback = { path = "../feedback" } # file_finder = { path = "../file_finder" } # search = { path = "../search" }