diff --git a/crates/editor2/src/display_map.rs b/crates/editor2/src/display_map.rs index 3cf4d7340f..2955a929f8 100644 --- a/crates/editor2/src/display_map.rs +++ b/crates/editor2/src/display_map.rs @@ -241,7 +241,7 @@ impl DisplayMap { pub fn set_font(&self, font: Font, font_size: Pixels, cx: &mut ModelContext) -> bool { self.wrap_map - .update(cx, |map, cx| map.set_font(font, font_size, cx)) + .update(cx, |map, cx| map.set_font_with_size(font, font_size, cx)) } pub fn set_fold_ellipses_color(&mut self, color: Hsla) -> bool { @@ -1388,7 +1388,7 @@ pub fn next_rows(display_row: u32, display_map: &DisplaySnapshot) -> impl Iterat // ); // // Re-wrap on font size changes -// map.update(cx, |map, cx| map.set_font(font_id, font_size + 3., cx)); +// map.update(cx, |map, cx| map.set_font_with_size(font_id, font_size + 3., cx)); // let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); // assert_eq!( diff --git a/crates/editor2/src/display_map/fold_map.rs b/crates/editor2/src/display_map/fold_map.rs index 61047043df..88cd202b08 100644 --- a/crates/editor2/src/display_map/fold_map.rs +++ b/crates/editor2/src/display_map/fold_map.rs @@ -1629,7 +1629,8 @@ mod tests { } fn init_test(cx: &mut gpui::AppContext) { - cx.set_global(SettingsStore::test(cx)); + let store = SettingsStore::test(cx); + cx.set_global(store); } impl FoldMap { diff --git a/crates/editor2/src/display_map/inlay_map.rs b/crates/editor2/src/display_map/inlay_map.rs index 0afed4028d..a6bc6343f4 100644 --- a/crates/editor2/src/display_map/inlay_map.rs +++ b/crates/editor2/src/display_map/inlay_map.rs @@ -1889,7 +1889,8 @@ mod tests { } fn init_test(cx: &mut AppContext) { - cx.set_global(SettingsStore::test(cx)); + let store = SettingsStore::test(cx); + cx.set_global(store); theme::init(cx); } } diff --git a/crates/editor2/src/display_map/wrap_map.rs b/crates/editor2/src/display_map/wrap_map.rs index c7c55348b1..408142ae07 100644 --- a/crates/editor2/src/display_map/wrap_map.rs +++ b/crates/editor2/src/display_map/wrap_map.rs @@ -23,7 +23,7 @@ pub struct WrapMap { edits_since_sync: Patch, wrap_width: Option, background_task: Option>, - font: (Font, Pixels), + font_with_size: (Font, Pixels), } #[derive(Clone)] @@ -76,7 +76,7 @@ impl WrapMap { ) -> (Model, WrapSnapshot) { let handle = cx.build_model(|cx| { let mut this = Self { - font: (font, font_size), + font_with_size: (font, font_size), wrap_width: None, pending_edits: Default::default(), interpolated_edits: Default::default(), @@ -116,9 +116,16 @@ impl WrapMap { (self.snapshot.clone(), mem::take(&mut self.edits_since_sync)) } - pub fn set_font(&mut self, font: Font, font_size: Pixels, cx: &mut ModelContext) -> bool { - if (font, font_size) != self.font { - self.font = (font, font_size); + pub fn set_font_with_size( + &mut self, + font: Font, + font_size: Pixels, + cx: &mut ModelContext, + ) -> bool { + let font_with_size = (font, font_size); + + if font_with_size != self.font_with_size { + self.font_with_size = font_with_size; self.rewrap(cx); true } else { @@ -149,10 +156,9 @@ impl WrapMap { let mut new_snapshot = self.snapshot.clone(); let mut edits = Patch::default(); let text_system = cx.text_system().clone(); - let (font_id, font_size) = self.font; + let (font, font_size) = self.font_with_size.clone(); let task = cx.background_executor().spawn(async move { - if let Some(mut line_wrapper) = - text_system.line_wrapper(font_id, font_size).log_err() + if let Some(mut line_wrapper) = text_system.line_wrapper(font, font_size).log_err() { let tab_snapshot = new_snapshot.tab_snapshot.clone(); let range = TabPoint::zero()..tab_snapshot.max_point(); @@ -236,11 +242,11 @@ impl WrapMap { let pending_edits = self.pending_edits.clone(); let mut snapshot = self.snapshot.clone(); let text_system = cx.text_system().clone(); - let (font_id, font_size) = self.font; + let (font, font_size) = self.font_with_size.clone(); let update_task = cx.background_executor().spawn(async move { let mut edits = Patch::default(); if let Some(mut line_wrapper) = - text_system.line_wrapper(font_id, font_size).log_err() + text_system.line_wrapper(font, font_size).log_err() { for (tab_snapshot, tab_edits) in pending_edits { let wrap_edits = snapshot diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 11ad04a659..e2d923c339 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -35,8 +35,8 @@ pub use element::{ use futures::FutureExt; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - AnyElement, AppContext, BackgroundExecutor, Element, EventEmitter, Model, Pixels, Render, - Subscription, Task, TextStyle, View, ViewContext, WeakView, WindowContext, + AnyElement, AppContext, BackgroundExecutor, Context, Element, EventEmitter, Model, Pixels, + Render, Subscription, Task, TextStyle, View, ViewContext, WeakView, WindowContext, }; use hover_popover::HoverState; pub use items::MAX_TAB_TITLE_LEN; @@ -64,7 +64,7 @@ use std::{ cmp::Reverse, ops::{Deref, DerefMut, Range}, sync::Arc, - time::Duration, + time::{Duration, Instant}, }; pub use sum_tree::Bias; use util::{ResultExt, TryFutureExt}; @@ -1810,14 +1810,14 @@ impl Editor { // ) // } - // 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_buffer( + buffer: Model, + project: Option>, + cx: &mut ViewContext, + ) -> Self { + let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx)); + Self::new(EditorMode::Full, buffer, project, cx) + } // pub fn for_multibuffer( // buffer: Model, @@ -1827,171 +1827,175 @@ impl Editor { // 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(), + pub fn clone(&self, cx: &mut ViewContext) -> Self { + let mut clone = Self::new( + self.mode, + self.buffer.clone(), + self.project.clone(), + // todo! + // 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>, + // todo!() + // get_field_editor_theme: Option>, + cx: &mut ViewContext, + ) -> Self { + todo!("old version below") + } + // 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, - // ); - // 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 selections = SelectionsCollection::new(display_map.clone(), buffer.clone()); + // let blink_manager = cx.add_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx)); - // 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 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 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 // } + // 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, @@ -2316,19 +2320,19 @@ impl Editor { // result // } - // pub fn edit(&mut self, edits: I, cx: &mut ViewContext) - // where - // I: IntoIterator, T)>, - // S: ToOffset, - // T: Into>, - // { - // if self.read_only { - // return; - // } + 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)); - // } + self.buffer + .update(cx, |buffer, cx| buffer.edit(edits, None, cx)); + } // pub fn edit_with_autoindent(&mut self, edits: I, cx: &mut ViewContext) // where @@ -8058,48 +8062,50 @@ impl Editor { // }); // } - // 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) - // } + 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 start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext) { + todo!() + // 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"); - // } + fn end_transaction_at( + &mut self, + now: Instant, + cx: &mut ViewContext, + ) -> Option { + todo!() + // 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 - // } - // } + // cx.emit(Event::Edited); + // Some(tx_id) + // } else { + // None + // } + } // pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext) { // let mut fold_ranges = Vec::new(); @@ -9304,7 +9310,7 @@ impl Render for Editor { // let style = self.style(cx); // let font_changed = self.display_map.update(cx, |map, cx| { // map.set_fold_ellipses_color(style.folds.ellipses.text_color); -// map.set_font(style.text.font_id, style.text.font_size, cx) +// map.set_font_with_size(style.text.font_id, style.text.font_size, cx) // }); // if font_changed { diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 6f36cf8e06..645cdc7646 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -2704,10 +2704,11 @@ impl PositionMap { let y = position.y.max(px(0.)).min(self.size.width); let x = position.x + (scroll_position.x * self.em_width); let row = (f32::from(y / self.line_height) + scroll_position.y) as u32; + let (column, x_overshoot_after_line_end) = if let Some(line) = self .line_layouts - .get(row as usize - scroll_position.y.into()) - .map(|LineWithInvisibles { line, .. }| line) + .get(row as usize - scroll_position.y as usize) + .map(|&LineWithInvisibles { ref line, .. }| line) { if let Some(ix) = line.index_for_x(x) { (ix as u32, px(0.)) diff --git a/crates/editor2/src/items.rs b/crates/editor2/src/items.rs index 717cdc08a2..b0b344a868 100644 --- a/crates/editor2/src/items.rs +++ b/crates/editor2/src/items.rs @@ -3,12 +3,12 @@ use crate::{ movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor, Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _, }; -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use collections::HashSet; use futures::future::try_join_all; use gpui::{ point, AnyElement, AppContext, AsyncAppContext, Entity, EntityId, Model, Pixels, SharedString, - Subscription, Task, View, ViewContext, WeakView, + Subscription, Task, View, ViewContext, VisualContext, WeakView, }; use language::{ proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point, @@ -566,11 +566,9 @@ impl Item for Editor { Some(file_path.into()) } - fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option { - match path_for_buffer(&self.buffer, detail, true, cx)? { - Cow::Borrowed(path) => Some(path.to_string_lossy().into()), - Cow::Owned(path) => Some(path.to_string_lossy().to_string().into()), - } + fn tab_description<'a>(&self, detail: usize, cx: &'a AppContext) -> Option { + let path = path_for_buffer(&self.buffer, detail, true, cx)?; + Some(path.to_string_lossy().to_string().into()) } fn tab_content(&self, detail: Option, cx: &AppContext) -> AnyElement { @@ -613,11 +611,11 @@ impl Item for Editor { &self, _workspace_id: WorkspaceId, cx: &mut ViewContext, - ) -> Option> + ) -> Option> where Self: Sized, { - Some(self.clone()) + Some(cx.build_view(|cx| self.clone(cx))) } fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext) { @@ -706,7 +704,9 @@ impl Item for Editor { .as_singleton() .expect("cannot call save_as on an excerpt list"); - let file_extension = abs_path.extension().map(|a| a.to_string_lossy.to_string()); + let file_extension = abs_path + .extension() + .map(|a| a.to_string_lossy().to_string()); self.report_editor_event("save", file_extension, cx); project.update(cx, |project, cx| { @@ -807,7 +807,7 @@ impl Item for Editor { fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext) { let workspace_id = workspace.database_id(); - let item_id = cx.view_id(); + let item_id = cx.view().entity_id().as_u64() as ItemId; self.workspace = Some((workspace.weak_handle(), workspace.database_id())); fn serialize( @@ -835,7 +835,12 @@ impl Item for Editor { cx.subscribe(&buffer, |this, buffer, event, cx| { if let Some((_, workspace_id)) = this.workspace.as_ref() { if let language::Event::FileHandleChanged = event { - serialize(buffer, *workspace_id, cx.view_id(), cx); + serialize( + buffer, + *workspace_id, + cx.view().entity_id().as_u64() as ItemId, + cx, + ); } } }) @@ -877,10 +882,11 @@ impl Item for Editor { let (_, project_item) = project_item.await?; let buffer = project_item .downcast::() - .context("Project item at stored path was not a buffer")?; + .map_err(|_| anyhow!("Project item at stored path was not a buffer"))?; Ok(pane.update(&mut cx, |_, cx| { - cx.add_view(|cx| { + cx.build_view(|cx| { let mut editor = Editor::for_buffer(buffer, Some(project), cx); + editor.read_scroll_position_from_db(item_id, workspace_id, cx); editor }) diff --git a/crates/editor2/src/link_go_to_definition.rs b/crates/editor2/src/link_go_to_definition.rs index da2b49def6..f9a5497846 100644 --- a/crates/editor2/src/link_go_to_definition.rs +++ b/crates/editor2/src/link_go_to_definition.rs @@ -108,59 +108,61 @@ pub fn update_go_to_definition_link( shift_held: bool, cx: &mut ViewContext, ) { - let pending_nonempty_selection = editor.has_pending_nonempty_selection(); - - // Store new mouse point as an anchor - let snapshot = editor.snapshot(cx); - let trigger_point = match origin { - Some(GoToDefinitionTrigger::Text(p)) => { - Some(TriggerPoint::Text(snapshot.buffer_snapshot.anchor_before( - p.to_offset(&snapshot.display_snapshot, Bias::Left), - ))) - } - Some(GoToDefinitionTrigger::InlayHint(p, lsp_location, language_server_id)) => { - Some(TriggerPoint::InlayHint(p, lsp_location, language_server_id)) - } - None => None, - }; - - // If the new point is the same as the previously stored one, return early - if let (Some(a), Some(b)) = ( - &trigger_point, - &editor.link_go_to_definition_state.last_trigger_point, - ) { - match (a, b) { - (TriggerPoint::Text(anchor_a), TriggerPoint::Text(anchor_b)) => { - if anchor_a.cmp(anchor_b, &snapshot.buffer_snapshot).is_eq() { - return; - } - } - (TriggerPoint::InlayHint(range_a, _, _), TriggerPoint::InlayHint(range_b, _, _)) => { - if range_a == range_b { - return; - } - } - _ => {} - } - } - - editor.link_go_to_definition_state.last_trigger_point = trigger_point.clone(); - - if pending_nonempty_selection { - hide_link_definition(editor, cx); - return; - } - - if cmd_held { - if let Some(trigger_point) = trigger_point { - let kind = trigger_point.definition_kind(shift_held); - show_link_definition(kind, editor, trigger_point, snapshot, cx); - return; - } - } - - hide_link_definition(editor, cx); + todo!("old version below"); } +// let pending_nonempty_selection = editor.has_pending_nonempty_selection(); + +// // Store new mouse point as an anchor +// let snapshot = editor.snapshot(cx); +// let trigger_point = match origin { +// Some(GoToDefinitionTrigger::Text(p)) => { +// Some(TriggerPoint::Text(snapshot.buffer_snapshot.anchor_before( +// p.to_offset(&snapshot.display_snapshot, Bias::Left), +// ))) +// } +// Some(GoToDefinitionTrigger::InlayHint(p, lsp_location, language_server_id)) => { +// Some(TriggerPoint::InlayHint(p, lsp_location, language_server_id)) +// } +// None => None, +// }; + +// // If the new point is the same as the previously stored one, return early +// if let (Some(a), Some(b)) = ( +// &trigger_point, +// &editor.link_go_to_definition_state.last_trigger_point, +// ) { +// match (a, b) { +// (TriggerPoint::Text(anchor_a), TriggerPoint::Text(anchor_b)) => { +// if anchor_a.cmp(anchor_b, &snapshot.buffer_snapshot).is_eq() { +// return; +// } +// } +// (TriggerPoint::InlayHint(range_a, _, _), TriggerPoint::InlayHint(range_b, _, _)) => { +// if range_a == range_b { +// return; +// } +// } +// _ => {} +// } +// } + +// editor.link_go_to_definition_state.last_trigger_point = trigger_point.clone(); + +// if pending_nonempty_selection { +// hide_link_definition(editor, cx); +// return; +// } + +// if cmd_held { +// if let Some(trigger_point) = trigger_point { +// let kind = trigger_point.definition_kind(shift_held); +// show_link_definition(kind, editor, trigger_point, snapshot, cx); +// return; +// } +// } + +// hide_link_definition(editor, cx); +// } pub fn update_inlay_link_and_hover_points( snapshot: &DisplaySnapshot, @@ -351,201 +353,204 @@ pub fn show_link_definition( snapshot: EditorSnapshot, cx: &mut ViewContext, ) { - let same_kind = editor.link_go_to_definition_state.kind == Some(definition_kind); - if !same_kind { - hide_link_definition(editor, cx); - } - - if editor.pending_rename.is_some() { - return; - } - - let trigger_anchor = trigger_point.anchor(); - let (buffer, buffer_position) = if let Some(output) = editor - .buffer - .read(cx) - .text_anchor_for_position(trigger_anchor.clone(), cx) - { - output - } else { - return; - }; - - let excerpt_id = if let Some((excerpt_id, _, _)) = editor - .buffer() - .read(cx) - .excerpt_containing(trigger_anchor.clone(), cx) - { - excerpt_id - } else { - return; - }; - - let project = if let Some(project) = editor.project.clone() { - project - } else { - return; - }; - - // Don't request again if the location is within the symbol region of a previous request with the same kind - if let Some(symbol_range) = &editor.link_go_to_definition_state.symbol_range { - if same_kind && symbol_range.point_within_range(&trigger_point, &snapshot) { - return; - } - } - - let task = cx.spawn(|this, mut cx| { - async move { - let result = match &trigger_point { - TriggerPoint::Text(_) => { - // query the LSP for definition info - cx.update(|cx| { - project.update(cx, |project, cx| match definition_kind { - LinkDefinitionKind::Symbol => { - project.definition(&buffer, buffer_position, cx) - } - - LinkDefinitionKind::Type => { - project.type_definition(&buffer, buffer_position, cx) - } - }) - }) - .await - .ok() - .map(|definition_result| { - ( - definition_result.iter().find_map(|link| { - link.origin.as_ref().map(|origin| { - let start = snapshot - .buffer_snapshot - .anchor_in_excerpt(excerpt_id.clone(), origin.range.start); - let end = snapshot - .buffer_snapshot - .anchor_in_excerpt(excerpt_id.clone(), origin.range.end); - RangeInEditor::Text(start..end) - }) - }), - definition_result - .into_iter() - .map(GoToDefinitionLink::Text) - .collect(), - ) - }) - } - TriggerPoint::InlayHint(highlight, lsp_location, server_id) => Some(( - Some(RangeInEditor::Inlay(highlight.clone())), - vec![GoToDefinitionLink::InlayHint( - lsp_location.clone(), - *server_id, - )], - )), - }; - - this.update(&mut cx, |this, cx| { - // Clear any existing highlights - this.clear_highlights::(cx); - this.link_go_to_definition_state.kind = Some(definition_kind); - this.link_go_to_definition_state.symbol_range = result - .as_ref() - .and_then(|(symbol_range, _)| symbol_range.clone()); - - if let Some((symbol_range, definitions)) = result { - this.link_go_to_definition_state.definitions = definitions.clone(); - - let buffer_snapshot = buffer.read(cx).snapshot(); - - // Only show highlight if there exists a definition to jump to that doesn't contain - // the current location. - let any_definition_does_not_contain_current_location = - definitions.iter().any(|definition| { - match &definition { - GoToDefinitionLink::Text(link) => { - if link.target.buffer == buffer { - let range = &link.target.range; - // Expand range by one character as lsp definition ranges include positions adjacent - // but not contained by the symbol range - let start = buffer_snapshot.clip_offset( - range - .start - .to_offset(&buffer_snapshot) - .saturating_sub(1), - Bias::Left, - ); - let end = buffer_snapshot.clip_offset( - range.end.to_offset(&buffer_snapshot) + 1, - Bias::Right, - ); - let offset = buffer_position.to_offset(&buffer_snapshot); - !(start <= offset && end >= offset) - } else { - true - } - } - GoToDefinitionLink::InlayHint(_, _) => true, - } - }); - - if any_definition_does_not_contain_current_location { - // todo!() - // // Highlight symbol using theme link definition highlight style - // let style = theme::current(cx).editor.link_definition; - // let highlight_range = - // symbol_range.unwrap_or_else(|| match &trigger_point { - // TriggerPoint::Text(trigger_anchor) => { - // let snapshot = &snapshot.buffer_snapshot; - // // If no symbol range returned from language server, use the surrounding word. - // let (offset_range, _) = - // snapshot.surrounding_word(*trigger_anchor); - // RangeInEditor::Text( - // snapshot.anchor_before(offset_range.start) - // ..snapshot.anchor_after(offset_range.end), - // ) - // } - // TriggerPoint::InlayHint(highlight, _, _) => { - // RangeInEditor::Inlay(highlight.clone()) - // } - // }); - - // match highlight_range { - // RangeInEditor::Text(text_range) => this - // .highlight_text::( - // vec![text_range], - // style, - // cx, - // ), - // RangeInEditor::Inlay(highlight) => this - // .highlight_inlays::( - // vec![highlight], - // style, - // cx, - // ), - // } - } else { - hide_link_definition(this, cx); - } - } - })?; - - Ok::<_, anyhow::Error>(()) - } - .log_err() - }); - - editor.link_go_to_definition_state.task = Some(task); + todo!("old implementation below") } +// let same_kind = editor.link_go_to_definition_state.kind == Some(definition_kind); +// if !same_kind { +// hide_link_definition(editor, cx); +// } + +// if editor.pending_rename.is_some() { +// return; +// } + +// let trigger_anchor = trigger_point.anchor(); +// let (buffer, buffer_position) = if let Some(output) = editor +// .buffer +// .read(cx) +// .text_anchor_for_position(trigger_anchor.clone(), cx) +// { +// output +// } else { +// return; +// }; + +// let excerpt_id = if let Some((excerpt_id, _, _)) = editor +// .buffer() +// .read(cx) +// .excerpt_containing(trigger_anchor.clone(), cx) +// { +// excerpt_id +// } else { +// return; +// }; + +// let project = if let Some(project) = editor.project.clone() { +// project +// } else { +// return; +// }; + +// // Don't request again if the location is within the symbol region of a previous request with the same kind +// if let Some(symbol_range) = &editor.link_go_to_definition_state.symbol_range { +// if same_kind && symbol_range.point_within_range(&trigger_point, &snapshot) { +// return; +// } +// } + +// let task = cx.spawn(|this, mut cx| { +// async move { +// let result = match &trigger_point { +// TriggerPoint::Text(_) => { +// // query the LSP for definition info +// cx.update(|cx| { +// project.update(cx, |project, cx| match definition_kind { +// LinkDefinitionKind::Symbol => { +// project.definition(&buffer, buffer_position, cx) +// } + +// LinkDefinitionKind::Type => { +// project.type_definition(&buffer, buffer_position, cx) +// } +// }) +// }) +// .await +// .ok() +// .map(|definition_result| { +// ( +// definition_result.iter().find_map(|link| { +// link.origin.as_ref().map(|origin| { +// let start = snapshot +// .buffer_snapshot +// .anchor_in_excerpt(excerpt_id.clone(), origin.range.start); +// let end = snapshot +// .buffer_snapshot +// .anchor_in_excerpt(excerpt_id.clone(), origin.range.end); +// RangeInEditor::Text(start..end) +// }) +// }), +// definition_result +// .into_iter() +// .map(GoToDefinitionLink::Text) +// .collect(), +// ) +// }) +// } +// TriggerPoint::InlayHint(highlight, lsp_location, server_id) => Some(( +// Some(RangeInEditor::Inlay(highlight.clone())), +// vec![GoToDefinitionLink::InlayHint( +// lsp_location.clone(), +// *server_id, +// )], +// )), +// }; + +// this.update(&mut cx, |this, cx| { +// // Clear any existing highlights +// this.clear_highlights::(cx); +// this.link_go_to_definition_state.kind = Some(definition_kind); +// this.link_go_to_definition_state.symbol_range = result +// .as_ref() +// .and_then(|(symbol_range, _)| symbol_range.clone()); + +// if let Some((symbol_range, definitions)) = result { +// this.link_go_to_definition_state.definitions = definitions.clone(); + +// let buffer_snapshot = buffer.read(cx).snapshot(); + +// // Only show highlight if there exists a definition to jump to that doesn't contain +// // the current location. +// let any_definition_does_not_contain_current_location = +// definitions.iter().any(|definition| { +// match &definition { +// GoToDefinitionLink::Text(link) => { +// if link.target.buffer == buffer { +// let range = &link.target.range; +// // Expand range by one character as lsp definition ranges include positions adjacent +// // but not contained by the symbol range +// let start = buffer_snapshot.clip_offset( +// range +// .start +// .to_offset(&buffer_snapshot) +// .saturating_sub(1), +// Bias::Left, +// ); +// let end = buffer_snapshot.clip_offset( +// range.end.to_offset(&buffer_snapshot) + 1, +// Bias::Right, +// ); +// let offset = buffer_position.to_offset(&buffer_snapshot); +// !(start <= offset && end >= offset) +// } else { +// true +// } +// } +// GoToDefinitionLink::InlayHint(_, _) => true, +// } +// }); + +// if any_definition_does_not_contain_current_location { +// // todo!() +// // // Highlight symbol using theme link definition highlight style +// // let style = theme::current(cx).editor.link_definition; +// // let highlight_range = +// // symbol_range.unwrap_or_else(|| match &trigger_point { +// // TriggerPoint::Text(trigger_anchor) => { +// // let snapshot = &snapshot.buffer_snapshot; +// // // If no symbol range returned from language server, use the surrounding word. +// // let (offset_range, _) = +// // snapshot.surrounding_word(*trigger_anchor); +// // RangeInEditor::Text( +// // snapshot.anchor_before(offset_range.start) +// // ..snapshot.anchor_after(offset_range.end), +// // ) +// // } +// // TriggerPoint::InlayHint(highlight, _, _) => { +// // RangeInEditor::Inlay(highlight.clone()) +// // } +// // }); + +// // match highlight_range { +// // RangeInEditor::Text(text_range) => this +// // .highlight_text::( +// // vec![text_range], +// // style, +// // cx, +// // ), +// // RangeInEditor::Inlay(highlight) => this +// // .highlight_inlays::( +// // vec![highlight], +// // style, +// // cx, +// // ), +// // } +// } else { +// hide_link_definition(this, cx); +// } +// } +// })?; + +// Ok::<_, anyhow::Error>(()) +// } +// .log_err() +// }); + +// editor.link_go_to_definition_state.task = Some(task); +// } pub fn hide_link_definition(editor: &mut Editor, cx: &mut ViewContext) { - if editor.link_go_to_definition_state.symbol_range.is_some() - || !editor.link_go_to_definition_state.definitions.is_empty() - { - editor.link_go_to_definition_state.symbol_range.take(); - editor.link_go_to_definition_state.definitions.clear(); - cx.notify(); - } + todo!() + // if editor.link_go_to_definition_state.symbol_range.is_some() + // || !editor.link_go_to_definition_state.definitions.is_empty() + // { + // editor.link_go_to_definition_state.symbol_range.take(); + // editor.link_go_to_definition_state.definitions.clear(); + // cx.notify(); + // } - editor.link_go_to_definition_state.task = None; + // editor.link_go_to_definition_state.task = None; - editor.clear_highlights::(cx); + // editor.clear_highlights::(cx); } pub fn go_to_fetched_definition( diff --git a/crates/editor2/src/movement.rs b/crates/editor2/src/movement.rs index 0749c3f178..962f4f60e6 100644 --- a/crates/editor2/src/movement.rs +++ b/crates/editor2/src/movement.rs @@ -1,7 +1,8 @@ use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint}; use crate::{char_kind, CharKind, EditorStyle, ToOffset, ToPoint}; -use gpui::TextSystem; +use gpui::{px, TextSystem}; use language::Point; +use serde::de::IntoDeserializer; use std::ops::Range; #[derive(Debug, PartialEq)] @@ -93,9 +94,9 @@ pub fn up_by_rows( text_layout_details: &TextLayoutDetails, ) -> (DisplayPoint, SelectionGoal) { let mut goal_x = match goal { - SelectionGoal::HorizontalPosition(x) => x, - SelectionGoal::WrappedHorizontalPosition((_, x)) => x, - SelectionGoal::HorizontalRange { end, .. } => end, + SelectionGoal::HorizontalPosition(x) => x.into(), // todo!("Can the fields in SelectionGoal by Pixels? We should extract a geometry crate and depend on that.") + SelectionGoal::WrappedHorizontalPosition((_, x)) => x.into(), + SelectionGoal::HorizontalRange { end, .. } => end.into(), _ => map.x_for_point(start, text_layout_details), }; @@ -110,14 +111,17 @@ pub fn up_by_rows( return (start, goal); } else { point = DisplayPoint::new(0, 0); - goal_x = 0.0; + goal_x = px(0.); } let mut clipped_point = map.clip_point(point, Bias::Left); if clipped_point.row() < point.row() { clipped_point = map.clip_point(point, Bias::Right); } - (clipped_point, SelectionGoal::HorizontalPosition(goal_x)) + ( + clipped_point, + SelectionGoal::HorizontalPosition(goal_x.into()), + ) } pub fn down_by_rows( @@ -129,9 +133,9 @@ pub fn down_by_rows( text_layout_details: &TextLayoutDetails, ) -> (DisplayPoint, SelectionGoal) { let mut goal_x = match goal { - SelectionGoal::HorizontalPosition(x) => x, - SelectionGoal::WrappedHorizontalPosition((_, x)) => x, - SelectionGoal::HorizontalRange { end, .. } => end, + SelectionGoal::HorizontalPosition(x) => x.into(), + SelectionGoal::WrappedHorizontalPosition((_, x)) => x.into(), + SelectionGoal::HorizontalRange { end, .. } => end.into(), _ => map.x_for_point(start, text_layout_details), }; @@ -150,7 +154,10 @@ pub fn down_by_rows( if clipped_point.row() > point.row() { clipped_point = map.clip_point(point, Bias::Left); } - (clipped_point, SelectionGoal::HorizontalPosition(goal_x)) + ( + clipped_point, + SelectionGoal::HorizontalPosition(goal_x.into()), + ) } pub fn line_beginning( diff --git a/crates/editor2/src/persistence.rs b/crates/editor2/src/persistence.rs index c1c1455014..6e37735c13 100644 --- a/crates/editor2/src/persistence.rs +++ b/crates/editor2/src/persistence.rs @@ -3,7 +3,6 @@ use std::path::PathBuf; use db::sqlez_macros::sql; use db::{define_connection, query}; -use gpui::EntityId; use workspace::{ItemId, WorkspaceDb, WorkspaceId}; define_connection!( @@ -67,7 +66,7 @@ impl EditorDb { query! { pub async fn save_scroll_position( - item_id: EntityId, + item_id: ItemId, workspace_id: WorkspaceId, top_row: u32, vertical_offset: f32, diff --git a/crates/editor2/src/scroll.rs b/crates/editor2/src/scroll.rs index 3c2a25dd93..0c6829fbcb 100644 --- a/crates/editor2/src/scroll.rs +++ b/crates/editor2/src/scroll.rs @@ -16,7 +16,7 @@ use std::{ time::{Duration, Instant}, }; use util::ResultExt; -use workspace::WorkspaceId; +use workspace::{ItemId, WorkspaceId}; use self::{ autoscroll::{Autoscroll, AutoscrollStrategy}, @@ -228,7 +228,7 @@ impl ScrollManager { self.show_scrollbar(cx); self.autoscroll_request.take(); if let Some(workspace_id) = workspace_id { - let item_id = cx.view().entity_id(); + let item_id = cx.view().entity_id().as_u64() as ItemId; cx.foreground_executor() .spawn(async move { @@ -355,31 +355,31 @@ impl Editor { // self.scroll_manager.anchor.scroll_position(&display_map) // } - // pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext) { - // hide_hover(self, cx); - // let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1); - // let top_row = scroll_anchor - // .anchor - // .to_point(&self.buffer().read(cx).snapshot(cx)) - // .row; - // self.scroll_manager - // .set_anchor(scroll_anchor, top_row, true, false, workspace_id, cx); - // } + pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext) { + hide_hover(self, cx); + let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1); + let top_row = scroll_anchor + .anchor + .to_point(&self.buffer().read(cx).snapshot(cx)) + .row; + self.scroll_manager + .set_anchor(scroll_anchor, top_row, true, false, workspace_id, cx); + } - // pub(crate) fn set_scroll_anchor_remote( - // &mut self, - // scroll_anchor: ScrollAnchor, - // cx: &mut ViewContext, - // ) { - // hide_hover(self, cx); - // let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1); - // let top_row = scroll_anchor - // .anchor - // .to_point(&self.buffer().read(cx).snapshot(cx)) - // .row; - // self.scroll_manager - // .set_anchor(scroll_anchor, top_row, false, false, workspace_id, cx); - // } + pub(crate) fn set_scroll_anchor_remote( + &mut self, + scroll_anchor: ScrollAnchor, + cx: &mut ViewContext, + ) { + hide_hover(self, cx); + let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1); + let top_row = scroll_anchor + .anchor + .to_point(&self.buffer().read(cx).snapshot(cx)) + .row; + self.scroll_manager + .set_anchor(scroll_anchor, top_row, false, false, workspace_id, cx); + } // pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext) { // if matches!(self.mode, EditorMode::SingleLine) { @@ -426,24 +426,24 @@ impl Editor { // Ordering::Greater // } - // pub fn read_scroll_position_from_db( - // &mut self, - // item_id: usize, - // workspace_id: WorkspaceId, - // cx: &mut ViewContext, - // ) { - // let scroll_position = DB.get_scroll_position(item_id, workspace_id); - // if let Ok(Some((top_row, x, y))) = scroll_position { - // let top_anchor = self - // .buffer() - // .read(cx) - // .snapshot(cx) - // .anchor_at(Point::new(top_row as u32, 0), Bias::Left); - // let scroll_anchor = ScrollAnchor { - // offset: Point::new(x, y), - // anchor: top_anchor, - // }; - // self.set_scroll_anchor(scroll_anchor, cx); - // } - // } + pub fn read_scroll_position_from_db( + &mut self, + item_id: usize, + workspace_id: WorkspaceId, + cx: &mut ViewContext, + ) { + let scroll_position = DB.get_scroll_position(item_id, workspace_id); + if let Ok(Some((top_row, x, y))) = scroll_position { + let top_anchor = self + .buffer() + .read(cx) + .snapshot(cx) + .anchor_at(Point::new(top_row as u32, 0), Bias::Left); + let scroll_anchor = ScrollAnchor { + offset: gpui::Point::new(x, y), + anchor: top_anchor, + }; + self.set_scroll_anchor(scroll_anchor, cx); + } + } } diff --git a/crates/text2/src/selection.rs b/crates/text2/src/selection.rs index 4bf205dd5d..4f1f9a2922 100644 --- a/crates/text2/src/selection.rs +++ b/crates/text2/src/selection.rs @@ -1,5 +1,3 @@ -use gpui::Pixels; - use crate::{Anchor, BufferSnapshot, TextDimension}; use std::cmp::Ordering; use std::ops::Range; @@ -7,8 +5,8 @@ use std::ops::Range; #[derive(Copy, Clone, Debug, PartialEq)] pub enum SelectionGoal { None, - HorizontalPosition(Pixels), - HorizontalRange { start: Pixels, end: Pixels }, + HorizontalPosition(f32), // todo!("Can we use pixels here without adding a runtime gpui dependency?") + HorizontalRange { start: f32, end: f32 }, WrappedHorizontalPosition((u32, f32)), }