Merge branch 'main' into v0.173.x

Joseph T. Lyons created

Change summary

assets/keymaps/vim.json                                         |   4 
crates/buffer_diff/src/buffer_diff.rs                           |  14 
crates/gpui/src/view.rs                                         | 185 +-
crates/gpui/src/window.rs                                       |  15 
crates/inline_completion_button/src/inline_completion_button.rs |  47 
crates/language/src/language_settings.rs                        |   4 
crates/languages/src/markdown/config.toml                       |   1 
crates/project/src/buffer_store.rs                              |  18 
crates/vim/src/object.rs                                        |  53 
crates/vim/test_data/test_around_containing_word_indent.json    |  23 
10 files changed, 241 insertions(+), 123 deletions(-)

Detailed changes

assets/keymaps/vim.json 🔗

@@ -696,7 +696,7 @@
     }
   },
   {
-    "context": "edit_prediction && !edit_prediction_requires_modifier",
+    "context": "Editor && edit_prediction && !edit_prediction_requires_modifier",
     "bindings": {
       // This is identical to the binding in the base keymap, but the vim bindings above to
       // "vim::Tab" shadow it, so it needs to be bound again.
@@ -704,7 +704,7 @@
     }
   },
   {
-    "context": "os != macos && edit_prediction",
+    "context": "os != macos && Editor && edit_prediction",
     "bindings": {
       // alt-l is provided as an alternative to tab/alt-tab. and will be displayed in the UI. This
       // is because alt-tab may not be available, as it is often used for window switching on Linux

crates/buffer_diff/src/buffer_diff.rs 🔗

@@ -587,16 +587,18 @@ impl BufferDiff {
         range: Range<Anchor>,
         buffer: &text::BufferSnapshot,
         cx: &App,
-    ) -> Range<Anchor> {
+    ) -> Option<Range<Anchor>> {
         let start = self
             .hunks_intersecting_range(range.clone(), &buffer, cx)
-            .next()
-            .map_or(Anchor::MIN, |hunk| hunk.buffer_range.start);
+            .next()?
+            .buffer_range
+            .start;
         let end = self
             .hunks_intersecting_range_rev(range.clone(), &buffer)
-            .next()
-            .map_or(Anchor::MAX, |hunk| hunk.buffer_range.end);
-        start..end
+            .next()?
+            .buffer_range
+            .end;
+        Some(start..end)
     }
 
     #[allow(clippy::too_many_arguments)]

crates/gpui/src/view.rs 🔗

@@ -40,7 +40,9 @@ impl<V: Render> Element for Entity<V> {
         cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
         let mut element = self.update(cx, |view, cx| view.render(window, cx).into_any_element());
-        let layout_id = element.request_layout(window, cx);
+        let layout_id = window.with_rendered_view(self.entity_id(), |window| {
+            element.request_layout(window, cx)
+        });
         (layout_id, element)
     }
 
@@ -53,7 +55,7 @@ impl<V: Render> Element for Entity<V> {
         cx: &mut App,
     ) {
         window.set_view_id(self.entity_id());
-        element.prepaint(window, cx);
+        window.with_rendered_view(self.entity_id(), |window| element.prepaint(window, cx));
     }
 
     fn paint(
@@ -65,7 +67,7 @@ impl<V: Render> Element for Entity<V> {
         window: &mut Window,
         cx: &mut App,
     ) {
-        element.paint(window, cx);
+        window.with_rendered_view(self.entity_id(), |window| element.paint(window, cx));
     }
 }
 
@@ -150,18 +152,18 @@ impl Element for AnyView {
         window: &mut Window,
         cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
-        if let Some(style) = self.cached_style.as_ref() {
-            let mut root_style = Style::default();
-            root_style.refine(style);
-            let layout_id = window.request_layout(root_style, None, cx);
-            (layout_id, None)
-        } else {
-            window.with_rendered_view(self.entity_id(), |window| {
+        window.with_rendered_view(self.entity_id(), |window| {
+            if let Some(style) = self.cached_style.as_ref() {
+                let mut root_style = Style::default();
+                root_style.refine(style);
+                let layout_id = window.request_layout(root_style, None, cx);
+                (layout_id, None)
+            } else {
                 let mut element = (self.render)(self, window, cx);
                 let layout_id = element.request_layout(window, cx);
                 (layout_id, Some(element))
-            })
-        }
+            }
+        })
     }
 
     fn prepaint(
@@ -173,69 +175,66 @@ impl Element for AnyView {
         cx: &mut App,
     ) -> Option<AnyElement> {
         window.set_view_id(self.entity_id());
-        if self.cached_style.is_some() {
-            window.with_element_state::<AnyViewState, _>(
-                global_id.unwrap(),
-                |element_state, window| {
-                    let content_mask = window.content_mask();
-                    let text_style = window.text_style();
-
-                    if let Some(mut element_state) = element_state {
-                        if element_state.cache_key.bounds == bounds
-                            && element_state.cache_key.content_mask == content_mask
-                            && element_state.cache_key.text_style == text_style
-                            && !window.dirty_views.contains(&self.entity_id())
-                            && !window.refreshing
-                        {
-                            let prepaint_start = window.prepaint_index();
-                            window.reuse_prepaint(element_state.prepaint_range.clone());
-                            cx.entities
-                                .extend_accessed(&element_state.accessed_entities);
-                            let prepaint_end = window.prepaint_index();
-                            element_state.prepaint_range = prepaint_start..prepaint_end;
-
-                            return (None, element_state);
+        window.with_rendered_view(self.entity_id(), |window| {
+            if self.cached_style.is_some() {
+                window.with_element_state::<AnyViewState, _>(
+                    global_id.unwrap(),
+                    |element_state, window| {
+                        let content_mask = window.content_mask();
+                        let text_style = window.text_style();
+
+                        if let Some(mut element_state) = element_state {
+                            if element_state.cache_key.bounds == bounds
+                                && element_state.cache_key.content_mask == content_mask
+                                && element_state.cache_key.text_style == text_style
+                                && !window.dirty_views.contains(&self.entity_id())
+                                && !window.refreshing
+                            {
+                                let prepaint_start = window.prepaint_index();
+                                window.reuse_prepaint(element_state.prepaint_range.clone());
+                                cx.entities
+                                    .extend_accessed(&element_state.accessed_entities);
+                                let prepaint_end = window.prepaint_index();
+                                element_state.prepaint_range = prepaint_start..prepaint_end;
+
+                                return (None, element_state);
+                            }
                         }
-                    }
-
-                    let refreshing = mem::replace(&mut window.refreshing, true);
-                    let prepaint_start = window.prepaint_index();
-                    let (mut element, accessed_entities) =
-                        window.with_rendered_view(self.entity_id(), |window| {
-                            cx.detect_accessed_entities(|cx| {
-                                let mut element = (self.render)(self, window, cx);
-                                element.layout_as_root(bounds.size.into(), window, cx);
-                                element.prepaint_at(bounds.origin, window, cx);
-                                element
-                            })
+
+                        let refreshing = mem::replace(&mut window.refreshing, true);
+                        let prepaint_start = window.prepaint_index();
+                        let (mut element, accessed_entities) = cx.detect_accessed_entities(|cx| {
+                            let mut element = (self.render)(self, window, cx);
+                            element.layout_as_root(bounds.size.into(), window, cx);
+                            element.prepaint_at(bounds.origin, window, cx);
+                            element
                         });
 
-                    let prepaint_end = window.prepaint_index();
-                    window.refreshing = refreshing;
-
-                    (
-                        Some(element),
-                        AnyViewState {
-                            accessed_entities,
-                            prepaint_range: prepaint_start..prepaint_end,
-                            paint_range: PaintIndex::default()..PaintIndex::default(),
-                            cache_key: ViewCacheKey {
-                                bounds,
-                                content_mask,
-                                text_style,
+                        let prepaint_end = window.prepaint_index();
+                        window.refreshing = refreshing;
+
+                        (
+                            Some(element),
+                            AnyViewState {
+                                accessed_entities,
+                                prepaint_range: prepaint_start..prepaint_end,
+                                paint_range: PaintIndex::default()..PaintIndex::default(),
+                                cache_key: ViewCacheKey {
+                                    bounds,
+                                    content_mask,
+                                    text_style,
+                                },
                             },
-                        },
-                    )
-                },
-            )
-        } else {
-            let mut element = element.take().unwrap();
-            window.with_rendered_view(self.entity_id(), |window| {
+                        )
+                    },
+                )
+            } else {
+                let mut element = element.take().unwrap();
                 element.prepaint(window, cx);
-            });
 
-            Some(element)
-        }
+                Some(element)
+            }
+        })
     }
 
     fn paint(
@@ -247,33 +246,33 @@ impl Element for AnyView {
         window: &mut Window,
         cx: &mut App,
     ) {
-        if self.cached_style.is_some() {
-            window.with_element_state::<AnyViewState, _>(
-                global_id.unwrap(),
-                |element_state, window| {
-                    let mut element_state = element_state.unwrap();
+        window.with_rendered_view(self.entity_id(), |window| {
+            if self.cached_style.is_some() {
+                window.with_element_state::<AnyViewState, _>(
+                    global_id.unwrap(),
+                    |element_state, window| {
+                        let mut element_state = element_state.unwrap();
 
-                    let paint_start = window.paint_index();
+                        let paint_start = window.paint_index();
 
-                    if let Some(element) = element {
-                        let refreshing = mem::replace(&mut window.refreshing, true);
-                        window.with_rendered_view(self.entity_id(), |window| {
+                        if let Some(element) = element {
+                            let refreshing = mem::replace(&mut window.refreshing, true);
                             element.paint(window, cx);
-                        });
-                        window.refreshing = refreshing;
-                    } else {
-                        window.reuse_paint(element_state.paint_range.clone());
-                    }
-
-                    let paint_end = window.paint_index();
-                    element_state.paint_range = paint_start..paint_end;
-
-                    ((), element_state)
-                },
-            )
-        } else {
-            element.as_mut().unwrap().paint(window, cx);
-        }
+                            window.refreshing = refreshing;
+                        } else {
+                            window.reuse_paint(element_state.paint_range.clone());
+                        }
+
+                        let paint_end = window.paint_index();
+                        element_state.paint_range = paint_start..paint_end;
+
+                        ((), element_state)
+                    },
+                )
+            } else {
+                element.as_mut().unwrap().paint(window, cx);
+            }
+        });
     }
 }
 

crates/gpui/src/window.rs 🔗

@@ -477,6 +477,7 @@ pub(crate) struct TooltipRequest {
 }
 
 pub(crate) struct DeferredDraw {
+    current_view: EntityId,
     priority: usize,
     parent_node: DispatchNodeId,
     element_id_stack: SmallVec<[ElementId; 32]>,
@@ -1750,9 +1751,11 @@ impl Window {
 
             let prepaint_start = self.prepaint_index();
             if let Some(element) = deferred_draw.element.as_mut() {
-                self.with_absolute_element_offset(deferred_draw.absolute_offset, |window| {
-                    element.prepaint(window, cx)
-                });
+                self.with_rendered_view(deferred_draw.current_view, |window| {
+                    window.with_absolute_element_offset(deferred_draw.absolute_offset, |window| {
+                        element.prepaint(window, cx)
+                    });
+                })
             } else {
                 self.reuse_prepaint(deferred_draw.prepaint_range.clone());
             }
@@ -1783,7 +1786,9 @@ impl Window {
 
             let paint_start = self.paint_index();
             if let Some(element) = deferred_draw.element.as_mut() {
-                element.paint(self, cx);
+                self.with_rendered_view(deferred_draw.current_view, |window| {
+                    element.paint(window, cx);
+                })
             } else {
                 self.reuse_paint(deferred_draw.paint_range.clone());
             }
@@ -1841,6 +1846,7 @@ impl Window {
                 [range.start.deferred_draws_index..range.end.deferred_draws_index]
                 .iter()
                 .map(|deferred_draw| DeferredDraw {
+                    current_view: deferred_draw.current_view,
                     parent_node: reused_subtree.refresh_node_id(deferred_draw.parent_node),
                     element_id_stack: deferred_draw.element_id_stack.clone(),
                     text_style_stack: deferred_draw.text_style_stack.clone(),
@@ -2247,6 +2253,7 @@ impl Window {
         self.invalidator.debug_assert_prepaint();
         let parent_node = self.next_frame.dispatch_tree.active_node_id().unwrap();
         self.next_frame.deferred_draws.push(DeferredDraw {
+            current_view: self.current_view(),
             parent_node,
             element_id_stack: self.element_id_stack.clone(),
             text_style_stack: self.text_style_stack.clone(),

crates/inline_completion_button/src/inline_completion_button.rs 🔗

@@ -438,13 +438,10 @@ impl InlineCompletionButton {
 
         let settings = AllLanguageSettings::get_global(cx);
         let globally_enabled = settings.show_inline_completions(None, cx);
-        menu = menu.toggleable_entry(
-            "All Files",
-            globally_enabled,
-            IconPosition::Start,
-            None,
-            move |_, cx| toggle_inline_completions_globally(fs.clone(), cx),
-        );
+        menu = menu.toggleable_entry("All Files", globally_enabled, IconPosition::Start, None, {
+            let fs = fs.clone();
+            move |_, cx| toggle_inline_completions_globally(fs.clone(), cx)
+        });
         menu = menu.separator().header("Privacy Settings");
 
         if let Some(provider) = &self.edit_prediction_provider {
@@ -554,6 +551,42 @@ impl InlineCompletionButton {
             );
         }
 
+        let is_eager_preview_enabled = match settings.inline_completions_preview_mode() {
+            language::InlineCompletionPreviewMode::Auto => true,
+            language::InlineCompletionPreviewMode::WhenHoldingModifier => false,
+        };
+        menu = menu.separator().toggleable_entry(
+            "Eager Preview",
+            is_eager_preview_enabled,
+            IconPosition::Start,
+            None,
+            {
+                let fs = fs.clone();
+                move |_window, cx| {
+                    update_settings_file::<AllLanguageSettings>(
+                        fs.clone(),
+                        cx,
+                        move |settings, _cx| {
+                            let inline_preview = match is_eager_preview_enabled {
+                                true => language::InlineCompletionPreviewMode::WhenHoldingModifier,
+                                false => language::InlineCompletionPreviewMode::Auto,
+                            };
+
+                            if let Some(edit_predictions) = settings.edit_predictions.as_mut() {
+                                edit_predictions.inline_preview = inline_preview;
+                            } else {
+                                settings.edit_predictions =
+                                    Some(language_settings::EditPredictionSettingsContent {
+                                        inline_preview,
+                                        ..Default::default()
+                                    });
+                            }
+                        },
+                    );
+                }
+            },
+        );
+
         if let Some(editor_focus_handle) = self.editor_focus_handle.clone() {
             menu = menu
                 .separator()

crates/language/src/language_settings.rs 🔗

@@ -250,7 +250,7 @@ pub struct AllLanguageSettingsContent {
     pub features: Option<FeaturesContent>,
     /// The edit prediction settings.
     #[serde(default)]
-    pub edit_predictions: Option<InlineCompletionSettingsContent>,
+    pub edit_predictions: Option<EditPredictionSettingsContent>,
     /// The default language settings.
     #[serde(flatten)]
     pub defaults: LanguageSettingsContent,
@@ -428,7 +428,7 @@ pub struct LanguageSettingsContent {
 
 /// The contents of the edit prediction settings.
 #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
-pub struct InlineCompletionSettingsContent {
+pub struct EditPredictionSettingsContent {
     /// A list of globs representing files that edit predictions should be disabled for.
     /// This list adds to a pre-existing, sensible default set of globs.
     /// Any additional ones you add are combined with them.

crates/languages/src/markdown/config.toml 🔗

@@ -3,6 +3,7 @@ grammar = "markdown"
 path_suffixes = ["md", "mdx", "mdwn", "markdown", "MD"]
 word_characters = ["-"]
 block_comment = ["<!-- ", " -->"]
+autoclose_before = "}])>"
 brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },

crates/project/src/buffer_store.rs 🔗

@@ -260,16 +260,20 @@ impl BufferDiffState {
                     let changed_range = match (unstaged_changed_range, uncommitted_changed_range) {
                         (None, None) => None,
                         (Some(unstaged_range), None) => {
-                            Some(uncommitted_diff.range_to_hunk_range(unstaged_range, &buffer, cx))
+                            uncommitted_diff.range_to_hunk_range(unstaged_range, &buffer, cx)
                         }
                         (None, Some(uncommitted_range)) => Some(uncommitted_range),
-                        (Some(unstaged_range), Some(uncommitted_range)) => maybe!({
-                            let expanded_range =
-                                uncommitted_diff.range_to_hunk_range(unstaged_range, &buffer, cx);
-                            let start = expanded_range.start.min(&uncommitted_range.start, &buffer);
-                            let end = expanded_range.end.max(&uncommitted_range.end, &buffer);
+                        (Some(unstaged_range), Some(uncommitted_range)) => {
+                            let mut start = uncommitted_range.start;
+                            let mut end = uncommitted_range.end;
+                            if let Some(unstaged_range) =
+                                uncommitted_diff.range_to_hunk_range(unstaged_range, &buffer, cx)
+                            {
+                                start = unstaged_range.start.min(&uncommitted_range.start, &buffer);
+                                end = unstaged_range.end.max(&uncommitted_range.end, &buffer);
+                            }
                             Some(start..end)
-                        }),
+                        }
                     };
                     cx.emit(BufferDiffEvent::DiffChanged { changed_range });
                 })?;

crates/vim/src/object.rs 🔗

@@ -727,8 +727,25 @@ fn around_containing_word(
     relative_to: DisplayPoint,
     ignore_punctuation: bool,
 ) -> Option<Range<DisplayPoint>> {
-    in_word(map, relative_to, ignore_punctuation)
-        .map(|range| expand_to_include_whitespace(map, range, true))
+    in_word(map, relative_to, ignore_punctuation).map(|range| {
+        let line_start = DisplayPoint::new(range.start.row(), 0);
+        let is_first_word = map
+            .buffer_chars_at(line_start.to_offset(map, Bias::Left))
+            .take_while(|(ch, offset)| {
+                offset < &range.start.to_offset(map, Bias::Left) && ch.is_whitespace()
+            })
+            .count()
+            > 0;
+
+        if is_first_word {
+            // For first word on line, trim indentation
+            let mut expanded = expand_to_include_whitespace(map, range.clone(), true);
+            expanded.start = range.start;
+            expanded
+        } else {
+            expand_to_include_whitespace(map, range, true)
+        }
+    })
 }
 
 fn around_next_word(
@@ -2455,4 +2472,36 @@ mod test {
             Mode::Visual,
         );
     }
+    #[gpui::test]
+    async fn test_around_containing_word_indent(cx: &mut gpui::TestAppContext) {
+        let mut cx = NeovimBackedTestContext::new(cx).await;
+
+        cx.set_shared_state("    ˇconst f = (x: unknown) => {")
+            .await;
+        cx.simulate_shared_keystrokes("v a w").await;
+        cx.shared_state()
+            .await
+            .assert_eq("    «const ˇ»f = (x: unknown) => {");
+
+        cx.set_shared_state("    ˇconst f = (x: unknown) => {")
+            .await;
+        cx.simulate_shared_keystrokes("y a w").await;
+        cx.shared_clipboard().await.assert_eq("const ");
+
+        cx.set_shared_state("    ˇconst f = (x: unknown) => {")
+            .await;
+        cx.simulate_shared_keystrokes("d a w").await;
+        cx.shared_state()
+            .await
+            .assert_eq("    ˇf = (x: unknown) => {");
+        cx.shared_clipboard().await.assert_eq("const ");
+
+        cx.set_shared_state("    ˇconst f = (x: unknown) => {")
+            .await;
+        cx.simulate_shared_keystrokes("c a w").await;
+        cx.shared_state()
+            .await
+            .assert_eq("    ˇf = (x: unknown) => {");
+        cx.shared_clipboard().await.assert_eq("const ");
+    }
 }

crates/vim/test_data/test_around_containing_word_indent.json 🔗

@@ -0,0 +1,23 @@
+{"Put":{"state":"    ˇconst f = (x: unknown) => {"}}
+{"Key":"v"}
+{"Key":"a"}
+{"Key":"w"}
+{"Get":{"state":"    «const ˇ»f = (x: unknown) => {","mode":"Visual"}}
+{"Put":{"state":"    ˇconst f = (x: unknown) => {"}}
+{"Key":"y"}
+{"Key":"a"}
+{"Key":"w"}
+{"Get":{"state":"    ˇconst f = (x: unknown) => {","mode":"Normal"}}
+{"ReadRegister":{"name":"\"","value":"const "}}
+{"Put":{"state":"    ˇconst f = (x: unknown) => {"}}
+{"Key":"d"}
+{"Key":"a"}
+{"Key":"w"}
+{"Get":{"state":"    ˇf = (x: unknown) => {","mode":"Normal"}}
+{"ReadRegister":{"name":"\"","value":"const "}}
+{"Put":{"state":"    ˇconst f = (x: unknown) => {"}}
+{"Key":"c"}
+{"Key":"a"}
+{"Key":"w"}
+{"Get":{"state":"    ˇf = (x: unknown) => {","mode":"Insert"}}
+{"ReadRegister":{"name":"\"","value":"const "}}