From a8a5785ec5de08866ad172f8009b842eb8f8cddb Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 1 Dec 2023 12:03:21 +0100 Subject: [PATCH 1/5] Re-enable opening excerpts in Editor --- crates/editor2/src/editor.rs | 100 ++++++++++++++++------------------ crates/editor2/src/element.rs | 2 +- 2 files changed, 48 insertions(+), 54 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 6ef843dfd10ca80196889c768d7422c769e1bb4a..ffc32dc83fe2282262c0f8b6e4d7f85579f025d5 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -8798,62 +8798,56 @@ impl Editor { // 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(); - // return; - // }; + fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext) { + let buffer = self.buffer.read(cx); + if buffer.is_singleton() { + cx.propagate(); + return; + } - // let editor = editor_handle.read(cx); - // let buffer = editor.buffer.read(cx); - // if buffer.is_singleton() { - // cx.propagate(); - // return; - // } + let Some(workspace) = self.workspace() else { + cx.propagate(); + return; + }; - // let mut new_selections_by_buffer = HashMap::default(); - // for selection in 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) - // } - // } + let mut new_selections_by_buffer = HashMap::default(); + for selection in self.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()); - // }); - // } + self.push_to_nav_history(self.selections.newest_anchor().head(), None, cx); + + // We defer the pane interaction because we ourselves are a workspace item + // and activating a new item causes the pane to call a method on us reentrantly, + // which panics if we're on the stack. + cx.window_context().defer(move |cx| { + workspace.update(cx, |workspace, cx| { + let pane = workspace.active_pane().clone(); + pane.update(cx, |pane, _| pane.disable_history()); + + for (buffer, ranges) in new_selections_by_buffer.into_iter() { + let editor = workspace.open_project_item::(buffer, cx); + editor.update(cx, |editor, cx| { + editor.change_selections(Some(Autoscroll::newest()), cx, |s| { + s.select_ranges(ranges); + }); + }); + } + + pane.update(cx, |pane, _| pane.enable_history()); + }) + }); + } fn jump( &mut self, diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 928d50224e702979813202755e966ae6a5e9b125..ad8434c188cb71ff86885bb2f9ca5a10717138f2 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -263,7 +263,7 @@ impl EditorElement { register_action(view, cx, Editor::fold_selected_ranges); register_action(view, cx, Editor::show_completions); register_action(view, cx, Editor::toggle_code_actions); - // on_action(cx, Editor::open_excerpts); todo!() + register_action(view, cx, Editor::open_excerpts); register_action(view, cx, Editor::toggle_soft_wrap); register_action(view, cx, Editor::toggle_inlay_hints); register_action(view, cx, hover_popover::hover); From faa896343b4d296588c0f97dd34144dcf03f0c8f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 1 Dec 2023 12:11:54 +0100 Subject: [PATCH 2/5] Register NewFile and NewFileInDirection from Editor --- crates/editor2/src/editor.rs | 168 ++++++---------------------------- crates/editor2/src/element.rs | 2 - 2 files changed, 26 insertions(+), 144 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index ffc32dc83fe2282262c0f8b6e4d7f85579f025d5..faa6e5f5cae7426aee6fa6717729f2874049499a 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -407,133 +407,17 @@ pub fn init_settings(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) { init_settings(cx); - // cx.register_action_type(Editor::new_file); - // cx.register_action_type(Editor::new_file_in_direction); - // cx.register_action_type(Editor::cancel); - // cx.register_action_type(Editor::newline); - // cx.register_action_type(Editor::newline_above); - // cx.register_action_type(Editor::newline_below); - // cx.register_action_type(Editor::backspace); - // cx.register_action_type(Editor::delete); - // cx.register_action_type(Editor::tab); - // cx.register_action_type(Editor::tab_prev); - // cx.register_action_type(Editor::indent); - // cx.register_action_type(Editor::outdent); - // cx.register_action_type(Editor::delete_line); - // cx.register_action_type(Editor::join_lines); - // cx.register_action_type(Editor::sort_lines_case_sensitive); - // cx.register_action_type(Editor::sort_lines_case_insensitive); - // cx.register_action_type(Editor::reverse_lines); - // cx.register_action_type(Editor::shuffle_lines); - // cx.register_action_type(Editor::convert_to_upper_case); - // cx.register_action_type(Editor::convert_to_lower_case); - // cx.register_action_type(Editor::convert_to_title_case); - // cx.register_action_type(Editor::convert_to_snake_case); - // cx.register_action_type(Editor::convert_to_kebab_case); - // cx.register_action_type(Editor::convert_to_upper_camel_case); - // cx.register_action_type(Editor::convert_to_lower_camel_case); - // cx.register_action_type(Editor::delete_to_previous_word_start); - // cx.register_action_type(Editor::delete_to_previous_subword_start); - // cx.register_action_type(Editor::delete_to_next_word_end); - // cx.register_action_type(Editor::delete_to_next_subword_end); - // cx.register_action_type(Editor::delete_to_beginning_of_line); - // cx.register_action_type(Editor::delete_to_end_of_line); - // cx.register_action_type(Editor::cut_to_end_of_line); - // cx.register_action_type(Editor::duplicate_line); - // cx.register_action_type(Editor::move_line_up); - // cx.register_action_type(Editor::move_line_down); - // cx.register_action_type(Editor::transpose); - // cx.register_action_type(Editor::cut); - // cx.register_action_type(Editor::copy); - // cx.register_action_type(Editor::paste); - // cx.register_action_type(Editor::undo); - // cx.register_action_type(Editor::redo); - // cx.register_action_type(Editor::move_page_up); - // cx.register_action_type::(); - // cx.register_action_type(Editor::move_page_down); - // cx.register_action_type(Editor::next_screen); - // cx.register_action_type::(); - // cx.register_action_type::(); - // cx.register_action_type(Editor::move_to_previous_word_start); - // cx.register_action_type(Editor::move_to_previous_subword_start); - // cx.register_action_type(Editor::move_to_next_word_end); - // cx.register_action_type(Editor::move_to_next_subword_end); - // cx.register_action_type(Editor::move_to_beginning_of_line); - // cx.register_action_type(Editor::move_to_end_of_line); - // cx.register_action_type(Editor::move_to_start_of_paragraph); - // cx.register_action_type(Editor::move_to_end_of_paragraph); - // cx.register_action_type(Editor::move_to_beginning); - // cx.register_action_type(Editor::move_to_end); - // cx.register_action_type(Editor::select_up); - // cx.register_action_type(Editor::select_down); - // cx.register_action_type(Editor::select_left); - // cx.register_action_type(Editor::select_right); - // cx.register_action_type(Editor::select_to_previous_word_start); - // cx.register_action_type(Editor::select_to_previous_subword_start); - // cx.register_action_type(Editor::select_to_next_word_end); - // cx.register_action_type(Editor::select_to_next_subword_end); - // cx.register_action_type(Editor::select_to_beginning_of_line); - // cx.register_action_type(Editor::select_to_end_of_line); - // cx.register_action_type(Editor::select_to_start_of_paragraph); - // cx.register_action_type(Editor::select_to_end_of_paragraph); - // cx.register_action_type(Editor::select_to_beginning); - // cx.register_action_type(Editor::select_to_end); - // cx.register_action_type(Editor::select_all); - // cx.register_action_type(Editor::select_all_matches); - // cx.register_action_type(Editor::select_line); - // cx.register_action_type(Editor::split_selection_into_lines); - // cx.register_action_type(Editor::add_selection_above); - // cx.register_action_type(Editor::add_selection_below); - // cx.register_action_type(Editor::select_next); - // cx.register_action_type(Editor::select_previous); - // cx.register_action_type(Editor::toggle_comments); - // cx.register_action_type(Editor::select_larger_syntax_node); - // cx.register_action_type(Editor::select_smaller_syntax_node); - // cx.register_action_type(Editor::move_to_enclosing_bracket); - // cx.register_action_type(Editor::undo_selection); - // cx.register_action_type(Editor::redo_selection); - // cx.register_action_type(Editor::go_to_diagnostic); - // cx.register_action_type(Editor::go_to_prev_diagnostic); - // cx.register_action_type(Editor::go_to_hunk); - // cx.register_action_type(Editor::go_to_prev_hunk); - // cx.register_action_type(Editor::go_to_definition); - // cx.register_action_type(Editor::go_to_definition_split); - // cx.register_action_type(Editor::go_to_type_definition); - // cx.register_action_type(Editor::go_to_type_definition_split); - // cx.register_action_type(Editor::fold); - // cx.register_action_type(Editor::fold_at); - // cx.register_action_type(Editor::unfold_lines); - // cx.register_action_type(Editor::unfold_at); - // cx.register_action_type(Editor::gutter_hover); - // cx.register_action_type(Editor::fold_selected_ranges); - // cx.register_action_type(Editor::show_completions); - // cx.register_action_type(Editor::toggle_code_actions); - // cx.register_action_type(Editor::open_excerpts); - // cx.register_action_type(Editor::toggle_soft_wrap); - // cx.register_action_type(Editor::toggle_inlay_hints); - // cx.register_action_type(Editor::reveal_in_finder); - // cx.register_action_type(Editor::copy_path); - // cx.register_action_type(Editor::copy_relative_path); - // cx.register_action_type(Editor::copy_highlight_json); - // cx.add_async_action(Editor::format); - // cx.register_action_type(Editor::restart_language_server); - // cx.register_action_type(Editor::show_character_palette); - // cx.add_async_action(Editor::confirm_completion); - // cx.add_async_action(Editor::confirm_code_action); - // cx.add_async_action(Editor::rename); - // cx.add_async_action(Editor::confirm_rename); - // cx.add_async_action(Editor::find_all_references); - // cx.register_action_type(Editor::next_copilot_suggestion); - // cx.register_action_type(Editor::previous_copilot_suggestion); - // cx.register_action_type(Editor::copilot_suggest); - // cx.register_action_type(Editor::context_menu_first); - // cx.register_action_type(Editor::context_menu_prev); - // cx.register_action_type(Editor::context_menu_next); - // cx.register_action_type(Editor::context_menu_last); workspace::register_project_item::(cx); workspace::register_followable_item::(cx); workspace::register_deserializable_item::(cx); + cx.observe_new_views( + |workspace: &mut Workspace, cx: &mut ViewContext| { + workspace.register_action(Editor::new_file); + workspace.register_action(Editor::new_file_in_direction); + }, + ) + .detach(); } trait InvalidationRegion { @@ -1990,25 +1874,25 @@ impl Editor { } } - // 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(); - // } 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 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(); + } 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.build_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() diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index ad8434c188cb71ff86885bb2f9ca5a10717138f2..4a27f457f6b5b97afa0965b057d9558ff21cdec9 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -139,8 +139,6 @@ impl EditorElement { register_action(view, cx, Editor::move_right); register_action(view, cx, Editor::move_down); register_action(view, cx, Editor::move_up); - // on_action(cx, Editor::new_file); todo!() - // on_action(cx, Editor::new_file_in_direction); todo!() register_action(view, cx, Editor::cancel); register_action(view, cx, Editor::newline); register_action(view, cx, Editor::newline_above); From 8e4f2fb25aa92375cbe383a61ca9bcdff2b07ab5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 1 Dec 2023 12:32:30 +0100 Subject: [PATCH 3/5] Remove all todos in Editor's Item implementation --- crates/editor2/src/editor.rs | 81 +--- crates/editor2/src/items.rs | 583 ++++++++++++++-------------- crates/workspace2/src/item.rs | 24 +- crates/workspace2/src/workspace2.rs | 2 +- 4 files changed, 302 insertions(+), 388 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index faa6e5f5cae7426aee6fa6717729f2874049499a..57759bcc5493ba8c4cf8740bf4027ddd81e09343 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -1642,14 +1642,7 @@ impl Editor { } 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, - ); + let mut clone = Self::new(self.mode, self.buffer.clone(), self.project.clone(), cx); self.display_map.update(cx, |display_map, cx| { let snapshot = display_map.snapshot(cx); clone.display_map.update(cx, |display_map, cx| { @@ -1666,17 +1659,11 @@ impl Editor { mode: EditorMode, buffer: Model, project: Option>, - // todo!() - // get_field_editor_theme: Option>, cx: &mut ViewContext, ) -> Self { - // let editor_view_id = cx.view_id(); let style = cx.text_style(); let font_size = style.font_size.to_pixels(cx.rem_size()); let display_map = cx.build_model(|cx| { - // todo!() - // let settings = settings::get::(cx); - // let style = build_style(settings, get_field_editor_theme.as_deref(), None, cx); DisplayMap::new(buffer.clone(), style.font(), font_size, None, 2, 1, cx) }); @@ -9670,72 +9657,6 @@ impl InputHandler for Editor { } } -// fn build_style( -// settings: &ThemeSettings, -// get_field_editor_theme: Option<&GetFieldEditorTheme>, -// override_text_style: Option<&OverrideTextStyle>, -// cx: &mut AppContext, -// ) -> EditorStyle { -// let font_cache = cx.font_cache(); -// let line_height_scalar = settings.line_height(); -// let theme_id = settings.theme.meta.id; -// let mut theme = settings.theme.editor.clone(); -// let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme { -// let field_editor_theme = get_field_editor_theme(&settings.theme); -// theme.text_color = field_editor_theme.text.color; -// theme.selection = field_editor_theme.selection; -// theme.background = field_editor_theme -// .container -// .background_color -// .unwrap_or_default(); -// EditorStyle { -// text: field_editor_theme.text, -// placeholder_text: field_editor_theme.placeholder_text, -// line_height_scalar, -// theme, -// theme_id, -// } -// } else { -// todo!(); -// // let font_family_id = settings.buffer_font_family; -// // let font_family_name = cx.font_cache().family_name(font_family_id).unwrap(); -// // let font_properties = Default::default(); -// // let font_id = font_cache -// // .select_font(font_family_id, &font_properties) -// // .unwrap(); -// // let font_size = settings.buffer_font_size(cx); -// // EditorStyle { -// // text: TextStyle { -// // color: settings.theme.editor.text_color, -// // font_family_name, -// // font_family_id, -// // font_id, -// // font_size, -// // font_properties, -// // underline: Default::default(), -// // soft_wrap: false, -// // }, -// // placeholder_text: None, -// // line_height_scalar, -// // theme, -// // theme_id, -// // } -// }; - -// if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) { -// if let Some(highlighted) = style -// .text -// .clone() -// .highlight(highlight_style, font_cache) -// .log_err() -// { -// style.text = highlighted; -// } -// } - -// style -// } - trait SelectionExt { fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range; fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range; diff --git a/crates/editor2/src/items.rs b/crates/editor2/src/items.rs index eca3b99d7807b455ba48ed99ef0287a9ee084abf..e41632c6c9522b1f787b9792b65027adf62f993e 100644 --- a/crates/editor2/src/items.rs +++ b/crates/editor2/src/items.rs @@ -4,13 +4,13 @@ use crate::{ EditorEvent, EditorSettings, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _, }; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, Context as _, Result}; use collections::HashSet; use futures::future::try_join_all; use gpui::{ - div, point, AnyElement, AppContext, AsyncAppContext, Entity, EntityId, EventEmitter, - FocusHandle, Model, ParentElement, Pixels, SharedString, Styled, Subscription, Task, View, - ViewContext, VisualContext, WeakView, WindowContext, + div, point, AnyElement, AppContext, AsyncAppContext, AsyncWindowContext, Context, Entity, + EntityId, EventEmitter, FocusHandle, Model, ParentElement, Pixels, SharedString, Styled, + Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext, }; use language::{ proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, OffsetRangeExt, @@ -71,110 +71,108 @@ impl FollowableItem for Editor { workspace: View, remote_id: ViewId, state: &mut Option, - cx: &mut AppContext, + cx: &mut WindowContext, ) -> Option>>> { - todo!() + let project = workspace.read(cx).project().to_owned(); + let Some(proto::view::Variant::Editor(_)) = state else { + return None; + }; + let Some(proto::view::Variant::Editor(state)) = state.take() else { + unreachable!() + }; + + let client = project.read(cx).client(); + let replica_id = project.read(cx).replica_id(); + let buffer_ids = state + .excerpts + .iter() + .map(|excerpt| excerpt.buffer_id) + .collect::>(); + let buffers = project.update(cx, |project, cx| { + buffer_ids + .iter() + .map(|id| project.open_buffer_by_id(*id, cx)) + .collect::>() + }); + + let pane = pane.downgrade(); + Some(cx.spawn(|mut cx| async move { + let mut buffers = futures::future::try_join_all(buffers).await?; + let editor = pane.update(&mut cx, |pane, cx| { + let mut editors = pane.items_of_type::(); + editors.find(|editor| { + let ids_match = editor.remote_id(&client, cx) == Some(remote_id); + let singleton_buffer_matches = state.singleton + && buffers.first() + == editor.read(cx).buffer.read(cx).as_singleton().as_ref(); + ids_match || singleton_buffer_matches + }) + })?; + + let editor = if let Some(editor) = editor { + editor + } else { + pane.update(&mut cx, |_, cx| { + let multibuffer = cx.build_model(|cx| { + let mut multibuffer; + if state.singleton && buffers.len() == 1 { + multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx) + } else { + multibuffer = MultiBuffer::new(replica_id); + let mut excerpts = state.excerpts.into_iter().peekable(); + while let Some(excerpt) = excerpts.peek() { + let buffer_id = excerpt.buffer_id; + let buffer_excerpts = iter::from_fn(|| { + let excerpt = excerpts.peek()?; + (excerpt.buffer_id == buffer_id) + .then(|| excerpts.next().unwrap()) + }); + let buffer = + buffers.iter().find(|b| b.read(cx).remote_id() == buffer_id); + if let Some(buffer) = buffer { + multibuffer.push_excerpts( + buffer.clone(), + buffer_excerpts.filter_map(deserialize_excerpt_range), + cx, + ); + } + } + }; + + if let Some(title) = &state.title { + multibuffer = multibuffer.with_title(title.clone()) + } + + multibuffer + }); + + cx.build_view(|cx| { + let mut editor = + Editor::for_multibuffer(multibuffer, Some(project.clone()), cx); + editor.remote_id = Some(remote_id); + editor + }) + })? + }; + + update_editor_from_message( + editor.downgrade(), + project, + proto::update_view::Editor { + selections: state.selections, + pending_selection: state.pending_selection, + scroll_top_anchor: state.scroll_top_anchor, + scroll_x: state.scroll_x, + scroll_y: state.scroll_y, + ..Default::default() + }, + &mut cx, + ) + .await?; + + Ok(editor) + })) } - // let project = workspace.read(cx).project().to_owned(); - // let Some(proto::view::Variant::Editor(_)) = state else { - // return None; - // }; - // let Some(proto::view::Variant::Editor(state)) = state.take() else { - // unreachable!() - // }; - - // let client = project.read(cx).client(); - // let replica_id = project.read(cx).replica_id(); - // let buffer_ids = state - // .excerpts - // .iter() - // .map(|excerpt| excerpt.buffer_id) - // .collect::>(); - // let buffers = project.update(cx, |project, cx| { - // buffer_ids - // .iter() - // .map(|id| project.open_buffer_by_id(*id, cx)) - // .collect::>() - // }); - - // let pane = pane.downgrade(); - // Some(cx.spawn(|mut cx| async move { - // let mut buffers = futures::future::try_join_all(buffers).await?; - // let editor = pane.read_with(&cx, |pane, cx| { - // let mut editors = pane.items_of_type::(); - // editors.find(|editor| { - // let ids_match = editor.remote_id(&client, cx) == Some(remote_id); - // let singleton_buffer_matches = state.singleton - // && buffers.first() - // == editor.read(cx).buffer.read(cx).as_singleton().as_ref(); - // ids_match || singleton_buffer_matches - // }) - // })?; - - // let editor = if let Some(editor) = editor { - // editor - // } else { - // pane.update(&mut cx, |_, cx| { - // let multibuffer = cx.add_model(|cx| { - // let mut multibuffer; - // if state.singleton && buffers.len() == 1 { - // multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx) - // } else { - // multibuffer = MultiBuffer::new(replica_id); - // let mut excerpts = state.excerpts.into_iter().peekable(); - // while let Some(excerpt) = excerpts.peek() { - // let buffer_id = excerpt.buffer_id; - // let buffer_excerpts = iter::from_fn(|| { - // let excerpt = excerpts.peek()?; - // (excerpt.buffer_id == buffer_id) - // .then(|| excerpts.next().unwrap()) - // }); - // let buffer = - // buffers.iter().find(|b| b.read(cx).remote_id() == buffer_id); - // if let Some(buffer) = buffer { - // multibuffer.push_excerpts( - // buffer.clone(), - // buffer_excerpts.filter_map(deserialize_excerpt_range), - // cx, - // ); - // } - // } - // }; - - // if let Some(title) = &state.title { - // multibuffer = multibuffer.with_title(title.clone()) - // } - - // multibuffer - // }); - - // cx.add_view(|cx| { - // let mut editor = - // Editor::for_multibuffer(multibuffer, Some(project.clone()), cx); - // editor.remote_id = Some(remote_id); - // editor - // }) - // })? - // }; - - // update_editor_from_message( - // editor.downgrade(), - // project, - // proto::update_view::Editor { - // selections: state.selections, - // pending_selection: state.pending_selection, - // scroll_top_anchor: state.scroll_top_anchor, - // scroll_x: state.scroll_x, - // scroll_y: state.scroll_y, - // ..Default::default() - // }, - // &mut cx, - // ) - // .await?; - - // Ok(editor) - // })) - // } fn set_leader_peer_id(&mut self, leader_peer_id: Option, cx: &mut ViewContext) { self.leader_peer_id = leader_peer_id; @@ -195,7 +193,7 @@ impl FollowableItem for Editor { cx.notify(); } - fn to_state_proto(&self, cx: &AppContext) -> Option { + fn to_state_proto(&self, cx: &WindowContext) -> Option { let buffer = self.buffer.read(cx); let scroll_anchor = self.scroll_manager.anchor(); let excerpts = buffer @@ -242,7 +240,7 @@ impl FollowableItem for Editor { &self, event: &Self::FollowableEvent, update: &mut Option, - cx: &AppContext, + cx: &WindowContext, ) -> bool { let update = update.get_or_insert_with(|| proto::update_view::Variant::Editor(Default::default())); @@ -315,7 +313,7 @@ impl FollowableItem for Editor { }) } - fn is_project_item(&self, _cx: &AppContext) -> bool { + fn is_project_item(&self, _cx: &WindowContext) -> bool { true } } @@ -324,132 +322,129 @@ async fn update_editor_from_message( this: WeakView, project: Model, message: proto::update_view::Editor, - cx: &mut AsyncAppContext, + cx: &mut AsyncWindowContext, ) -> Result<()> { - todo!() -} -// Previous implementation of the above -// // Open all of the buffers of which excerpts were added to the editor. -// let inserted_excerpt_buffer_ids = message -// .inserted_excerpts -// .iter() -// .filter_map(|insertion| Some(insertion.excerpt.as_ref()?.buffer_id)) -// .collect::>(); -// let inserted_excerpt_buffers = project.update(cx, |project, cx| { -// inserted_excerpt_buffer_ids -// .into_iter() -// .map(|id| project.open_buffer_by_id(id, cx)) -// .collect::>() -// })?; -// let _inserted_excerpt_buffers = try_join_all(inserted_excerpt_buffers).await?; - -// // Update the editor's excerpts. -// this.update(cx, |editor, cx| { -// editor.buffer.update(cx, |multibuffer, cx| { -// let mut removed_excerpt_ids = message -// .deleted_excerpts -// .into_iter() -// .map(ExcerptId::from_proto) -// .collect::>(); -// removed_excerpt_ids.sort_by({ -// let multibuffer = multibuffer.read(cx); -// move |a, b| a.cmp(&b, &multibuffer) -// }); - -// let mut insertions = message.inserted_excerpts.into_iter().peekable(); -// while let Some(insertion) = insertions.next() { -// let Some(excerpt) = insertion.excerpt else { -// continue; -// }; -// let Some(previous_excerpt_id) = insertion.previous_excerpt_id else { -// continue; -// }; -// let buffer_id = excerpt.buffer_id; -// let Some(buffer) = project.read(cx).buffer_for_id(buffer_id) else { -// continue; -// }; - -// let adjacent_excerpts = iter::from_fn(|| { -// let insertion = insertions.peek()?; -// if insertion.previous_excerpt_id.is_none() -// && insertion.excerpt.as_ref()?.buffer_id == buffer_id -// { -// insertions.next()?.excerpt -// } else { -// None -// } -// }); - -// multibuffer.insert_excerpts_with_ids_after( -// ExcerptId::from_proto(previous_excerpt_id), -// buffer, -// [excerpt] -// .into_iter() -// .chain(adjacent_excerpts) -// .filter_map(|excerpt| { -// Some(( -// ExcerptId::from_proto(excerpt.id), -// deserialize_excerpt_range(excerpt)?, -// )) -// }), -// cx, -// ); -// } + // Open all of the buffers of which excerpts were added to the editor. + let inserted_excerpt_buffer_ids = message + .inserted_excerpts + .iter() + .filter_map(|insertion| Some(insertion.excerpt.as_ref()?.buffer_id)) + .collect::>(); + let inserted_excerpt_buffers = project.update(cx, |project, cx| { + inserted_excerpt_buffer_ids + .into_iter() + .map(|id| project.open_buffer_by_id(id, cx)) + .collect::>() + })?; + let _inserted_excerpt_buffers = try_join_all(inserted_excerpt_buffers).await?; + + // Update the editor's excerpts. + this.update(cx, |editor, cx| { + editor.buffer.update(cx, |multibuffer, cx| { + let mut removed_excerpt_ids = message + .deleted_excerpts + .into_iter() + .map(ExcerptId::from_proto) + .collect::>(); + removed_excerpt_ids.sort_by({ + let multibuffer = multibuffer.read(cx); + move |a, b| a.cmp(&b, &multibuffer) + }); -// multibuffer.remove_excerpts(removed_excerpt_ids, cx); -// }); -// })?; - -// // Deserialize the editor state. -// let (selections, pending_selection, scroll_top_anchor) = this.update(cx, |editor, cx| { -// let buffer = editor.buffer.read(cx).read(cx); -// let selections = message -// .selections -// .into_iter() -// .filter_map(|selection| deserialize_selection(&buffer, selection)) -// .collect::>(); -// let pending_selection = message -// .pending_selection -// .and_then(|selection| deserialize_selection(&buffer, selection)); -// let scroll_top_anchor = message -// .scroll_top_anchor -// .and_then(|anchor| deserialize_anchor(&buffer, anchor)); -// anyhow::Ok((selections, pending_selection, scroll_top_anchor)) -// })??; - -// // Wait until the buffer has received all of the operations referenced by -// // the editor's new state. -// this.update(cx, |editor, cx| { -// editor.buffer.update(cx, |buffer, cx| { -// buffer.wait_for_anchors( -// selections -// .iter() -// .chain(pending_selection.as_ref()) -// .flat_map(|selection| [selection.start, selection.end]) -// .chain(scroll_top_anchor), -// cx, -// ) -// }) -// })? -// .await?; - -// // Update the editor's state. -// this.update(cx, |editor, cx| { -// if !selections.is_empty() || pending_selection.is_some() { -// editor.set_selections_from_remote(selections, pending_selection, cx); -// editor.request_autoscroll_remotely(Autoscroll::newest(), cx); -// } else if let Some(scroll_top_anchor) = scroll_top_anchor { -// editor.set_scroll_anchor_remote( -// ScrollAnchor { -// anchor: scroll_top_anchor, -// offset: point(message.scroll_x, message.scroll_y), -// }, -// cx, -// ); -// } -// })?; -// Ok(()) -// } + let mut insertions = message.inserted_excerpts.into_iter().peekable(); + while let Some(insertion) = insertions.next() { + let Some(excerpt) = insertion.excerpt else { + continue; + }; + let Some(previous_excerpt_id) = insertion.previous_excerpt_id else { + continue; + }; + let buffer_id = excerpt.buffer_id; + let Some(buffer) = project.read(cx).buffer_for_id(buffer_id) else { + continue; + }; + + let adjacent_excerpts = iter::from_fn(|| { + let insertion = insertions.peek()?; + if insertion.previous_excerpt_id.is_none() + && insertion.excerpt.as_ref()?.buffer_id == buffer_id + { + insertions.next()?.excerpt + } else { + None + } + }); + + multibuffer.insert_excerpts_with_ids_after( + ExcerptId::from_proto(previous_excerpt_id), + buffer, + [excerpt] + .into_iter() + .chain(adjacent_excerpts) + .filter_map(|excerpt| { + Some(( + ExcerptId::from_proto(excerpt.id), + deserialize_excerpt_range(excerpt)?, + )) + }), + cx, + ); + } + + multibuffer.remove_excerpts(removed_excerpt_ids, cx); + }); + })?; + + // Deserialize the editor state. + let (selections, pending_selection, scroll_top_anchor) = this.update(cx, |editor, cx| { + let buffer = editor.buffer.read(cx).read(cx); + let selections = message + .selections + .into_iter() + .filter_map(|selection| deserialize_selection(&buffer, selection)) + .collect::>(); + let pending_selection = message + .pending_selection + .and_then(|selection| deserialize_selection(&buffer, selection)); + let scroll_top_anchor = message + .scroll_top_anchor + .and_then(|anchor| deserialize_anchor(&buffer, anchor)); + anyhow::Ok((selections, pending_selection, scroll_top_anchor)) + })??; + + // Wait until the buffer has received all of the operations referenced by + // the editor's new state. + this.update(cx, |editor, cx| { + editor.buffer.update(cx, |buffer, cx| { + buffer.wait_for_anchors( + selections + .iter() + .chain(pending_selection.as_ref()) + .flat_map(|selection| [selection.start, selection.end]) + .chain(scroll_top_anchor), + cx, + ) + }) + })? + .await?; + + // Update the editor's state. + this.update(cx, |editor, cx| { + if !selections.is_empty() || pending_selection.is_some() { + editor.set_selections_from_remote(selections, pending_selection, cx); + editor.request_autoscroll_remotely(Autoscroll::newest(), cx); + } else if let Some(scroll_top_anchor) = scroll_top_anchor { + editor.set_scroll_anchor_remote( + ScrollAnchor { + anchor: scroll_top_anchor, + offset: point(message.scroll_x, message.scroll_y), + }, + cx, + ); + } + })?; + Ok(()) +} fn serialize_excerpt( buffer_id: u64, @@ -529,39 +524,38 @@ fn deserialize_anchor(buffer: &MultiBufferSnapshot, anchor: proto::EditorAnchor) impl Item for Editor { fn navigate(&mut self, data: Box, cx: &mut ViewContext) -> bool { - todo!(); - // if let Ok(data) = data.downcast::() { - // let newest_selection = self.selections.newest::(cx); - // let buffer = self.buffer.read(cx).read(cx); - // let offset = if buffer.can_resolve(&data.cursor_anchor) { - // data.cursor_anchor.to_point(&buffer) - // } else { - // buffer.clip_point(data.cursor_position, Bias::Left) - // }; - - // let mut scroll_anchor = data.scroll_anchor; - // if !buffer.can_resolve(&scroll_anchor.anchor) { - // scroll_anchor.anchor = buffer.anchor_before( - // buffer.clip_point(Point::new(data.scroll_top_row, 0), Bias::Left), - // ); - // } - - // drop(buffer); - - // if newest_selection.head() == offset { - // false - // } else { - // let nav_history = self.nav_history.take(); - // self.set_scroll_anchor(scroll_anchor, cx); - // self.change_selections(Some(Autoscroll::fit()), cx, |s| { - // s.select_ranges([offset..offset]) - // }); - // self.nav_history = nav_history; - // true - // } - // } else { - // false - // } + if let Ok(data) = data.downcast::() { + let newest_selection = self.selections.newest::(cx); + let buffer = self.buffer.read(cx).read(cx); + let offset = if buffer.can_resolve(&data.cursor_anchor) { + data.cursor_anchor.to_point(&buffer) + } else { + buffer.clip_point(data.cursor_position, Bias::Left) + }; + + let mut scroll_anchor = data.scroll_anchor; + if !buffer.can_resolve(&scroll_anchor.anchor) { + scroll_anchor.anchor = buffer.anchor_before( + buffer.clip_point(Point::new(data.scroll_top_row, 0), Bias::Left), + ); + } + + drop(buffer); + + if newest_selection.head() == offset { + false + } else { + let nav_history = self.nav_history.take(); + self.set_scroll_anchor(scroll_anchor, cx); + self.change_selections(Some(Autoscroll::fit()), cx, |s| { + s.select_ranges([offset..offset]) + }); + self.nav_history = nav_history; + true + } + } else { + false + } } fn tab_tooltip_text(&self, cx: &AppContext) -> Option { @@ -765,35 +759,34 @@ impl Item for Editor { } fn breadcrumbs(&self, variant: &Theme, 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 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 cursor = self.selections.newest_anchor().head(); + let multibuffer = &self.buffer().read(cx); + let (buffer_id, symbols) = + multibuffer.symbols_containing(cursor, Some(&variant.syntax()), cx)?; + let buffer = multibuffer.buffer(buffer_id)?; + + let buffer = buffer.read(cx); + let filename = buffer + .snapshot() + .resolve_file_path( + cx, + self.project + .as_ref() + .map(|project| project.read(cx).visible_worktrees(cx).count() > 1) + .unwrap_or_default(), + ) + .map(|path| path.to_string_lossy().to_string()) + .unwrap_or_else(|| "untitled".to_string()); + + let mut breadcrumbs = vec![BreadcrumbText { + text: filename, + highlights: None, + }]; + breadcrumbs.extend(symbols.into_iter().map(|symbol| BreadcrumbText { + text: symbol.text, + highlights: Some(symbol.highlight_ranges), + })); + Some(breadcrumbs) } fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext) { diff --git a/crates/workspace2/src/item.rs b/crates/workspace2/src/item.rs index 2cd2ac0bd6563532dfd502c7a6646f166c7cfd5e..258f8d7211014fcd862ae0fede81aa8d15cc94b0 100644 --- a/crates/workspace2/src/item.rs +++ b/crates/workspace2/src/item.rs @@ -662,19 +662,19 @@ pub trait FollowableEvents { pub trait FollowableItem: Item { type FollowableEvent: FollowableEvents; fn remote_id(&self) -> Option; - fn to_state_proto(&self, cx: &AppContext) -> Option; + fn to_state_proto(&self, cx: &WindowContext) -> Option; fn from_state_proto( pane: View, project: View, id: ViewId, state: &mut Option, - cx: &mut AppContext, + cx: &mut WindowContext, ) -> Option>>>; fn add_event_to_update_proto( &self, event: &Self::FollowableEvent, update: &mut Option, - cx: &AppContext, + cx: &WindowContext, ) -> bool; fn apply_update_proto( &mut self, @@ -682,20 +682,20 @@ pub trait FollowableItem: Item { message: proto::update_view::Variant, cx: &mut ViewContext, ) -> Task>; - fn is_project_item(&self, cx: &AppContext) -> bool; + fn is_project_item(&self, cx: &WindowContext) -> bool; fn set_leader_peer_id(&mut self, leader_peer_id: Option, cx: &mut ViewContext); } pub trait FollowableItemHandle: ItemHandle { - fn remote_id(&self, client: &Arc, cx: &AppContext) -> Option; + fn remote_id(&self, client: &Arc, cx: &WindowContext) -> Option; fn set_leader_peer_id(&self, leader_peer_id: Option, cx: &mut WindowContext); - fn to_state_proto(&self, cx: &AppContext) -> Option; + fn to_state_proto(&self, cx: &WindowContext) -> Option; fn add_event_to_update_proto( &self, event: &dyn Any, update: &mut Option, - cx: &AppContext, + cx: &WindowContext, ) -> bool; fn to_follow_event(&self, event: &dyn Any) -> Option; fn apply_update_proto( @@ -704,11 +704,11 @@ pub trait FollowableItemHandle: ItemHandle { message: proto::update_view::Variant, cx: &mut WindowContext, ) -> Task>; - fn is_project_item(&self, cx: &AppContext) -> bool; + fn is_project_item(&self, cx: &WindowContext) -> bool; } impl FollowableItemHandle for View { - fn remote_id(&self, client: &Arc, cx: &AppContext) -> Option { + fn remote_id(&self, client: &Arc, cx: &WindowContext) -> Option { self.read(cx).remote_id().or_else(|| { client.peer_id().map(|creator| ViewId { creator, @@ -721,7 +721,7 @@ impl FollowableItemHandle for View { self.update(cx, |this, cx| this.set_leader_peer_id(leader_peer_id, cx)) } - fn to_state_proto(&self, cx: &AppContext) -> Option { + fn to_state_proto(&self, cx: &WindowContext) -> Option { self.read(cx).to_state_proto(cx) } @@ -729,7 +729,7 @@ impl FollowableItemHandle for View { &self, event: &dyn Any, update: &mut Option, - cx: &AppContext, + cx: &WindowContext, ) -> bool { if let Some(event) = event.downcast_ref() { self.read(cx).add_event_to_update_proto(event, update, cx) @@ -754,7 +754,7 @@ impl FollowableItemHandle for View { self.update(cx, |this, cx| this.apply_update_proto(project, message, cx)) } - fn is_project_item(&self, cx: &AppContext) -> bool { + fn is_project_item(&self, cx: &WindowContext) -> bool { self.read(cx).is_project_item(cx) } } diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index cbd3e4309cd9a3e3aa38a1dd6645cc3b22a05505..df61d058c7c6aa2c29d66ea28a80642392c73a69 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -247,7 +247,7 @@ type FollowableItemBuilder = fn( View, ViewId, &mut Option, - &mut AppContext, + &mut WindowContext, ) -> Option>>>; type FollowableItemBuilders = HashMap< TypeId, From f0bc4a04bd087d5e31452de13d5efb6d78070cb2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 1 Dec 2023 13:45:14 +0100 Subject: [PATCH 4/5] Uncomment git gutter painting --- crates/editor2/src/element.rs | 159 +++++++++++++++++----------------- crates/gpui2/src/geometry.rs | 14 +++ 2 files changed, 93 insertions(+), 80 deletions(-) diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 4a27f457f6b5b97afa0965b057d9558ff21cdec9..3559665e8c88621d7a58a2d09b6733a36023f18a 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -19,6 +19,7 @@ use crate::{ }; use anyhow::Result; use collections::{BTreeMap, HashMap}; +use git::diff::DiffHunkStatus; use gpui::{ div, point, px, relative, size, transparent_black, Action, AnyElement, AsyncWindowContext, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners, DispatchPhase, Edges, Element, @@ -723,87 +724,85 @@ impl EditorElement { } fn paint_diff_hunks(bounds: Bounds, layout: &LayoutState, cx: &mut WindowContext) { - // todo!() - // 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 + point(-width, start_y); - // let highlight_size = point(width * 2., end_y - start_y); - // let highlight_bounds = Bounds::::new(highlight_origin, highlight_size); - - // cx.paint_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; - // } + let line_height = layout.position_map.line_height; - // 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 + point(-width, start_y); - // let highlight_size = point(width * 2., end_y - start_y); - // let highlight_bounds = Bounds::::new(highlight_origin, highlight_size); - - // cx.paint_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 + point(-width, start_y); - // let highlight_size = point(width * 2., end_y - start_y); - // let highlight_bounds = Bounds::::new(highlight_origin, highlight_size); - - // cx.paint_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(), - // }); - // } + let scroll_position = layout.position_map.snapshot.scroll_position(); + let scroll_top = scroll_position.y * line_height; + + for hunk in &layout.display_hunks { + let (display_row_range, status) = match hunk { + //TODO: This rendering is entirely a horrible hack + &DisplayDiffHunk::Folded { display_row: row } => { + let start_y = row as f32 * line_height - scroll_top; + let end_y = start_y + line_height; + + let width = 0.275 * line_height; + let highlight_origin = bounds.origin + point(-width, start_y); + let highlight_size = size(width * 2., end_y - start_y); + let highlight_bounds = Bounds::new(highlight_origin, highlight_size); + cx.paint_quad( + highlight_bounds, + Corners::all(1. * line_height), + gpui::yellow(), // todo!("use the right color") + Edges::default(), + transparent_black(), + ); + + continue; + } + + DisplayDiffHunk::Unfolded { + display_row_range, + status, + } => (display_row_range, status), + }; + + let color = match status { + DiffHunkStatus::Added => gpui::green(), // todo!("use the appropriate color") + DiffHunkStatus::Modified => gpui::yellow(), // todo!("use the appropriate color") + + //TODO: This rendering is entirely a horrible hack + DiffHunkStatus::Removed => { + let row = display_row_range.start; + + let offset = line_height / 2.; + let start_y = row as f32 * line_height - offset - scroll_top; + let end_y = start_y + line_height; + + let width = 0.275 * line_height; + let highlight_origin = bounds.origin + point(-width, start_y); + let highlight_size = size(width * 2., end_y - start_y); + let highlight_bounds = Bounds::new(highlight_origin, highlight_size); + cx.paint_quad( + highlight_bounds, + Corners::all(1. * line_height), + gpui::red(), // todo!("use the right color") + Edges::default(), + transparent_black(), + ); + + continue; + } + }; + + let start_row = display_row_range.start; + let end_row = display_row_range.end; + + let start_y = start_row as f32 * line_height - scroll_top; + let end_y = end_row as f32 * line_height - scroll_top; + + let width = 0.275 * line_height; + let highlight_origin = bounds.origin + point(-width, start_y); + let highlight_size = size(width * 2., end_y - start_y); + let highlight_bounds = Bounds::new(highlight_origin, highlight_size); + cx.paint_quad( + highlight_bounds, + Corners::all(0.05 * line_height), + color, // todo!("use the right color") + Edges::default(), + transparent_black(), + ); + } } fn paint_text( diff --git a/crates/gpui2/src/geometry.rs b/crates/gpui2/src/geometry.rs index d32c2e849be6151e6a1909405ee96c9ff838cca3..20afd2d288b29bc8953c41c79389b9331a80244b 100644 --- a/crates/gpui2/src/geometry.rs +++ b/crates/gpui2/src/geometry.rs @@ -655,6 +655,20 @@ pub struct Corners { pub bottom_left: T, } +impl Corners +where + T: Clone + Default + Debug, +{ + pub fn all(value: T) -> Self { + Self { + top_left: value.clone(), + top_right: value.clone(), + bottom_right: value.clone(), + bottom_left: value, + } + } +} + impl Corners { pub fn to_pixels(&self, size: Size, rem_size: Pixels) -> Corners { let max = size.width.max(size.height) / 2.; From f0c205be5d1bbf22086804c6a65adb44468af961 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 1 Dec 2023 14:26:49 +0100 Subject: [PATCH 5/5] Respond to modifiers changed event in editor2 and set cursor --- crates/editor2/src/editor.rs | 100 ---------------------------------- crates/editor2/src/element.rs | 90 ++++++++++++++++++++++++------ 2 files changed, 73 insertions(+), 117 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 57759bcc5493ba8c4cf8740bf4027ddd81e09343..ae763f2ea02e518e1535cec148f05edd15651a45 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -9311,106 +9311,6 @@ impl Render for Editor { } } -// impl View for Editor { -// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { -// 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_with_size(style.text.font_id, style.text.font_size, cx) -// }); - -// if font_changed { -// cx.defer(move |editor, cx: &mut ViewContext| { -// hide_hover(editor, cx); -// hide_link_definition(editor, cx); -// }); -// } - -// Stack::new() -// .with_child(EditorElement::new(style.clone())) -// .with_child(ChildView::new(&self.mouse_context_menu, cx)) -// .into_any() -// } - -// fn ui_name() -> &'static str { -// "Editor" -// } - -// fn focus_in(&mut self, focused: AnyView, cx: &mut ViewContext) { -// if cx.is_self_focused() { -// let focused_event = EditorFocused(cx.handle()); -// cx.emit(Event::Focused); -// cx.emit_global(focused_event); -// } -// if let Some(rename) = self.pending_rename.as_ref() { -// cx.focus(&rename.editor); -// } else if cx.is_self_focused() || !focused.is::() { -// if !self.focused { -// self.blink_manager.update(cx, BlinkManager::enable); -// } -// self.focused = true; -// self.buffer.update(cx, |buffer, cx| { -// buffer.finalize_last_transaction(cx); -// if self.leader_peer_id.is_none() { -// buffer.set_active_selections( -// &self.selections.disjoint_anchors(), -// self.selections.line_mode, -// self.cursor_shape, -// cx, -// ); -// } -// }); -// } -// } - -// fn focus_out(&mut self, _: AnyView, cx: &mut ViewContext) { -// let blurred_event = EditorBlurred(cx.handle()); -// cx.emit_global(blurred_event); -// self.focused = false; -// self.blink_manager.update(cx, BlinkManager::disable); -// self.buffer -// .update(cx, |buffer, cx| buffer.remove_active_selections(cx)); -// self.hide_context_menu(cx); -// hide_hover(self, cx); -// cx.emit(Event::Blurred); -// cx.notify(); -// } - -// fn modifiers_changed( -// &mut self, -// event: &gpui::platform::ModifiersChangedEvent, -// cx: &mut ViewContext, -// ) -> bool { -// let pending_selection = self.has_pending_selection(); - -// if let Some(point) = &self.link_go_to_definition_state.last_trigger_point { -// if event.cmd && !pending_selection { -// let point = point.clone(); -// let snapshot = self.snapshot(cx); -// let kind = point.definition_kind(event.shift); - -// show_link_definition(kind, self, point, snapshot, cx); -// return false; -// } -// } - -// { -// if self.link_go_to_definition_state.symbol_range.is_some() -// || !self.link_go_to_definition_state.definitions.is_empty() -// { -// self.link_go_to_definition_state.symbol_range.take(); -// self.link_go_to_definition_state.definitions.clear(); -// cx.notify(); -// } - -// self.link_go_to_definition_state.task = None; - -// self.clear_highlights::(cx); -// } - -// false -// } - impl InputHandler for Editor { fn text_for_range( &mut self, diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 3559665e8c88621d7a58a2d09b6733a36023f18a..76bdebac44337edf51882ad7032fef0a31ebc7ca 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -9,8 +9,9 @@ use crate::{ self, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT, }, link_go_to_definition::{ - go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link, - update_inlay_link_and_hover_points, GoToDefinitionTrigger, + go_to_fetched_definition, go_to_fetched_type_definition, show_link_definition, + update_go_to_definition_link, update_inlay_link_and_hover_points, GoToDefinitionTrigger, + LinkGoToDefinitionState, }, scroll::scroll_amount::ScrollAmount, CursorShape, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, @@ -22,12 +23,12 @@ use collections::{BTreeMap, HashMap}; use git::diff::DiffHunkStatus; use gpui::{ div, point, px, relative, size, transparent_black, Action, AnyElement, AsyncWindowContext, - AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners, DispatchPhase, Edges, Element, - ElementId, ElementInputHandler, Entity, EntityId, Hsla, InteractiveBounds, InteractiveElement, - IntoElement, LineLayout, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, - ParentElement, Pixels, RenderOnce, ScrollWheelEvent, ShapedLine, SharedString, Size, - StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun, TextStyle, View, - ViewContext, WeakView, WindowContext, WrappedLine, + AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners, CursorStyle, DispatchPhase, Edges, + Element, ElementId, ElementInputHandler, Entity, EntityId, Hsla, InteractiveBounds, + InteractiveElement, IntoElement, LineLayout, ModifiersChangedEvent, MouseButton, + MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, RenderOnce, + ScrollWheelEvent, ShapedLine, SharedString, Size, StackingOrder, StatefulInteractiveElement, + Style, Styled, TextRun, TextStyle, View, ViewContext, WeakView, WindowContext, WrappedLine, }; use itertools::Itertools; use language::language_settings::ShowWhitespaceSetting; @@ -311,6 +312,56 @@ impl EditorElement { register_action(view, cx, Editor::context_menu_last); } + fn register_key_listeners(&self, cx: &mut WindowContext) { + cx.on_key_event({ + let editor = self.editor.clone(); + move |event: &ModifiersChangedEvent, phase, cx| { + if phase != DispatchPhase::Bubble { + return; + } + + if editor.update(cx, |editor, cx| Self::modifiers_changed(editor, event, cx)) { + cx.stop_propagation(); + } + } + }); + } + + fn modifiers_changed( + editor: &mut Editor, + event: &ModifiersChangedEvent, + cx: &mut ViewContext, + ) -> bool { + let pending_selection = editor.has_pending_selection(); + + if let Some(point) = &editor.link_go_to_definition_state.last_trigger_point { + if event.command && !pending_selection { + let point = point.clone(); + let snapshot = editor.snapshot(cx); + let kind = point.definition_kind(event.shift); + + show_link_definition(kind, editor, point, snapshot, cx); + return false; + } + } + + { + if editor.link_go_to_definition_state.symbol_range.is_some() + || !editor.link_go_to_definition_state.definitions.is_empty() + { + editor.link_go_to_definition_state.symbol_range.take(); + editor.link_go_to_definition_state.definitions.clear(); + cx.notify(); + } + + editor.link_go_to_definition_state.task = None; + + editor.clear_highlights::(cx); + } + + false + } + fn mouse_down( editor: &mut Editor, event: &MouseDownEvent, @@ -828,15 +879,19 @@ impl EditorElement { bounds: text_bounds, }), |cx| { - // todo!("cursor region") - // cx.scene().push_cursor_region(CursorRegion { - // bounds, - // style: if !editor.link_go_to_definition_state.definitions.is_empty { - // CursorStyle::PointingHand - // } else { - // CursorStyle::IBeam - // }, - // }); + if text_bounds.contains_point(&cx.mouse_position()) { + if self + .editor + .read(cx) + .link_go_to_definition_state + .definitions + .is_empty() + { + cx.set_cursor_style(CursorStyle::IBeam); + } else { + cx.set_cursor_style(CursorStyle::PointingHand); + } + } let fold_corner_radius = 0.15 * layout.position_map.line_height; cx.with_element_id(Some("folds"), |cx| { @@ -2656,6 +2711,7 @@ impl Element for EditorElement { let dispatch_context = self.editor.read(cx).dispatch_context(cx); cx.with_key_dispatch(dispatch_context, Some(focus_handle.clone()), |_, cx| { self.register_actions(cx); + self.register_key_listeners(cx); // We call with_z_index to establish a new stacking context. cx.with_z_index(0, |cx| {