Re-enable most of the functionalities in editor2

Antonio Scandurra created

Change summary

crates/copilot2/src/copilot2.rs             |  16 
crates/editor2/src/editor.rs                | 821 +++++++++++-----------
crates/editor2/src/element.rs               | 152 ++++
crates/editor2/src/scroll.rs                | 109 +-
crates/editor2/src/scroll/actions.rs        | 203 ++---
crates/editor2/src/selections_collection.rs |  66 
crates/gpui2/src/window.rs                  |   4 
crates/gpui2_macros/src/action.rs           |   5 
crates/gpui2_macros/src/register_action.rs  |   1 
9 files changed, 745 insertions(+), 632 deletions(-)

Detailed changes

crates/copilot2/src/copilot2.rs 🔗

@@ -7,8 +7,8 @@ use async_tar::Archive;
 use collections::{HashMap, HashSet};
 use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
 use gpui::{
-    AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model, ModelContext,
-    Task, WeakModel,
+    actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model,
+    ModelContext, Task, WeakModel,
 };
 use language::{
     language_settings::{all_language_settings, language_settings},
@@ -34,19 +34,11 @@ use util::{
 
 // todo!()
 // const COPILOT_AUTH_NAMESPACE: &'static str = "copilot_auth";
-// actions!(copilot_auth, [SignIn, SignOut]);
+actions!(SignIn, SignOut);
 
 // todo!()
 // const COPILOT_NAMESPACE: &'static str = "copilot";
-// actions!(
-//     copilot,
-//     [Suggest, NextSuggestion, PreviousSuggestion, Reinstall]
-// );
-//
-pub struct Suggest;
-pub struct NextSuggestion;
-pub struct PreviousSuggestion;
-pub struct Reinstall;
+actions!(Suggest, NextSuggestion, PreviousSuggestion, Reinstall);
 
 pub fn init(
     new_server_id: LanguageServerId,

crates/editor2/src/editor.rs 🔗

@@ -20,12 +20,14 @@ pub mod selections_collection;
 mod editor_tests;
 #[cfg(any(test, feature = "test-support"))]
 pub mod test;
+use ::git::diff::DiffHunk;
 use aho_corasick::AhoCorasick;
 use anyhow::{Context as _, Result};
 use blink_manager::BlinkManager;
 use client::{ClickhouseEvent, Client, Collaborator, ParticipantIndex, TelemetrySettings};
 use clock::ReplicaId;
-use collections::{BTreeMap, HashMap, HashSet, VecDeque};
+use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
+use convert_case::{Case, Casing};
 use copilot::Copilot;
 pub use display_map::DisplayPoint;
 use display_map::*;
@@ -35,11 +37,13 @@ pub use element::{
 };
 use futures::FutureExt;
 use fuzzy::{StringMatch, StringMatchCandidate};
+use git::diff_hunk_to_display;
 use gpui::{
-    actions, div, px, relative, AnyElement, AppContext, BackgroundExecutor, Context,
-    DispatchContext, Div, Element, Entity, EventEmitter, FocusHandle, FontStyle, FontWeight,
-    HighlightStyle, Hsla, InputHandler, Model, Pixels, PlatformInputHandler, Render, Styled,
-    Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakView, WindowContext,
+    action, actions, div, px, relative, AnyElement, AppContext, BackgroundExecutor, ClipboardItem,
+    Context, DispatchContext, Div, Element, Entity, EventEmitter, FocusHandle, FontStyle,
+    FontWeight, HighlightStyle, Hsla, InputHandler, Model, Pixels, PlatformInputHandler, Render,
+    Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakView,
+    WindowContext,
 };
 use highlight_matching_bracket::refresh_matching_bracket_highlights;
 use hover_popover::{hide_hover, HoverState};
@@ -50,8 +54,8 @@ pub use language::{char_kind, CharKind};
 use language::{
     language_settings::{self, all_language_settings, InlayHintSettings},
     point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, Completion, CursorShape,
-    Diagnostic, Language, LanguageRegistry, LanguageServerName, OffsetRangeExt, Point, Selection,
-    SelectionGoal, TransactionId,
+    Diagnostic, IndentKind, IndentSize, Language, LanguageRegistry, LanguageServerName,
+    OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
 };
 use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
 use lsp::{DiagnosticSeverity, Documentation, LanguageServerId};
@@ -64,6 +68,7 @@ pub use multi_buffer::{
 use ordered_float::OrderedFloat;
 use parking_lot::RwLock;
 use project::{FormatTrigger, Location, Project};
+use rand::prelude::*;
 use rpc::proto::*;
 use scroll::{
     autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
@@ -72,11 +77,14 @@ use selections_collection::{resolve_multiple, MutableSelectionsCollection, Selec
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsStore};
 use smallvec::SmallVec;
+use snippet::Snippet;
 use std::{
     any::TypeId,
     borrow::Cow,
     cmp::{self, Ordering, Reverse},
-    ops::{ControlFlow, Deref, DerefMut, Range},
+    mem,
+    num::NonZeroU32,
+    ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
     path::Path,
     sync::Arc,
     time::{Duration, Instant},
@@ -165,83 +173,83 @@ pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
 //     //     .with_soft_wrap(true)
 // }
 
-#[derive(Clone, Deserialize, PartialEq, Default)]
+#[action]
 pub struct SelectNext {
     #[serde(default)]
     pub replace_newest: bool,
 }
 
-#[derive(Clone, Deserialize, PartialEq, Default)]
+#[action]
 pub struct SelectPrevious {
     #[serde(default)]
     pub replace_newest: bool,
 }
 
-#[derive(Clone, Deserialize, PartialEq, Default)]
+#[action]
 pub struct SelectAllMatches {
     #[serde(default)]
     pub replace_newest: bool,
 }
 
-#[derive(Clone, Deserialize, PartialEq)]
+#[action]
 pub struct SelectToBeginningOfLine {
     #[serde(default)]
     stop_at_soft_wraps: bool,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[action]
 pub struct MovePageUp {
     #[serde(default)]
     center_cursor: bool,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[action]
 pub struct MovePageDown {
     #[serde(default)]
     center_cursor: bool,
 }
 
-#[derive(Clone, Deserialize, PartialEq)]
+#[action]
 pub struct SelectToEndOfLine {
     #[serde(default)]
     stop_at_soft_wraps: bool,
 }
 
-#[derive(Clone, Deserialize, PartialEq)]
+#[action]
 pub struct ToggleCodeActions {
     #[serde(default)]
     pub deployed_from_indicator: bool,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[action]
 pub struct ConfirmCompletion {
     #[serde(default)]
     pub item_ix: Option<usize>,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[action]
 pub struct ConfirmCodeAction {
     #[serde(default)]
     pub item_ix: Option<usize>,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[action]
 pub struct ToggleComments {
     #[serde(default)]
     pub advance_downwards: bool,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[action]
 pub struct FoldAt {
     pub buffer_row: u32,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[action]
 pub struct UnfoldAt {
     pub buffer_row: u32,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[action]
 pub struct GutterHover {
     pub hovered: bool,
 }
@@ -262,113 +270,121 @@ impl InlayId {
 }
 
 actions!(
-    Cancel,
+    AddSelectionAbove,
+    AddSelectionBelow,
     Backspace,
+    Cancel,
+    ConfirmRename,
+    ContextMenuFirst,
+    ContextMenuLast,
+    ContextMenuNext,
+    ContextMenuPrev,
+    ConvertToKebabCase,
+    ConvertToLowerCamelCase,
+    ConvertToLowerCase,
+    ConvertToSnakeCase,
+    ConvertToTitleCase,
+    ConvertToUpperCamelCase,
+    ConvertToUpperCase,
+    Copy,
+    CopyHighlightJson,
+    CopyPath,
+    CopyRelativePath,
+    Cut,
+    CutToEndOfLine,
     Delete,
-    Newline,
-    NewlineAbove,
-    NewlineBelow,
-    GoToDiagnostic,
-    GoToPrevDiagnostic,
-    GoToHunk,
-    GoToPrevHunk,
-    Indent,
-    Outdent,
     DeleteLine,
-    DeleteToPreviousWordStart,
-    DeleteToPreviousSubwordStart,
-    DeleteToNextWordEnd,
-    DeleteToNextSubwordEnd,
     DeleteToBeginningOfLine,
     DeleteToEndOfLine,
-    CutToEndOfLine,
+    DeleteToNextSubwordEnd,
+    DeleteToNextWordEnd,
+    DeleteToPreviousSubwordStart,
+    DeleteToPreviousWordStart,
     DuplicateLine,
-    MoveLineUp,
-    MoveLineDown,
+    FindAllReferences,
+    Fold,
+    FoldSelectedRanges,
+    Format,
+    GoToDefinition,
+    GoToDefinitionSplit,
+    GoToDiagnostic,
+    GoToHunk,
+    GoToPrevDiagnostic,
+    GoToPrevHunk,
+    GoToTypeDefinition,
+    GoToTypeDefinitionSplit,
+    HalfPageDown,
+    HalfPageUp,
+    Hover,
+    Indent,
     JoinLines,
-    SortLinesCaseSensitive,
-    SortLinesCaseInsensitive,
-    ReverseLines,
-    ShuffleLines,
-    ConvertToUpperCase,
-    ConvertToLowerCase,
-    ConvertToTitleCase,
-    ConvertToSnakeCase,
-    ConvertToKebabCase,
-    ConvertToUpperCamelCase,
-    ConvertToLowerCamelCase,
-    Transpose,
-    Cut,
-    Copy,
-    Paste,
-    Undo,
-    Redo,
-    MoveUp,
-    PageUp,
+    LineDown,
+    LineUp,
     MoveDown,
-    PageDown,
     MoveLeft,
+    MoveLineDown,
+    MoveLineUp,
     MoveRight,
-    MoveToPreviousWordStart,
-    MoveToPreviousSubwordStart,
-    MoveToNextWordEnd,
-    MoveToNextSubwordEnd,
+    MoveToBeginning,
     MoveToBeginningOfLine,
+    MoveToEnclosingBracket,
+    MoveToEnd,
     MoveToEndOfLine,
-    MoveToStartOfParagraph,
     MoveToEndOfParagraph,
-    MoveToBeginning,
-    MoveToEnd,
-    SelectUp,
+    MoveToNextSubwordEnd,
+    MoveToNextWordEnd,
+    MoveToPreviousSubwordStart,
+    MoveToPreviousWordStart,
+    MoveToStartOfParagraph,
+    MoveUp,
+    Newline,
+    NewlineAbove,
+    NewlineBelow,
+    NextScreen,
+    OpenExcerpts,
+    Outdent,
+    PageDown,
+    PageUp,
+    Paste,
+    Redo,
+    RedoSelection,
+    Rename,
+    RestartLanguageServer,
+    RevealInFinder,
+    ReverseLines,
+    ScrollCursorBottom,
+    ScrollCursorCenter,
+    ScrollCursorTop,
+    SelectAll,
     SelectDown,
+    SelectLargerSyntaxNode,
     SelectLeft,
+    SelectLine,
     SelectRight,
-    SelectToPreviousWordStart,
-    SelectToPreviousSubwordStart,
-    SelectToNextWordEnd,
-    SelectToNextSubwordEnd,
-    SelectToStartOfParagraph,
-    SelectToEndOfParagraph,
+    SelectSmallerSyntaxNode,
     SelectToBeginning,
     SelectToEnd,
-    SelectAll,
-    SelectLine,
+    SelectToEndOfParagraph,
+    SelectToNextSubwordEnd,
+    SelectToNextWordEnd,
+    SelectToPreviousSubwordStart,
+    SelectToPreviousWordStart,
+    SelectToStartOfParagraph,
+    SelectUp,
+    ShowCharacterPalette,
+    ShowCompletions,
+    ShuffleLines,
+    SortLinesCaseInsensitive,
+    SortLinesCaseSensitive,
     SplitSelectionIntoLines,
-    AddSelectionAbove,
-    AddSelectionBelow,
     Tab,
     TabPrev,
-    ShowCharacterPalette,
-    SelectLargerSyntaxNode,
-    SelectSmallerSyntaxNode,
-    GoToDefinition,
-    GoToDefinitionSplit,
-    GoToTypeDefinition,
-    GoToTypeDefinitionSplit,
-    MoveToEnclosingBracket,
+    ToggleInlayHints,
+    ToggleSoftWrap,
+    Transpose,
+    Undo,
     UndoSelection,
-    RedoSelection,
-    FindAllReferences,
-    Rename,
-    ConfirmRename,
-    Fold,
     UnfoldLines,
-    FoldSelectedRanges,
-    ShowCompletions,
-    OpenExcerpts,
-    RestartLanguageServer,
-    Hover,
-    Format,
-    ToggleSoftWrap,
-    ToggleInlayHints,
-    RevealInFinder,
-    CopyPath,
-    CopyRelativePath,
-    CopyHighlightJson,
-    ContextMenuFirst,
-    ContextMenuPrev,
-    ContextMenuNext,
-    ContextMenuLast,
 );
 
 // impl_actions!(
@@ -532,7 +548,6 @@ pub fn init(cx: &mut AppContext) {
     // cx.register_action_type(Editor::context_menu_last);
 
     hover_popover::init(cx);
-    scroll::actions::init(cx);
 
     workspace::register_project_item::<Editor>(cx);
     workspace::register_followable_item::<Editor>(cx);
@@ -988,7 +1003,7 @@ impl CompletionsMenu {
         project: Option<Model<Project>>,
         cx: &mut ViewContext<Editor>,
     ) {
-        todo!("implementation below ");
+        // todo!("implementation below ");
     }
     // ) {
     //     let settings = EditorSettings::get_global(cx);
@@ -1155,7 +1170,7 @@ impl CompletionsMenu {
         client: Arc<Client>,
         language_registry: Arc<LanguageRegistry>,
     ) {
-        todo!()
+        // todo!()
         // let request = proto::ResolveCompletionDocumentation {
         //     project_id,
         //     language_server_id: server_id.0 as u64,
@@ -1199,7 +1214,7 @@ impl CompletionsMenu {
         completion: lsp::CompletionItem,
         language_registry: Arc<LanguageRegistry>,
     ) {
-        todo!()
+        // todo!()
         // let can_resolve = server
         //     .capabilities()
         //     .completion_provider
@@ -2383,45 +2398,45 @@ impl Editor {
             .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
     }
 
-    //     pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
-    //     where
-    //         I: IntoIterator<Item = (Range<S>, T)>,
-    //         S: ToOffset,
-    //         T: Into<Arc<str>>,
-    //     {
-    //         if self.read_only {
-    //             return;
-    //         }
+    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
+    where
+        I: IntoIterator<Item = (Range<S>, T)>,
+        S: ToOffset,
+        T: Into<Arc<str>>,
+    {
+        if self.read_only {
+            return;
+        }
 
-    //         self.buffer.update(cx, |buffer, cx| {
-    //             buffer.edit(edits, self.autoindent_mode.clone(), cx)
-    //         });
-    //     }
+        self.buffer.update(cx, |buffer, cx| {
+            buffer.edit(edits, self.autoindent_mode.clone(), cx)
+        });
+    }
 
-    //     pub fn edit_with_block_indent<I, S, T>(
-    //         &mut self,
-    //         edits: I,
-    //         original_indent_columns: Vec<u32>,
-    //         cx: &mut ViewContext<Self>,
-    //     ) where
-    //         I: IntoIterator<Item = (Range<S>, T)>,
-    //         S: ToOffset,
-    //         T: Into<Arc<str>>,
-    //     {
-    //         if self.read_only {
-    //             return;
-    //         }
+    pub fn edit_with_block_indent<I, S, T>(
+        &mut self,
+        edits: I,
+        original_indent_columns: Vec<u32>,
+        cx: &mut ViewContext<Self>,
+    ) where
+        I: IntoIterator<Item = (Range<S>, T)>,
+        S: ToOffset,
+        T: Into<Arc<str>>,
+    {
+        if self.read_only {
+            return;
+        }
 
-    //         self.buffer.update(cx, |buffer, cx| {
-    //             buffer.edit(
-    //                 edits,
-    //                 Some(AutoindentMode::Block {
-    //                     original_indent_columns,
-    //                 }),
-    //                 cx,
-    //             )
-    //         });
-    //     }
+        self.buffer.update(cx, |buffer, cx| {
+            buffer.edit(
+                edits,
+                Some(AutoindentMode::Block {
+                    original_indent_columns,
+                }),
+                cx,
+            )
+        });
+    }
 
     fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
         self.hide_context_menu(cx);
@@ -2734,40 +2749,40 @@ impl Editor {
         self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
     }
 
-    //     pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
-    //         if self.take_rename(false, cx).is_some() {
-    //             return;
-    //         }
+    pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
+        if self.take_rename(false, cx).is_some() {
+            return;
+        }
 
-    //         if hide_hover(self, cx) {
-    //             return;
-    //         }
+        if hide_hover(self, cx) {
+            return;
+        }
 
-    //         if self.hide_context_menu(cx).is_some() {
-    //             return;
-    //         }
+        if self.hide_context_menu(cx).is_some() {
+            return;
+        }
 
-    //         if self.discard_copilot_suggestion(cx) {
-    //             return;
-    //         }
+        if self.discard_copilot_suggestion(cx) {
+            return;
+        }
 
-    //         if self.snippet_stack.pop().is_some() {
-    //             return;
-    //         }
+        if self.snippet_stack.pop().is_some() {
+            return;
+        }
 
-    //         if self.mode == EditorMode::Full {
-    //             if self.active_diagnostics.is_some() {
-    //                 self.dismiss_diagnostics(cx);
-    //                 return;
-    //             }
+        if self.mode == EditorMode::Full {
+            if self.active_diagnostics.is_some() {
+                self.dismiss_diagnostics(cx);
+                return;
+            }
 
-    //             if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
-    //                 return;
-    //             }
-    //         }
+            if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
+                return;
+            }
+        }
 
-    //         cx.propagate();
-    //     }
+        cx.propagate();
+    }
 
     pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
         let text: Arc<str> = text.into();
@@ -2961,263 +2976,263 @@ impl Editor {
         });
     }
 
-    //     pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
-    //         self.transact(cx, |this, cx| {
-    //             let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
-    //                 let selections = this.selections.all::<usize>(cx);
-    //                 let multi_buffer = this.buffer.read(cx);
-    //                 let buffer = multi_buffer.snapshot(cx);
-    //                 selections
-    //                     .iter()
-    //                     .map(|selection| {
-    //                         let start_point = selection.start.to_point(&buffer);
-    //                         let mut indent = buffer.indent_size_for_line(start_point.row);
-    //                         indent.len = cmp::min(indent.len, start_point.column);
-    //                         let start = selection.start;
-    //                         let end = selection.end;
-    //                         let is_cursor = start == end;
-    //                         let language_scope = buffer.language_scope_at(start);
-    //                         let (comment_delimiter, insert_extra_newline) = if let Some(language) =
-    //                             &language_scope
-    //                         {
-    //                             let leading_whitespace_len = buffer
-    //                                 .reversed_chars_at(start)
-    //                                 .take_while(|c| c.is_whitespace() && *c != '\n')
-    //                                 .map(|c| c.len_utf8())
-    //                                 .sum::<usize>();
-
-    //                             let trailing_whitespace_len = buffer
-    //                                 .chars_at(end)
-    //                                 .take_while(|c| c.is_whitespace() && *c != '\n')
-    //                                 .map(|c| c.len_utf8())
-    //                                 .sum::<usize>();
-
-    //                             let insert_extra_newline =
-    //                                 language.brackets().any(|(pair, enabled)| {
-    //                                     let pair_start = pair.start.trim_end();
-    //                                     let pair_end = pair.end.trim_start();
-
-    //                                     enabled
-    //                                         && pair.newline
-    //                                         && buffer.contains_str_at(
-    //                                             end + trailing_whitespace_len,
-    //                                             pair_end,
-    //                                         )
-    //                                         && buffer.contains_str_at(
-    //                                             (start - leading_whitespace_len)
-    //                                                 .saturating_sub(pair_start.len()),
-    //                                             pair_start,
-    //                                         )
-    //                                 });
-    //                             // Comment extension on newline is allowed only for cursor selections
-    //                             let comment_delimiter = language.line_comment_prefix().filter(|_| {
-    //                                 let is_comment_extension_enabled =
-    //                                     multi_buffer.settings_at(0, cx).extend_comment_on_newline;
-    //                                 is_cursor && is_comment_extension_enabled
-    //                             });
-    //                             let comment_delimiter = if let Some(delimiter) = comment_delimiter {
-    //                                 buffer
-    //                                     .buffer_line_for_row(start_point.row)
-    //                                     .is_some_and(|(snapshot, range)| {
-    //                                         let mut index_of_first_non_whitespace = 0;
-    //                                         let line_starts_with_comment = snapshot
-    //                                             .chars_for_range(range)
-    //                                             .skip_while(|c| {
-    //                                                 let should_skip = c.is_whitespace();
-    //                                                 if should_skip {
-    //                                                     index_of_first_non_whitespace += 1;
-    //                                                 }
-    //                                                 should_skip
-    //                                             })
-    //                                             .take(delimiter.len())
-    //                                             .eq(delimiter.chars());
-    //                                         let cursor_is_placed_after_comment_marker =
-    //                                             index_of_first_non_whitespace + delimiter.len()
-    //                                                 <= start_point.column as usize;
-    //                                         line_starts_with_comment
-    //                                             && cursor_is_placed_after_comment_marker
-    //                                     })
-    //                                     .then(|| delimiter.clone())
-    //                             } else {
-    //                                 None
-    //                             };
-    //                             (comment_delimiter, insert_extra_newline)
-    //                         } else {
-    //                             (None, false)
-    //                         };
-
-    //                         let capacity_for_delimiter = comment_delimiter
-    //                             .as_deref()
-    //                             .map(str::len)
-    //                             .unwrap_or_default();
-    //                         let mut new_text =
-    //                             String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
-    //                         new_text.push_str("\n");
-    //                         new_text.extend(indent.chars());
-    //                         if let Some(delimiter) = &comment_delimiter {
-    //                             new_text.push_str(&delimiter);
-    //                         }
-    //                         if insert_extra_newline {
-    //                             new_text = new_text.repeat(2);
-    //                         }
-
-    //                         let anchor = buffer.anchor_after(end);
-    //                         let new_selection = selection.map(|_| anchor);
-    //                         (
-    //                             (start..end, new_text),
-    //                             (insert_extra_newline, new_selection),
-    //                         )
-    //                     })
-    //                     .unzip()
-    //             };
+    pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
+        self.transact(cx, |this, cx| {
+            let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
+                let selections = this.selections.all::<usize>(cx);
+                let multi_buffer = this.buffer.read(cx);
+                let buffer = multi_buffer.snapshot(cx);
+                selections
+                    .iter()
+                    .map(|selection| {
+                        let start_point = selection.start.to_point(&buffer);
+                        let mut indent = buffer.indent_size_for_line(start_point.row);
+                        indent.len = cmp::min(indent.len, start_point.column);
+                        let start = selection.start;
+                        let end = selection.end;
+                        let is_cursor = start == end;
+                        let language_scope = buffer.language_scope_at(start);
+                        let (comment_delimiter, insert_extra_newline) = if let Some(language) =
+                            &language_scope
+                        {
+                            let leading_whitespace_len = buffer
+                                .reversed_chars_at(start)
+                                .take_while(|c| c.is_whitespace() && *c != '\n')
+                                .map(|c| c.len_utf8())
+                                .sum::<usize>();
+
+                            let trailing_whitespace_len = buffer
+                                .chars_at(end)
+                                .take_while(|c| c.is_whitespace() && *c != '\n')
+                                .map(|c| c.len_utf8())
+                                .sum::<usize>();
+
+                            let insert_extra_newline =
+                                language.brackets().any(|(pair, enabled)| {
+                                    let pair_start = pair.start.trim_end();
+                                    let pair_end = pair.end.trim_start();
+
+                                    enabled
+                                        && pair.newline
+                                        && buffer.contains_str_at(
+                                            end + trailing_whitespace_len,
+                                            pair_end,
+                                        )
+                                        && buffer.contains_str_at(
+                                            (start - leading_whitespace_len)
+                                                .saturating_sub(pair_start.len()),
+                                            pair_start,
+                                        )
+                                });
+                            // Comment extension on newline is allowed only for cursor selections
+                            let comment_delimiter = language.line_comment_prefix().filter(|_| {
+                                let is_comment_extension_enabled =
+                                    multi_buffer.settings_at(0, cx).extend_comment_on_newline;
+                                is_cursor && is_comment_extension_enabled
+                            });
+                            let comment_delimiter = if let Some(delimiter) = comment_delimiter {
+                                buffer
+                                    .buffer_line_for_row(start_point.row)
+                                    .is_some_and(|(snapshot, range)| {
+                                        let mut index_of_first_non_whitespace = 0;
+                                        let line_starts_with_comment = snapshot
+                                            .chars_for_range(range)
+                                            .skip_while(|c| {
+                                                let should_skip = c.is_whitespace();
+                                                if should_skip {
+                                                    index_of_first_non_whitespace += 1;
+                                                }
+                                                should_skip
+                                            })
+                                            .take(delimiter.len())
+                                            .eq(delimiter.chars());
+                                        let cursor_is_placed_after_comment_marker =
+                                            index_of_first_non_whitespace + delimiter.len()
+                                                <= start_point.column as usize;
+                                        line_starts_with_comment
+                                            && cursor_is_placed_after_comment_marker
+                                    })
+                                    .then(|| delimiter.clone())
+                            } else {
+                                None
+                            };
+                            (comment_delimiter, insert_extra_newline)
+                        } else {
+                            (None, false)
+                        };
+
+                        let capacity_for_delimiter = comment_delimiter
+                            .as_deref()
+                            .map(str::len)
+                            .unwrap_or_default();
+                        let mut new_text =
+                            String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
+                        new_text.push_str("\n");
+                        new_text.extend(indent.chars());
+                        if let Some(delimiter) = &comment_delimiter {
+                            new_text.push_str(&delimiter);
+                        }
+                        if insert_extra_newline {
+                            new_text = new_text.repeat(2);
+                        }
 
-    //             this.edit_with_autoindent(edits, cx);
-    //             let buffer = this.buffer.read(cx).snapshot(cx);
-    //             let new_selections = selection_fixup_info
-    //                 .into_iter()
-    //                 .map(|(extra_newline_inserted, new_selection)| {
-    //                     let mut cursor = new_selection.end.to_point(&buffer);
-    //                     if extra_newline_inserted {
-    //                         cursor.row -= 1;
-    //                         cursor.column = buffer.line_len(cursor.row);
-    //                     }
-    //                     new_selection.map(|_| cursor)
-    //                 })
-    //                 .collect();
+                        let anchor = buffer.anchor_after(end);
+                        let new_selection = selection.map(|_| anchor);
+                        (
+                            (start..end, new_text),
+                            (insert_extra_newline, new_selection),
+                        )
+                    })
+                    .unzip()
+            };
 
-    //             this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
-    //             this.refresh_copilot_suggestions(true, cx);
-    //         });
-    //     }
+            this.edit_with_autoindent(edits, cx);
+            let buffer = this.buffer.read(cx).snapshot(cx);
+            let new_selections = selection_fixup_info
+                .into_iter()
+                .map(|(extra_newline_inserted, new_selection)| {
+                    let mut cursor = new_selection.end.to_point(&buffer);
+                    if extra_newline_inserted {
+                        cursor.row -= 1;
+                        cursor.column = buffer.line_len(cursor.row);
+                    }
+                    new_selection.map(|_| cursor)
+                })
+                .collect();
 
-    //     pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
-    //         let buffer = self.buffer.read(cx);
-    //         let snapshot = buffer.snapshot(cx);
+            this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
+            this.refresh_copilot_suggestions(true, cx);
+        });
+    }
 
-    //         let mut edits = Vec::new();
-    //         let mut rows = Vec::new();
-    //         let mut rows_inserted = 0;
+    pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
+        let buffer = self.buffer.read(cx);
+        let snapshot = buffer.snapshot(cx);
 
-    //         for selection in self.selections.all_adjusted(cx) {
-    //             let cursor = selection.head();
-    //             let row = cursor.row;
+        let mut edits = Vec::new();
+        let mut rows = Vec::new();
+        let mut rows_inserted = 0;
 
-    //             let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
+        for selection in self.selections.all_adjusted(cx) {
+            let cursor = selection.head();
+            let row = cursor.row;
 
-    //             let newline = "\n".to_string();
-    //             edits.push((start_of_line..start_of_line, newline));
+            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 
-    //             rows.push(row + rows_inserted);
-    //             rows_inserted += 1;
-    //         }
+            let newline = "\n".to_string();
+            edits.push((start_of_line..start_of_line, newline));
 
-    //         self.transact(cx, |editor, cx| {
-    //             editor.edit(edits, cx);
+            rows.push(row + rows_inserted);
+            rows_inserted += 1;
+        }
 
-    //             editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-    //                 let mut index = 0;
-    //                 s.move_cursors_with(|map, _, _| {
-    //                     let row = rows[index];
-    //                     index += 1;
+        self.transact(cx, |editor, cx| {
+            editor.edit(edits, cx);
 
-    //                     let point = Point::new(row, 0);
-    //                     let boundary = map.next_line_boundary(point).1;
-    //                     let clipped = map.clip_point(boundary, Bias::Left);
+            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                let mut index = 0;
+                s.move_cursors_with(|map, _, _| {
+                    let row = rows[index];
+                    index += 1;
 
-    //                     (clipped, SelectionGoal::None)
-    //                 });
-    //             });
+                    let point = Point::new(row, 0);
+                    let boundary = map.next_line_boundary(point).1;
+                    let clipped = map.clip_point(boundary, Bias::Left);
 
-    //             let mut indent_edits = Vec::new();
-    //             let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
-    //             for row in rows {
-    //                 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
-    //                 for (row, indent) in indents {
-    //                     if indent.len == 0 {
-    //                         continue;
-    //                     }
+                    (clipped, SelectionGoal::None)
+                });
+            });
 
-    //                     let text = match indent.kind {
-    //                         IndentKind::Space => " ".repeat(indent.len as usize),
-    //                         IndentKind::Tab => "\t".repeat(indent.len as usize),
-    //                     };
-    //                     let point = Point::new(row, 0);
-    //                     indent_edits.push((point..point, text));
-    //                 }
-    //             }
-    //             editor.edit(indent_edits, cx);
-    //         });
-    //     }
+            let mut indent_edits = Vec::new();
+            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
+            for row in rows {
+                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
+                for (row, indent) in indents {
+                    if indent.len == 0 {
+                        continue;
+                    }
 
-    //     pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
-    //         let buffer = self.buffer.read(cx);
-    //         let snapshot = buffer.snapshot(cx);
+                    let text = match indent.kind {
+                        IndentKind::Space => " ".repeat(indent.len as usize),
+                        IndentKind::Tab => "\t".repeat(indent.len as usize),
+                    };
+                    let point = Point::new(row, 0);
+                    indent_edits.push((point..point, text));
+                }
+            }
+            editor.edit(indent_edits, cx);
+        });
+    }
 
-    //         let mut edits = Vec::new();
-    //         let mut rows = Vec::new();
-    //         let mut rows_inserted = 0;
+    pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
+        let buffer = self.buffer.read(cx);
+        let snapshot = buffer.snapshot(cx);
 
-    //         for selection in self.selections.all_adjusted(cx) {
-    //             let cursor = selection.head();
-    //             let row = cursor.row;
+        let mut edits = Vec::new();
+        let mut rows = Vec::new();
+        let mut rows_inserted = 0;
 
-    //             let point = Point::new(row + 1, 0);
-    //             let start_of_line = snapshot.clip_point(point, Bias::Left);
+        for selection in self.selections.all_adjusted(cx) {
+            let cursor = selection.head();
+            let row = cursor.row;
 
-    //             let newline = "\n".to_string();
-    //             edits.push((start_of_line..start_of_line, newline));
+            let point = Point::new(row + 1, 0);
+            let start_of_line = snapshot.clip_point(point, Bias::Left);
 
-    //             rows_inserted += 1;
-    //             rows.push(row + rows_inserted);
-    //         }
+            let newline = "\n".to_string();
+            edits.push((start_of_line..start_of_line, newline));
 
-    //         self.transact(cx, |editor, cx| {
-    //             editor.edit(edits, cx);
+            rows_inserted += 1;
+            rows.push(row + rows_inserted);
+        }
 
-    //             editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-    //                 let mut index = 0;
-    //                 s.move_cursors_with(|map, _, _| {
-    //                     let row = rows[index];
-    //                     index += 1;
+        self.transact(cx, |editor, cx| {
+            editor.edit(edits, cx);
 
-    //                     let point = Point::new(row, 0);
-    //                     let boundary = map.next_line_boundary(point).1;
-    //                     let clipped = map.clip_point(boundary, Bias::Left);
+            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                let mut index = 0;
+                s.move_cursors_with(|map, _, _| {
+                    let row = rows[index];
+                    index += 1;
 
-    //                     (clipped, SelectionGoal::None)
-    //                 });
-    //             });
+                    let point = Point::new(row, 0);
+                    let boundary = map.next_line_boundary(point).1;
+                    let clipped = map.clip_point(boundary, Bias::Left);
 
-    //             let mut indent_edits = Vec::new();
-    //             let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
-    //             for row in rows {
-    //                 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
-    //                 for (row, indent) in indents {
-    //                     if indent.len == 0 {
-    //                         continue;
-    //                     }
+                    (clipped, SelectionGoal::None)
+                });
+            });
 
-    //                     let text = match indent.kind {
-    //                         IndentKind::Space => " ".repeat(indent.len as usize),
-    //                         IndentKind::Tab => "\t".repeat(indent.len as usize),
-    //                     };
-    //                     let point = Point::new(row, 0);
-    //                     indent_edits.push((point..point, text));
-    //                 }
-    //             }
-    //             editor.edit(indent_edits, cx);
-    //         });
-    //     }
+            let mut indent_edits = Vec::new();
+            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
+            for row in rows {
+                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
+                for (row, indent) in indents {
+                    if indent.len == 0 {
+                        continue;
+                    }
 
-    //     pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
-    //         self.insert_with_autoindent_mode(
-    //             text,
-    //             Some(AutoindentMode::Block {
-    //                 original_indent_columns: Vec::new(),
-    //             }),
-    //             cx,
-    //         );
-    //     }
+                    let text = match indent.kind {
+                        IndentKind::Space => " ".repeat(indent.len as usize),
+                        IndentKind::Tab => "\t".repeat(indent.len as usize),
+                    };
+                    let point = Point::new(row, 0);
+                    indent_edits.push((point..point, text));
+                }
+            }
+            editor.edit(indent_edits, cx);
+        });
+    }
+
+    pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
+        self.insert_with_autoindent_mode(
+            text,
+            Some(AutoindentMode::Block {
+                original_indent_columns: Vec::new(),
+            }),
+            cx,
+        );
+    }
 
     fn insert_with_autoindent_mode(
         &mut self,

crates/editor2/src/element.rs 🔗

@@ -2,8 +2,10 @@ use crate::{
     display_map::{BlockStyle, DisplaySnapshot, FoldStatus, HighlightedChunk, ToDisplayPoint},
     editor_settings::ShowScrollbar,
     git::{diff_hunk_to_display, DisplayDiffHunk},
+    scroll::scroll_amount::ScrollAmount,
     CursorShape, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle,
-    MoveDown, Point, Selection, SoftWrap, ToPoint, MAX_LINE_LEN,
+    HalfPageDown, HalfPageUp, LineDown, LineUp, MoveDown, PageDown, PageUp, Point, Selection,
+    SoftWrap, ToPoint, MAX_LINE_LEN,
 };
 use anyhow::Result;
 use collections::{BTreeMap, HashMap};
@@ -31,6 +33,7 @@ use std::{
 };
 use sum_tree::Bias;
 use theme::{ActiveTheme, PlayerColor};
+use util::ResultExt;
 use workspace::item::Item;
 
 enum FoldMarkers {}
@@ -2562,6 +2565,153 @@ impl Element<Editor> for EditorElement {
                         build_action_listener(Editor::move_right),
                         build_action_listener(Editor::move_down),
                         build_action_listener(Editor::move_up),
+                        // build_action_listener(Editor::new_file), todo!()
+                        // build_action_listener(Editor::new_file_in_direction), todo!()
+                        build_action_listener(Editor::cancel),
+                        build_action_listener(Editor::newline),
+                        build_action_listener(Editor::newline_above),
+                        build_action_listener(Editor::newline_below),
+                        build_action_listener(Editor::backspace),
+                        build_action_listener(Editor::delete),
+                        build_action_listener(Editor::tab),
+                        build_action_listener(Editor::tab_prev),
+                        build_action_listener(Editor::indent),
+                        build_action_listener(Editor::outdent),
+                        build_action_listener(Editor::delete_line),
+                        build_action_listener(Editor::join_lines),
+                        build_action_listener(Editor::sort_lines_case_sensitive),
+                        build_action_listener(Editor::sort_lines_case_insensitive),
+                        build_action_listener(Editor::reverse_lines),
+                        build_action_listener(Editor::shuffle_lines),
+                        build_action_listener(Editor::convert_to_upper_case),
+                        build_action_listener(Editor::convert_to_lower_case),
+                        build_action_listener(Editor::convert_to_title_case),
+                        build_action_listener(Editor::convert_to_snake_case),
+                        build_action_listener(Editor::convert_to_kebab_case),
+                        build_action_listener(Editor::convert_to_upper_camel_case),
+                        build_action_listener(Editor::convert_to_lower_camel_case),
+                        build_action_listener(Editor::delete_to_previous_word_start),
+                        build_action_listener(Editor::delete_to_previous_subword_start),
+                        build_action_listener(Editor::delete_to_next_word_end),
+                        build_action_listener(Editor::delete_to_next_subword_end),
+                        build_action_listener(Editor::delete_to_beginning_of_line),
+                        build_action_listener(Editor::delete_to_end_of_line),
+                        build_action_listener(Editor::cut_to_end_of_line),
+                        build_action_listener(Editor::duplicate_line),
+                        build_action_listener(Editor::move_line_up),
+                        build_action_listener(Editor::move_line_down),
+                        build_action_listener(Editor::transpose),
+                        build_action_listener(Editor::cut),
+                        build_action_listener(Editor::copy),
+                        build_action_listener(Editor::paste),
+                        build_action_listener(Editor::undo),
+                        build_action_listener(Editor::redo),
+                        build_action_listener(Editor::move_page_up),
+                        build_action_listener(Editor::move_page_down),
+                        build_action_listener(Editor::next_screen),
+                        build_action_listener(Editor::scroll_cursor_top),
+                        build_action_listener(Editor::scroll_cursor_center),
+                        build_action_listener(Editor::scroll_cursor_bottom),
+                        build_action_listener(|editor, _: &LineDown, cx| {
+                            editor.scroll_screen(&ScrollAmount::Line(1.), cx)
+                        }),
+                        build_action_listener(|editor, _: &LineUp, cx| {
+                            editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
+                        }),
+                        build_action_listener(|editor, _: &HalfPageDown, cx| {
+                            editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
+                        }),
+                        build_action_listener(|editor, _: &HalfPageUp, cx| {
+                            editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
+                        }),
+                        build_action_listener(|editor, _: &PageDown, cx| {
+                            editor.scroll_screen(&ScrollAmount::Page(1.), cx)
+                        }),
+                        build_action_listener(|editor, _: &PageUp, cx| {
+                            editor.scroll_screen(&ScrollAmount::Page(-1.), cx)
+                        }),
+                        build_action_listener(Editor::move_to_previous_word_start),
+                        build_action_listener(Editor::move_to_previous_subword_start),
+                        build_action_listener(Editor::move_to_next_word_end),
+                        build_action_listener(Editor::move_to_next_subword_end),
+                        build_action_listener(Editor::move_to_beginning_of_line),
+                        build_action_listener(Editor::move_to_end_of_line),
+                        build_action_listener(Editor::move_to_start_of_paragraph),
+                        build_action_listener(Editor::move_to_end_of_paragraph),
+                        build_action_listener(Editor::move_to_beginning),
+                        build_action_listener(Editor::move_to_end),
+                        build_action_listener(Editor::select_up),
+                        build_action_listener(Editor::select_down),
+                        build_action_listener(Editor::select_left),
+                        build_action_listener(Editor::select_right),
+                        build_action_listener(Editor::select_to_previous_word_start),
+                        build_action_listener(Editor::select_to_previous_subword_start),
+                        build_action_listener(Editor::select_to_next_word_end),
+                        build_action_listener(Editor::select_to_next_subword_end),
+                        build_action_listener(Editor::select_to_beginning_of_line),
+                        build_action_listener(Editor::select_to_end_of_line),
+                        build_action_listener(Editor::select_to_start_of_paragraph),
+                        build_action_listener(Editor::select_to_end_of_paragraph),
+                        build_action_listener(Editor::select_to_beginning),
+                        build_action_listener(Editor::select_to_end),
+                        build_action_listener(Editor::select_all),
+                        build_action_listener(|editor, action, cx| {
+                            editor.select_all_matches(action, cx).log_err();
+                        }),
+                        build_action_listener(Editor::select_line),
+                        build_action_listener(Editor::split_selection_into_lines),
+                        build_action_listener(Editor::add_selection_above),
+                        build_action_listener(Editor::add_selection_below),
+                        build_action_listener(|editor, action, cx| {
+                            editor.select_next(action, cx).log_err();
+                        }),
+                        build_action_listener(|editor, action, cx| {
+                            editor.select_previous(action, cx).log_err();
+                        }),
+                        build_action_listener(Editor::toggle_comments),
+                        build_action_listener(Editor::select_larger_syntax_node),
+                        build_action_listener(Editor::select_smaller_syntax_node),
+                        build_action_listener(Editor::move_to_enclosing_bracket),
+                        build_action_listener(Editor::undo_selection),
+                        build_action_listener(Editor::redo_selection),
+                        build_action_listener(Editor::go_to_diagnostic),
+                        build_action_listener(Editor::go_to_prev_diagnostic),
+                        build_action_listener(Editor::go_to_hunk),
+                        build_action_listener(Editor::go_to_prev_hunk),
+                        build_action_listener(Editor::go_to_definition),
+                        build_action_listener(Editor::go_to_definition_split),
+                        build_action_listener(Editor::go_to_type_definition),
+                        build_action_listener(Editor::go_to_type_definition_split),
+                        build_action_listener(Editor::fold),
+                        build_action_listener(Editor::fold_at),
+                        build_action_listener(Editor::unfold_lines),
+                        build_action_listener(Editor::unfold_at),
+                        // build_action_listener(Editor::gutter_hover), todo!()
+                        build_action_listener(Editor::fold_selected_ranges),
+                        build_action_listener(Editor::show_completions),
+                        // build_action_listener(Editor::toggle_code_actions), todo!()
+                        // build_action_listener(Editor::open_excerpts), todo!()
+                        build_action_listener(Editor::toggle_soft_wrap),
+                        build_action_listener(Editor::toggle_inlay_hints),
+                        build_action_listener(Editor::reveal_in_finder),
+                        build_action_listener(Editor::copy_path),
+                        build_action_listener(Editor::copy_relative_path),
+                        build_action_listener(Editor::copy_highlight_json),
+                        build_action_listener(|editor, action, cx| {
+                            editor
+                                .format(action, cx)
+                                .map(|task| task.detach_and_log_err(cx));
+                        }),
+                        build_action_listener(Editor::restart_language_server),
+                        build_action_listener(Editor::show_character_palette),
+                        // build_action_listener(Editor::confirm_completion), todo!()
+                        // build_action_listener(Editor::confirm_code_action), todo!()
+                        // build_action_listener(Editor::rename), todo!()
+                        // build_action_listener(Editor::confirm_rename), todo!()
+                        // build_action_listener(Editor::find_all_references), todo!()
+                        build_action_listener(Editor::next_copilot_suggestion),
+                        build_action_listener(Editor::previous_copilot_suggestion),
+                        build_action_listener(Editor::copilot_suggest),
                         build_key_listener(
                             move |editor, key_down: &KeyDownEvent, dispatch_context, phase, cx| {
                                 if phase == DispatchPhase::Bubble {

crates/editor2/src/scroll.rs 🔗

@@ -288,16 +288,15 @@ impl ScrollManager {
     }
 }
 
-// todo!()
 impl Editor {
-    //     pub fn vertical_scroll_margin(&mut self) -> usize {
-    //         self.scroll_manager.vertical_scroll_margin as usize
-    //     }
+    pub fn vertical_scroll_margin(&mut self) -> usize {
+        self.scroll_manager.vertical_scroll_margin as usize
+    }
 
-    //     pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) {
-    //         self.scroll_manager.vertical_scroll_margin = margin_rows as f32;
-    //         cx.notify();
-    //     }
+    pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) {
+        self.scroll_manager.vertical_scroll_margin = margin_rows as f32;
+        cx.notify();
+    }
 
     pub fn visible_line_count(&self) -> Option<f32> {
         self.scroll_manager.visible_line_count
@@ -349,11 +348,9 @@ impl Editor {
         self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
     }
 
-    pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> gpui::Point<Pixels> {
+    pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> gpui::Point<f32> {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        // todo!() Should `self.scroll_manager.anchor.scroll_position()` return `Pixels`?
-        // self.scroll_manager.anchor.scroll_position(&display_map)
-        todo!()
+        self.scroll_manager.anchor.scroll_position(&display_map)
     }
 
     pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) {
@@ -382,50 +379,50 @@ impl Editor {
             .set_anchor(scroll_anchor, top_row, false, false, workspace_id, cx);
     }
 
-    //     pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
-    //         if matches!(self.mode, EditorMode::SingleLine) {
-    //             cx.propagate_action();
-    //             return;
-    //         }
-
-    //         if self.take_rename(true, cx).is_some() {
-    //             return;
-    //         }
-
-    //         let cur_position = self.scroll_position(cx);
-    //         let new_pos = cur_position + point(0., amount.lines(self));
-    //         self.set_scroll_position(new_pos, cx);
-    //     }
-
-    //     /// Returns an ordering. The newest selection is:
-    //     ///     Ordering::Equal => on screen
-    //     ///     Ordering::Less => above the screen
-    //     ///     Ordering::Greater => below the screen
-    //     pub fn newest_selection_on_screen(&self, cx: &mut AppContext) -> Ordering {
-    //         let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-    //         let newest_head = self
-    //             .selections
-    //             .newest_anchor()
-    //             .head()
-    //             .to_display_point(&snapshot);
-    //         let screen_top = self
-    //             .scroll_manager
-    //             .anchor
-    //             .anchor
-    //             .to_display_point(&snapshot);
-
-    //         if screen_top > newest_head {
-    //             return Ordering::Less;
-    //         }
-
-    //         if let Some(visible_lines) = self.visible_line_count() {
-    //             if newest_head.row() < screen_top.row() + visible_lines as u32 {
-    //                 return Ordering::Equal;
-    //             }
-    //         }
-
-    //         Ordering::Greater
-    //     }
+    pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
+        if matches!(self.mode, EditorMode::SingleLine) {
+            cx.propagate();
+            return;
+        }
+
+        if self.take_rename(true, cx).is_some() {
+            return;
+        }
+
+        let cur_position = self.scroll_position(cx);
+        let new_pos = cur_position + point(0., amount.lines(self));
+        self.set_scroll_position(new_pos, cx);
+    }
+
+    /// Returns an ordering. The newest selection is:
+    ///     Ordering::Equal => on screen
+    ///     Ordering::Less => above the screen
+    ///     Ordering::Greater => below the screen
+    pub fn newest_selection_on_screen(&self, cx: &mut AppContext) -> Ordering {
+        let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
+        let newest_head = self
+            .selections
+            .newest_anchor()
+            .head()
+            .to_display_point(&snapshot);
+        let screen_top = self
+            .scroll_manager
+            .anchor
+            .anchor
+            .to_display_point(&snapshot);
+
+        if screen_top > newest_head {
+            return Ordering::Less;
+        }
+
+        if let Some(visible_lines) = self.visible_line_count() {
+            if newest_head.row() < screen_top.row() + visible_lines as u32 {
+                return Ordering::Equal;
+            }
+        }
+
+        Ordering::Greater
+    }
 
     pub fn read_scroll_position_from_db(
         &mut self,

crates/editor2/src/scroll/actions.rs 🔗

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

crates/editor2/src/selections_collection.rs 🔗

@@ -302,39 +302,39 @@ impl SelectionsCollection {
             .collect()
     }
 
-    // pub fn build_columnar_selection(
-    //     &mut self,
-    //     display_map: &DisplaySnapshot,
-    //     row: u32,
-    //     positions: &Range<Pixels>,
-    //     reversed: bool,
-    //     text_layout_details: &TextLayoutDetails,
-    // ) -> Option<Selection<Point>> {
-    //     let is_empty = positions.start == positions.end;
-    //     let line_len = display_map.line_len(row);
-
-    //     let layed_out_line = display_map.lay_out_line_for_row(row, &text_layout_details);
-
-    //     let start_col = layed_out_line.closest_index_for_x(positions.start) as u32;
-    //     if start_col < line_len || (is_empty && positions.start == layed_out_line.width()) {
-    //         let start = DisplayPoint::new(row, start_col);
-    //         let end_col = layed_out_line.closest_index_for_x(positions.end) as u32;
-    //         let end = DisplayPoint::new(row, end_col);
-
-    //         Some(Selection {
-    //             id: post_inc(&mut self.next_selection_id),
-    //             start: start.to_point(display_map),
-    //             end: end.to_point(display_map),
-    //             reversed,
-    //             goal: SelectionGoal::HorizontalRange {
-    //                 start: positions.start,
-    //                 end: positions.end,
-    //             },
-    //         })
-    //     } else {
-    //         None
-    //     }
-    // }
+    pub fn build_columnar_selection(
+        &mut self,
+        display_map: &DisplaySnapshot,
+        row: u32,
+        positions: &Range<Pixels>,
+        reversed: bool,
+        text_layout_details: &TextLayoutDetails,
+    ) -> Option<Selection<Point>> {
+        let is_empty = positions.start == positions.end;
+        let line_len = display_map.line_len(row);
+
+        let layed_out_line = display_map.lay_out_line_for_row(row, &text_layout_details);
+
+        let start_col = layed_out_line.closest_index_for_x(positions.start) as u32;
+        if start_col < line_len || (is_empty && positions.start == layed_out_line.width) {
+            let start = DisplayPoint::new(row, start_col);
+            let end_col = layed_out_line.closest_index_for_x(positions.end) as u32;
+            let end = DisplayPoint::new(row, end_col);
+
+            Some(Selection {
+                id: post_inc(&mut self.next_selection_id),
+                start: start.to_point(display_map),
+                end: end.to_point(display_map),
+                reversed,
+                goal: SelectionGoal::HorizontalRange {
+                    start: positions.start.into(),
+                    end: positions.end.into(),
+                },
+            })
+        } else {
+            None
+        }
+    }
 
     pub(crate) fn change_with<R>(
         &mut self,

crates/gpui2/src/window.rs 🔗

@@ -608,6 +608,10 @@ impl<'a> WindowContext<'a> {
             .find(|display| display.id() == self.window.display_id)
     }
 
+    pub fn show_character_palette(&self) {
+        self.window.platform_window.show_character_palette();
+    }
+
     /// The scale factor of the display associated with the window. For example, it could
     /// return 2.0 for a "retina" display, indicating that each logical pixel should actually
     /// be rendered as two pixels on screen.

crates/gpui2_macros/src/action.rs 🔗

@@ -31,20 +31,21 @@ pub fn action(_attr: TokenStream, item: TokenStream) -> TokenStream {
         #[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::default::Default, std::fmt::Debug)]
         #(#attrs)*
     };
+    let visibility = input.vis;
 
     let output = match input.data {
         syn::Data::Struct(ref struct_data) => {
             let fields = &struct_data.fields;
             quote! {
                 #attributes
-                struct #name { #fields }
+                #visibility struct #name #fields
             }
         }
         syn::Data::Enum(ref enum_data) => {
             let variants = &enum_data.variants;
             quote! {
                 #attributes
-                enum #name { #variants }
+                #visibility enum #name { #variants }
             }
         }
         _ => panic!("Expected a struct or an enum."),