Merge branch 'main' into v0.173.x

Joseph T. Lyons created

Change summary

Cargo.lock                                                      |   1 
assets/settings/default.json                                    |  13 
crates/collab/src/db/queries/users.rs                           |  61 
crates/collab_ui/src/channel_view.rs                            |   2 
crates/component/src/component.rs                               |  32 
crates/component_preview/src/component_preview.rs               |  36 
crates/editor/src/editor.rs                                     | 170 +
crates/editor/src/editor_settings.rs                            |   7 
crates/editor/src/editor_tests.rs                               | 170 +
crates/editor/src/items.rs                                      |   6 
crates/git_ui/src/git_panel.rs                                  |  22 
crates/git_ui/src/project_diff.rs                               |   2 
crates/image_viewer/src/image_viewer.rs                         |   2 
crates/inline_completion_button/src/inline_completion_button.rs |  16 
crates/language/src/language.rs                                 |   2 
crates/language/src/language_settings.rs                        |  34 
crates/migrator/Cargo.toml                                      |   1 
crates/migrator/src/migrator.rs                                 | 261 +-
crates/project_panel/src/project_panel.rs                       |   2 
crates/repl/src/notebook/notebook_ui.rs                         |   2 
crates/storybook/src/story_selector.rs                          |   2 
crates/ui/src/components/avatar/avatar.rs                       |   1 
crates/ui/src/components/button/button.rs                       |   1 
crates/ui/src/components/content_group.rs                       |   1 
crates/ui/src/components/icon.rs                                |   1 
crates/ui/src/components/icon/decorated_icon.rs                 |   1 
crates/ui/src/components/keybinding_hint.rs                     |   1 
crates/ui/src/components/label/highlighted_label.rs             |  12 
crates/ui/src/components/label/label.rs                         | 130 
crates/ui/src/components/label/label_like.rs                    |  41 
crates/ui/src/components/stories.rs                             |   2 
crates/ui/src/components/stories/label.rs                       |  38 
crates/ui/src/components/tab.rs                                 |   1 
crates/ui/src/components/table.rs                               |   1 
crates/ui/src/components/toggle.rs                              |   3 
crates/ui/src/components/tooltip.rs                             |   1 
crates/ui/src/prelude.rs                                        |   5 
crates/ui/src/styles/typography.rs                              |   1 
crates/welcome/src/welcome.rs                                   |   2 
crates/zed/src/zed/migrate.rs                                   |  11 
script/mitm-proxy.sh                                            |  18 
41 files changed, 731 insertions(+), 385 deletions(-)

Detailed changes

Cargo.lock πŸ”—

@@ -7878,6 +7878,7 @@ dependencies = [
 name = "migrator"
 version = "0.1.0"
 dependencies = [
+ "anyhow",
  "collections",
  "convert_case 0.7.1",
  "log",

assets/settings/default.json πŸ”—

@@ -168,9 +168,6 @@
   /// Whether to show the signature help after completion or a bracket pair inserted.
   /// If `auto_signature_help` is enabled, this setting will be treated as enabled also.
   "show_signature_help_after_edits": false,
-  /// Whether to show the edit predictions next to the completions provided by a language server.
-  /// Only has an effect if edit prediction provider supports it.
-  "show_edit_predictions_in_menu": true,
   // Whether to show wrap guides (vertical rulers) in the editor.
   // Setting this to true will show a guide at the 'preferred_line_length' value
   // if 'soft_wrap' is set to 'preferred_line_length', and will show any
@@ -795,11 +792,11 @@
     ],
     // When to show edit predictions previews in buffer.
     // This setting takes two possible values:
-    // 1. Display inline when there are no language server completions available.
-    //     "inline_preview": "auto"
-    // 2. Display inline when holding modifier key (alt by default).
-    //     "inline_preview": "when_holding_modifier"
-    "inline_preview": "auto"
+    // 1. Display inline when holding modifier key (alt by default).
+    //     "mode": "auto"
+    // 2. Display inline when there are no language server completions available.
+    //     "mode": "eager_preview"
+    "mode": "auto"
   },
   // Settings specific to journaling
   "journal": {

crates/collab/src/db/queries/users.rs πŸ”—

@@ -133,26 +133,23 @@ impl Database {
         initial_channel_id: Option<ChannelId>,
         tx: &DatabaseTransaction,
     ) -> Result<User> {
-        if let Some(user_by_github_user_id) = user::Entity::find()
-            .filter(user::Column::GithubUserId.eq(github_user_id))
-            .one(tx)
-            .await?
-        {
-            let mut user_by_github_user_id = user_by_github_user_id.into_active_model();
-            user_by_github_user_id.github_login = ActiveValue::set(github_login.into());
-            user_by_github_user_id.github_user_created_at =
-                ActiveValue::set(Some(github_user_created_at));
-            Ok(user_by_github_user_id.update(tx).await?)
-        } else if let Some(user_by_github_login) = user::Entity::find()
-            .filter(user::Column::GithubLogin.eq(github_login))
-            .one(tx)
+        if let Some(existing_user) = self
+            .get_user_by_github_user_id_or_github_login(github_user_id, github_login, tx)
             .await?
         {
-            let mut user_by_github_login = user_by_github_login.into_active_model();
-            user_by_github_login.github_user_id = ActiveValue::set(github_user_id);
-            user_by_github_login.github_user_created_at =
-                ActiveValue::set(Some(github_user_created_at));
-            Ok(user_by_github_login.update(tx).await?)
+            let mut existing_user = existing_user.into_active_model();
+            existing_user.github_login = ActiveValue::set(github_login.into());
+            existing_user.github_user_created_at = ActiveValue::set(Some(github_user_created_at));
+
+            if let Some(github_email) = github_email {
+                existing_user.email_address = ActiveValue::set(Some(github_email.into()));
+            }
+
+            if let Some(github_name) = github_name {
+                existing_user.name = ActiveValue::set(Some(github_name.into()));
+            }
+
+            Ok(existing_user.update(tx).await?)
         } else {
             let user = user::Entity::insert(user::ActiveModel {
                 email_address: ActiveValue::set(github_email.map(|email| email.into())),
@@ -183,6 +180,34 @@ impl Database {
         }
     }
 
+    /// Tries to retrieve a user, first by their GitHub user ID, and then by their GitHub login.
+    ///
+    /// Returns `None` if a user is not found with this GitHub user ID or GitHub login.
+    pub async fn get_user_by_github_user_id_or_github_login(
+        &self,
+        github_user_id: i32,
+        github_login: &str,
+        tx: &DatabaseTransaction,
+    ) -> Result<Option<User>> {
+        if let Some(user_by_github_user_id) = user::Entity::find()
+            .filter(user::Column::GithubUserId.eq(github_user_id))
+            .one(tx)
+            .await?
+        {
+            return Ok(Some(user_by_github_user_id));
+        }
+
+        if let Some(user_by_github_login) = user::Entity::find()
+            .filter(user::Column::GithubLogin.eq(github_login))
+            .one(tx)
+            .await?
+        {
+            return Ok(Some(user_by_github_login));
+        }
+
+        Ok(None)
+    }
+
     /// get_all_users returns the next page of users. To get more call again with
     /// the same limit and the page incremented by 1.
     pub async fn get_all_users(&self, page: u32, limit: u32) -> Result<Vec<User>> {

crates/collab_ui/src/channel_view.rs πŸ”—

@@ -461,7 +461,7 @@ impl Item for ChannelView {
             .child(
                 Label::new(channel_name)
                     .color(params.text_color())
-                    .italic(params.preview),
+                    .when(params.preview, |this| this.italic()),
             )
             .when_some(status, |element, status| {
                 element.child(

crates/component/src/component.rs πŸ”—

@@ -1,7 +1,7 @@
 use std::ops::{Deref, DerefMut};
 
 use collections::HashMap;
-use gpui::{div, prelude::*, AnyElement, App, IntoElement, RenderOnce, SharedString, Window};
+use gpui::{div, prelude::*, px, AnyElement, App, IntoElement, RenderOnce, SharedString, Window};
 use linkme::distributed_slice;
 use once_cell::sync::Lazy;
 use parking_lot::RwLock;
@@ -201,8 +201,9 @@ impl RenderOnce for ComponentExample {
         };
 
         base.gap_1()
-            .text_xs()
-            .text_color(cx.theme().colors().text_muted)
+            .p_2()
+            .text_sm()
+            .text_color(cx.theme().colors().text)
             .when(self.grow, |this| this.flex_1())
             .child(self.element)
             .child(self.variant_name)
@@ -243,13 +244,34 @@ impl RenderOnce for ComponentExampleGroup {
             .text_sm()
             .text_color(cx.theme().colors().text_muted)
             .when(self.grow, |this| this.w_full().flex_1())
-            .when_some(self.title, |this, title| this.gap_4().child(title))
+            .when_some(self.title, |this, title| {
+                this.gap_4().pb_5().child(
+                    div()
+                        .flex()
+                        .items_center()
+                        .gap_3()
+                        .child(div().h_px().w_4().bg(cx.theme().colors().border_variant))
+                        .child(
+                            div()
+                                .flex_none()
+                                .text_size(px(10.))
+                                .child(title.to_uppercase()),
+                        )
+                        .child(
+                            div()
+                                .h_px()
+                                .w_full()
+                                .flex_1()
+                                .bg(cx.theme().colors().border),
+                        ),
+                )
+            })
             .child(
                 div()
                     .flex()
                     .items_start()
                     .w_full()
-                    .gap_6()
+                    .gap_8()
                     .children(self.examples)
                     .into_any_element(),
             )

crates/component_preview/src/component_preview.rs πŸ”—

@@ -41,11 +41,21 @@ impl ComponentPreview {
         let components = components().all_sorted();
         let sorted_components = components.clone();
 
-        v_flex().gap_px().p_1().children(
-            sorted_components
-                .into_iter()
-                .map(|component| self.render_sidebar_entry(&component, _cx)),
-        )
+        v_flex()
+            .max_w_48()
+            .gap_px()
+            .p_1()
+            .children(
+                sorted_components
+                    .into_iter()
+                    .map(|component| self.render_sidebar_entry(&component, _cx)),
+            )
+            .child(
+                Label::new("These will be clickable once the layout is moved to a gpui::List.")
+                    .color(Color::Muted)
+                    .size(LabelSize::XSmall)
+                    .italic(),
+            )
     }
 
     fn render_sidebar_entry(
@@ -56,7 +66,8 @@ impl ComponentPreview {
         h_flex()
             .w_40()
             .px_1p5()
-            .py_1()
+            .py_0p5()
+            .text_sm()
             .child(component.name().clone())
     }
 
@@ -71,18 +82,19 @@ impl ComponentPreview {
 
         let description = component.description();
 
-        v_group()
+        v_flex()
+            .border_b_1()
+            .border_color(cx.theme().colors().border)
             .w_full()
-            .gap_4()
-            .p_8()
-            .rounded_md()
+            .gap_3()
+            .py_6()
             .child(
                 v_flex()
                     .gap_1()
                     .child(
                         h_flex()
                             .gap_1()
-                            .text_xl()
+                            .text_2xl()
                             .child(div().child(name))
                             .when_some(scope, |this, scope| {
                                 this.child(div().opacity(0.5).child(format!("({})", scope)))
@@ -110,7 +122,7 @@ impl ComponentPreview {
             .size_full()
             .overflow_y_scroll()
             .p_4()
-            .gap_2()
+            .gap_4()
             .children(
                 components()
                     .all_previews_sorted()

crates/editor/src/editor.rs πŸ”—

@@ -95,8 +95,8 @@ use itertools::Itertools;
 use language::{
     language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
     markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
-    CompletionDocumentation, CursorShape, Diagnostic, EditPreview, HighlightedText, IndentKind,
-    IndentSize, InlineCompletionPreviewMode, Language, OffsetRangeExt, Point, Selection,
+    CompletionDocumentation, CursorShape, Diagnostic, EditPredictionsMode, EditPreview,
+    HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point, Selection,
     SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
 };
 use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
@@ -5047,7 +5047,6 @@ impl Editor {
         );
 
         let show_in_menu = by_provider
-            && EditorSettings::get_global(cx).show_edit_predictions_in_menu
             && self
                 .edit_prediction_provider
                 .as_ref()
@@ -5055,9 +5054,8 @@ impl Editor {
                     provider.provider.show_completions_in_menu()
                 });
 
-        let preview_requires_modifier = all_language_settings(file, cx)
-            .inline_completions_preview_mode()
-            == InlineCompletionPreviewMode::WhenHoldingModifier;
+        let preview_requires_modifier =
+            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Auto;
 
         EditPredictionSettings::Enabled {
             show_in_menu,
@@ -10584,13 +10582,17 @@ impl Editor {
             }
         }
 
-        let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
+        let active_group_id = self
+            .active_diagnostics
+            .as_ref()
+            .map(|active_group| active_group.group_id);
+        let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
             active_diagnostics
                 .primary_range
                 .to_offset(&buffer)
                 .to_inclusive()
         });
-        let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
+        let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
             if active_primary_range.contains(&selection.head()) {
                 *active_primary_range.start()
             } else {
@@ -10599,83 +10601,91 @@ impl Editor {
         } else {
             selection.head()
         };
+
         let snapshot = self.snapshot(window, cx);
-        loop {
-            let mut diagnostics;
-            if direction == Direction::Prev {
-                diagnostics = buffer
-                    .diagnostics_in_range::<usize>(0..search_start)
-                    .collect::<Vec<_>>();
-                diagnostics.reverse();
-            } else {
-                diagnostics = buffer
-                    .diagnostics_in_range::<usize>(search_start..buffer.len())
-                    .collect::<Vec<_>>();
-            };
-            let group = diagnostics
-                .into_iter()
-                .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
-                // relies on diagnostics_in_range to return diagnostics with the same starting range to
-                // be sorted in a stable way
-                // skip until we are at current active diagnostic, if it exists
-                .skip_while(|entry| {
-                    let is_in_range = match direction {
-                        Direction::Prev => entry.range.end > search_start,
-                        Direction::Next => entry.range.start < search_start,
-                    };
-                    is_in_range
-                        && self
-                            .active_diagnostics
-                            .as_ref()
-                            .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
-                })
-                .find_map(|entry| {
-                    if entry.diagnostic.is_primary
-                        && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
-                        && entry.range.start != entry.range.end
-                        // if we match with the active diagnostic, skip it
-                        && Some(entry.diagnostic.group_id)
-                            != self.active_diagnostics.as_ref().map(|d| d.group_id)
-                    {
-                        Some((entry.range, entry.diagnostic.group_id))
+        let primary_diagnostics_before = buffer
+            .diagnostics_in_range::<usize>(0..search_start)
+            .filter(|entry| entry.diagnostic.is_primary)
+            .filter(|entry| entry.range.start != entry.range.end)
+            .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
+            .filter(|entry| !snapshot.intersects_fold(entry.range.start))
+            .collect::<Vec<_>>();
+        let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
+            primary_diagnostics_before
+                .iter()
+                .position(|entry| entry.diagnostic.group_id == active_group_id)
+        });
+
+        let primary_diagnostics_after = buffer
+            .diagnostics_in_range::<usize>(search_start..buffer.len())
+            .filter(|entry| entry.diagnostic.is_primary)
+            .filter(|entry| entry.range.start != entry.range.end)
+            .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
+            .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
+            .collect::<Vec<_>>();
+        let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
+            primary_diagnostics_after
+                .iter()
+                .enumerate()
+                .rev()
+                .find_map(|(i, entry)| {
+                    if entry.diagnostic.group_id == active_group_id {
+                        Some(i)
                     } else {
                         None
                     }
-                });
+                })
+        });
 
-            if let Some((primary_range, group_id)) = group {
-                let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
-                    return;
-                };
-                self.activate_diagnostics(buffer_id, group_id, window, cx);
-                if self.active_diagnostics.is_some() {
-                    self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
-                        s.select(vec![Selection {
-                            id: selection.id,
-                            start: primary_range.start,
-                            end: primary_range.start,
-                            reversed: false,
-                            goal: SelectionGoal::None,
-                        }]);
-                    });
-                    self.refresh_inline_completion(false, true, window, cx);
-                }
-                break;
-            } else {
-                // Cycle around to the start of the buffer, potentially moving back to the start of
-                // the currently active diagnostic.
-                active_primary_range.take();
-                if direction == Direction::Prev {
-                    if search_start == buffer.len() {
-                        break;
-                    } else {
-                        search_start = buffer.len();
-                    }
-                } else if search_start == 0 {
-                    break;
-                } else {
-                    search_start = 0;
-                }
+        let next_primary_diagnostic = match direction {
+            Direction::Prev => primary_diagnostics_before
+                .iter()
+                .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
+                .rev()
+                .next(),
+            Direction::Next => primary_diagnostics_after
+                .iter()
+                .skip(
+                    last_same_group_diagnostic_after
+                        .map(|index| index + 1)
+                        .unwrap_or(0),
+                )
+                .next(),
+        };
+
+        // Cycle around to the start of the buffer, potentially moving back to the start of
+        // the currently active diagnostic.
+        let cycle_around = || match direction {
+            Direction::Prev => primary_diagnostics_after
+                .iter()
+                .rev()
+                .chain(primary_diagnostics_before.iter().rev())
+                .next(),
+            Direction::Next => primary_diagnostics_before
+                .iter()
+                .chain(primary_diagnostics_after.iter())
+                .next(),
+        };
+
+        if let Some((primary_range, group_id)) = next_primary_diagnostic
+            .or_else(cycle_around)
+            .map(|entry| (&entry.range, entry.diagnostic.group_id))
+        {
+            let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
+                return;
+            };
+            self.activate_diagnostics(buffer_id, group_id, window, cx);
+            if self.active_diagnostics.is_some() {
+                self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                    s.select(vec![Selection {
+                        id: selection.id,
+                        start: primary_range.start,
+                        end: primary_range.start,
+                        reversed: false,
+                        goal: SelectionGoal::None,
+                    }]);
+                });
+                self.refresh_inline_completion(false, true, window, cx);
             }
         }
     }

crates/editor/src/editor_settings.rs πŸ”—

@@ -35,7 +35,6 @@ pub struct EditorSettings {
     pub auto_signature_help: bool,
     pub show_signature_help_after_edits: bool,
     pub jupyter: Jupyter,
-    pub show_edit_predictions_in_menu: bool,
 }
 
 #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
@@ -368,12 +367,6 @@ pub struct EditorSettingsContent {
     /// Default: false
     pub show_signature_help_after_edits: Option<bool>,
 
-    /// Whether to show the edit predictions next to the completions provided by a language server.
-    /// Only has an effect if edit prediction provider supports it.
-    ///
-    /// Default: true
-    pub show_edit_predictions_in_menu: Option<bool>,
-
     /// Jupyter REPL settings.
     pub jupyter: Option<JupyterContent>,
 }

crates/editor/src/editor_tests.rs πŸ”—

@@ -10668,6 +10668,176 @@ async fn go_to_prev_overlapping_diagnostic(
     "});
 }
 
+#[gpui::test]
+async fn cycle_through_same_place_diagnostics(
+    executor: BackgroundExecutor,
+    cx: &mut gpui::TestAppContext,
+) {
+    init_test(cx, |_| {});
+
+    let mut cx = EditorTestContext::new(cx).await;
+    let lsp_store =
+        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
+
+    cx.set_state(indoc! {"
+        Λ‡fn func(abc def: i32) -> u32 {
+        }
+    "});
+
+    cx.update(|_, cx| {
+        lsp_store.update(cx, |lsp_store, cx| {
+            lsp_store
+                .update_diagnostics(
+                    LanguageServerId(0),
+                    lsp::PublishDiagnosticsParams {
+                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
+                        version: None,
+                        diagnostics: vec![
+                            lsp::Diagnostic {
+                                range: lsp::Range::new(
+                                    lsp::Position::new(0, 11),
+                                    lsp::Position::new(0, 12),
+                                ),
+                                severity: Some(lsp::DiagnosticSeverity::ERROR),
+                                ..Default::default()
+                            },
+                            lsp::Diagnostic {
+                                range: lsp::Range::new(
+                                    lsp::Position::new(0, 12),
+                                    lsp::Position::new(0, 15),
+                                ),
+                                severity: Some(lsp::DiagnosticSeverity::ERROR),
+                                ..Default::default()
+                            },
+                            lsp::Diagnostic {
+                                range: lsp::Range::new(
+                                    lsp::Position::new(0, 12),
+                                    lsp::Position::new(0, 15),
+                                ),
+                                severity: Some(lsp::DiagnosticSeverity::ERROR),
+                                ..Default::default()
+                            },
+                            lsp::Diagnostic {
+                                range: lsp::Range::new(
+                                    lsp::Position::new(0, 25),
+                                    lsp::Position::new(0, 28),
+                                ),
+                                severity: Some(lsp::DiagnosticSeverity::ERROR),
+                                ..Default::default()
+                            },
+                        ],
+                    },
+                    &[],
+                    cx,
+                )
+                .unwrap()
+        });
+    });
+    executor.run_until_parked();
+
+    //// Backward
+
+    // Fourth diagnostic
+    cx.update_editor(|editor, window, cx| {
+        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
+    });
+    cx.assert_editor_state(indoc! {"
+        fn func(abc def: i32) -> Λ‡u32 {
+        }
+    "});
+
+    // Third diagnostic
+    cx.update_editor(|editor, window, cx| {
+        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
+    });
+    cx.assert_editor_state(indoc! {"
+        fn func(abc Λ‡def: i32) -> u32 {
+        }
+    "});
+
+    // Second diagnostic, same place
+    cx.update_editor(|editor, window, cx| {
+        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
+    });
+    cx.assert_editor_state(indoc! {"
+        fn func(abc Λ‡def: i32) -> u32 {
+        }
+    "});
+
+    // First diagnostic
+    cx.update_editor(|editor, window, cx| {
+        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
+    });
+    cx.assert_editor_state(indoc! {"
+        fn func(abcˇ def: i32) -> u32 {
+        }
+    "});
+
+    // Wrapped over, fourth diagnostic
+    cx.update_editor(|editor, window, cx| {
+        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
+    });
+    cx.assert_editor_state(indoc! {"
+        fn func(abc def: i32) -> Λ‡u32 {
+        }
+    "});
+
+    cx.update_editor(|editor, window, cx| {
+        editor.move_to_beginning(&MoveToBeginning, window, cx);
+    });
+    cx.assert_editor_state(indoc! {"
+        Λ‡fn func(abc def: i32) -> u32 {
+        }
+    "});
+
+    //// Forward
+
+    // First diagnostic
+    cx.update_editor(|editor, window, cx| {
+        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
+    });
+    cx.assert_editor_state(indoc! {"
+        fn func(abcˇ def: i32) -> u32 {
+        }
+    "});
+
+    // Second diagnostic
+    cx.update_editor(|editor, window, cx| {
+        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
+    });
+    cx.assert_editor_state(indoc! {"
+        fn func(abc Λ‡def: i32) -> u32 {
+        }
+    "});
+
+    // Third diagnostic, same place
+    cx.update_editor(|editor, window, cx| {
+        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
+    });
+    cx.assert_editor_state(indoc! {"
+        fn func(abc Λ‡def: i32) -> u32 {
+        }
+    "});
+
+    // Fourth diagnostic
+    cx.update_editor(|editor, window, cx| {
+        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
+    });
+    cx.assert_editor_state(indoc! {"
+        fn func(abc def: i32) -> Λ‡u32 {
+        }
+    "});
+
+    // Wrapped around, first diagnostic
+    cx.update_editor(|editor, window, cx| {
+        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
+    });
+    cx.assert_editor_state(indoc! {"
+        fn func(abcˇ def: i32) -> u32 {
+        }
+    "});
+}
+
 #[gpui::test]
 async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
     init_test(cx, |_| {});

crates/editor/src/items.rs πŸ”—

@@ -36,7 +36,7 @@ use std::{
 };
 use text::{BufferId, Selection};
 use theme::{Theme, ThemeSettings};
-use ui::{h_flex, prelude::*, IconDecorationKind, Label};
+use ui::{prelude::*, IconDecorationKind};
 use util::{paths::PathExt, ResultExt, TryFutureExt};
 use workspace::item::{Dedup, ItemSettings, SerializableItem, TabContentParams};
 use workspace::{
@@ -679,8 +679,8 @@ impl Item for Editor {
             .child(
                 Label::new(self.title(cx).to_string())
                     .color(label_color)
-                    .italic(params.preview)
-                    .strikethrough(was_deleted),
+                    .when(params.preview, |this| this.italic())
+                    .when(was_deleted, |this| this.strikethrough()),
             )
             .when_some(description, |this, description| {
                 this.child(

crates/git_ui/src/git_panel.rs πŸ”—

@@ -789,7 +789,14 @@ impl GitPanel {
                 let email = participant.user.email.clone().unwrap();
 
                 if !existing_co_authors.contains(&email.as_ref()) {
-                    new_co_authors.push((participant.user.github_login.clone(), email))
+                    new_co_authors.push((
+                        participant
+                            .user
+                            .name
+                            .clone()
+                            .unwrap_or_else(|| participant.user.github_login.clone()),
+                        email,
+                    ))
                 }
             }
         }
@@ -797,7 +804,12 @@ impl GitPanel {
             if let Some(user) = room.local_participant_user(cx) {
                 if let Some(email) = user.email.clone() {
                     if !existing_co_authors.contains(&email.as_ref()) {
-                        new_co_authors.push((user.github_login.clone(), email.clone()))
+                        new_co_authors.push((
+                            user.name
+                                .clone()
+                                .unwrap_or_else(|| user.github_login.clone()),
+                            email.clone(),
+                        ))
                     }
                 }
             }
@@ -1657,9 +1669,7 @@ impl GitPanel {
                                 if !parent_str.is_empty() {
                                     this.child(
                                         self.entry_label(format!("{}/", parent_str), path_color)
-                                            .when(status.is_deleted(), |this| {
-                                                this.strikethrough(true)
-                                            }),
+                                            .when(status.is_deleted(), |this| this.strikethrough()),
                                     )
                                 } else {
                                     this
@@ -1667,7 +1677,7 @@ impl GitPanel {
                             })
                             .child(
                                 self.entry_label(display_name.clone(), label_color)
-                                    .when(status.is_deleted(), |this| this.strikethrough(true)),
+                                    .when(status.is_deleted(), |this| this.strikethrough()),
                             ),
                     ),
             )

crates/git_ui/src/project_diff.rs πŸ”—

@@ -407,7 +407,7 @@ impl Item for ProjectDiff {
     }
 
     fn telemetry_event_text(&self) -> Option<&'static str> {
-        Some("project diagnostics")
+        Some("Project Diff Opened")
     }
 
     fn as_searchable(&self, _: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {

crates/image_viewer/src/image_viewer.rs πŸ”—

@@ -131,7 +131,7 @@ impl Item for ImageView {
         Label::new(title)
             .single_line()
             .color(label_color)
-            .italic(params.preview)
+            .when(params.preview, |this| this.italic())
             .into_any_element()
     }
 

crates/inline_completion_button/src/inline_completion_button.rs πŸ”—

@@ -551,9 +551,9 @@ impl InlineCompletionButton {
             );
         }
 
-        let is_eager_preview_enabled = match settings.inline_completions_preview_mode() {
-            language::InlineCompletionPreviewMode::Auto => true,
-            language::InlineCompletionPreviewMode::WhenHoldingModifier => false,
+        let is_eager_preview_enabled = match settings.edit_predictions_mode() {
+            language::EditPredictionsMode::Auto => false,
+            language::EditPredictionsMode::EagerPreview => true,
         };
         menu = menu.separator().toggleable_entry(
             "Eager Preview",
@@ -567,17 +567,17 @@ impl InlineCompletionButton {
                         fs.clone(),
                         cx,
                         move |settings, _cx| {
-                            let inline_preview = match is_eager_preview_enabled {
-                                true => language::InlineCompletionPreviewMode::WhenHoldingModifier,
-                                false => language::InlineCompletionPreviewMode::Auto,
+                            let new_mode = match is_eager_preview_enabled {
+                                true => language::EditPredictionsMode::Auto,
+                                false => language::EditPredictionsMode::EagerPreview,
                             };
 
                             if let Some(edit_predictions) = settings.edit_predictions.as_mut() {
-                                edit_predictions.inline_preview = inline_preview;
+                                edit_predictions.mode = new_mode;
                             } else {
                                 settings.edit_predictions =
                                     Some(language_settings::EditPredictionSettingsContent {
-                                        inline_preview,
+                                        mode: new_mode,
                                         ..Default::default()
                                     });
                             }

crates/language/src/language.rs πŸ”—

@@ -21,7 +21,7 @@ mod toolchain;
 pub mod buffer_tests;
 pub mod markdown;
 
-pub use crate::language_settings::InlineCompletionPreviewMode;
+pub use crate::language_settings::EditPredictionsMode;
 use crate::language_settings::SoftWrap;
 use anyhow::{anyhow, Context as _, Result};
 use async_trait::async_trait;

crates/language/src/language_settings.rs πŸ”—

@@ -227,19 +227,20 @@ pub struct EditPredictionSettings {
     /// This list adds to a pre-existing, sensible default set of globs.
     /// Any additional ones you add are combined with them.
     pub disabled_globs: Vec<GlobMatcher>,
-    /// When to show edit predictions previews in buffer.
-    pub inline_preview: InlineCompletionPreviewMode,
+    /// Configures how edit predictions are displayed in the buffer.
+    pub mode: EditPredictionsMode,
 }
 
 /// The mode in which edit predictions should be displayed.
 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
 #[serde(rename_all = "snake_case")]
-pub enum InlineCompletionPreviewMode {
-    /// Display inline when there are no language server completions available.
+pub enum EditPredictionsMode {
+    /// If provider supports it, display inline when holding modifier key (e.g., alt).
+    /// Otherwise, eager preview is used.
     #[default]
     Auto,
-    /// Display inline when holding modifier key (alt by default).
-    WhenHoldingModifier,
+    /// Display inline when there are no language server completions available.
+    EagerPreview,
 }
 
 /// The settings for all languages.
@@ -434,9 +435,10 @@ pub struct EditPredictionSettingsContent {
     /// Any additional ones you add are combined with them.
     #[serde(default)]
     pub disabled_globs: Option<Vec<String>>,
-    /// When to show edit predictions previews in buffer.
+    /// The mode used to display edit predictions in the buffer.
+    /// Provider support required.
     #[serde(default)]
-    pub inline_preview: InlineCompletionPreviewMode,
+    pub mode: EditPredictionsMode,
 }
 
 /// The settings for enabling/disabling features.
@@ -923,8 +925,8 @@ impl AllLanguageSettings {
     }
 
     /// Returns the edit predictions preview mode for the given language and path.
-    pub fn inline_completions_preview_mode(&self) -> InlineCompletionPreviewMode {
-        self.edit_predictions.inline_preview
+    pub fn edit_predictions_mode(&self) -> EditPredictionsMode {
+        self.edit_predictions.mode
     }
 }
 
@@ -1023,10 +1025,10 @@ impl settings::Settings for AllLanguageSettings {
             .features
             .as_ref()
             .and_then(|f| f.edit_prediction_provider);
-        let mut inline_completions_preview = default_value
+        let mut edit_predictions_mode = default_value
             .edit_predictions
             .as_ref()
-            .map(|inline_completions| inline_completions.inline_preview)
+            .map(|edit_predictions| edit_predictions.mode)
             .ok_or_else(Self::missing_default)?;
 
         let mut completion_globs: HashSet<&String> = default_value
@@ -1060,10 +1062,10 @@ impl settings::Settings for AllLanguageSettings {
                 edit_prediction_provider = Some(provider);
             }
 
-            if let Some(inline_completions) = user_settings.edit_predictions.as_ref() {
-                inline_completions_preview = inline_completions.inline_preview;
+            if let Some(edit_predictions) = user_settings.edit_predictions.as_ref() {
+                edit_predictions_mode = edit_predictions.mode;
 
-                if let Some(disabled_globs) = inline_completions.disabled_globs.as_ref() {
+                if let Some(disabled_globs) = edit_predictions.disabled_globs.as_ref() {
                     completion_globs.extend(disabled_globs.iter());
                 }
             }
@@ -1118,7 +1120,7 @@ impl settings::Settings for AllLanguageSettings {
                     .iter()
                     .filter_map(|g| Some(globset::Glob::new(g).ok()?.compile_matcher()))
                     .collect(),
-                inline_preview: inline_completions_preview,
+                mode: edit_predictions_mode,
             },
             defaults,
             languages,

crates/migrator/Cargo.toml πŸ”—

@@ -13,6 +13,7 @@ path = "src/migrator.rs"
 doctest = false
 
 [dependencies]
+anyhow.workspace = true
 collections.workspace = true
 convert_case.workspace = true
 log.workspace = true

crates/migrator/src/migrator.rs πŸ”—

@@ -1,15 +1,16 @@
+use anyhow::{Context, Result};
 use collections::HashMap;
 use convert_case::{Case, Casing};
 use std::{cmp::Reverse, ops::Range, sync::LazyLock};
 use streaming_iterator::StreamingIterator;
 use tree_sitter::{Query, QueryMatch};
 
-fn migrate(text: &str, patterns: MigrationPatterns, query: &Query) -> Option<String> {
+fn migrate(text: &str, patterns: MigrationPatterns, query: &Query) -> Result<Option<String>> {
     let mut parser = tree_sitter::Parser::new();
-    parser
-        .set_language(&tree_sitter_json::LANGUAGE.into())
-        .unwrap();
-    let syntax_tree = parser.parse(&text, None).unwrap();
+    parser.set_language(&tree_sitter_json::LANGUAGE.into())?;
+    let syntax_tree = parser
+        .parse(&text, None)
+        .context("failed to parse settings")?;
 
     let mut cursor = tree_sitter::QueryCursor::new();
     let mut matches = cursor.matches(query, syntax_tree.root_node(), text.as_bytes());
@@ -27,7 +28,7 @@ fn migrate(text: &str, patterns: MigrationPatterns, query: &Query) -> Option<Str
     });
 
     if edits.is_empty() {
-        None
+        Ok(None)
     } else {
         let mut new_text = text.to_string();
         for (range, replacement) in edits.iter().rev() {
@@ -38,28 +39,28 @@ fn migrate(text: &str, patterns: MigrationPatterns, query: &Query) -> Option<Str
                 "Edits computed for configuration migration do not cause a change: {:?}",
                 edits
             );
-            None
+            Ok(None)
         } else {
-            Some(new_text)
+            Ok(Some(new_text))
         }
     }
 }
 
-pub fn migrate_keymap(text: &str) -> Option<String> {
+pub fn migrate_keymap(text: &str) -> Result<Option<String>> {
     let transformed_text = migrate(
         text,
         KEYMAP_MIGRATION_TRANSFORMATION_PATTERNS,
         &KEYMAP_MIGRATION_TRANSFORMATION_QUERY,
-    );
+    )?;
     let replacement_text = migrate(
         &transformed_text.as_ref().unwrap_or(&text.to_string()),
         KEYMAP_MIGRATION_REPLACEMENT_PATTERNS,
         &KEYMAP_MIGRATION_REPLACEMENT_QUERY,
-    );
-    replacement_text.or(transformed_text)
+    )?;
+    Ok(replacement_text.or(transformed_text))
 }
 
-pub fn migrate_settings(text: &str) -> Option<String> {
+pub fn migrate_settings(text: &str) -> Result<Option<String>> {
     migrate(
         &text,
         SETTINGS_MIGRATION_PATTERNS,
@@ -72,7 +73,7 @@ type MigrationPatterns = &'static [(
     fn(&str, &QueryMatch, &Query) -> Option<(Range<usize>, String)>,
 )];
 
-static KEYMAP_MIGRATION_TRANSFORMATION_PATTERNS: MigrationPatterns = &[
+const KEYMAP_MIGRATION_TRANSFORMATION_PATTERNS: MigrationPatterns = &[
     (ACTION_ARRAY_PATTERN, replace_array_with_single_string),
     (
         ACTION_ARGUMENT_OBJECT_PATTERN,
@@ -120,9 +121,9 @@ fn replace_array_with_single_string(
     mat: &QueryMatch,
     query: &Query,
 ) -> Option<(Range<usize>, String)> {
-    let array_ix = query.capture_index_for_name("array").unwrap();
-    let action_name_ix = query.capture_index_for_name("action_name").unwrap();
-    let argument_ix = query.capture_index_for_name("argument").unwrap();
+    let array_ix = query.capture_index_for_name("array")?;
+    let action_name_ix = query.capture_index_for_name("action_name")?;
+    let argument_ix = query.capture_index_for_name("argument")?;
 
     let action_name = contents.get(
         mat.nodes_for_capture_index(action_name_ix)
@@ -142,50 +143,109 @@ fn replace_array_with_single_string(
     Some((range_to_replace, replacement_as_string))
 }
 
-#[rustfmt::skip]
 static TRANSFORM_ARRAY: LazyLock<HashMap<(&str, &str), &str>> = LazyLock::new(|| {
     HashMap::from_iter([
         // activate
-        (("workspace::ActivatePaneInDirection", "Up"), "workspace::ActivatePaneUp"),
-        (("workspace::ActivatePaneInDirection", "Down"), "workspace::ActivatePaneDown"),
-        (("workspace::ActivatePaneInDirection", "Left"), "workspace::ActivatePaneLeft"),
-        (("workspace::ActivatePaneInDirection", "Right"), "workspace::ActivatePaneRight"),
+        (
+            ("workspace::ActivatePaneInDirection", "Up"),
+            "workspace::ActivatePaneUp",
+        ),
+        (
+            ("workspace::ActivatePaneInDirection", "Down"),
+            "workspace::ActivatePaneDown",
+        ),
+        (
+            ("workspace::ActivatePaneInDirection", "Left"),
+            "workspace::ActivatePaneLeft",
+        ),
+        (
+            ("workspace::ActivatePaneInDirection", "Right"),
+            "workspace::ActivatePaneRight",
+        ),
         // swap
-        (("workspace::SwapPaneInDirection", "Up"), "workspace::SwapPaneUp"),
-        (("workspace::SwapPaneInDirection", "Down"), "workspace::SwapPaneDown"),
-        (("workspace::SwapPaneInDirection", "Left"), "workspace::SwapPaneLeft"),
-        (("workspace::SwapPaneInDirection", "Right"), "workspace::SwapPaneRight"),
+        (
+            ("workspace::SwapPaneInDirection", "Up"),
+            "workspace::SwapPaneUp",
+        ),
+        (
+            ("workspace::SwapPaneInDirection", "Down"),
+            "workspace::SwapPaneDown",
+        ),
+        (
+            ("workspace::SwapPaneInDirection", "Left"),
+            "workspace::SwapPaneLeft",
+        ),
+        (
+            ("workspace::SwapPaneInDirection", "Right"),
+            "workspace::SwapPaneRight",
+        ),
         // menu
-        (("app_menu::NavigateApplicationMenuInDirection", "Left"), "app_menu::ActivateMenuLeft"),
-        (("app_menu::NavigateApplicationMenuInDirection", "Right"), "app_menu::ActivateMenuRight"),
+        (
+            ("app_menu::NavigateApplicationMenuInDirection", "Left"),
+            "app_menu::ActivateMenuLeft",
+        ),
+        (
+            ("app_menu::NavigateApplicationMenuInDirection", "Right"),
+            "app_menu::ActivateMenuRight",
+        ),
         // vim push
         (("vim::PushOperator", "Change"), "vim::PushChange"),
         (("vim::PushOperator", "Delete"), "vim::PushDelete"),
         (("vim::PushOperator", "Yank"), "vim::PushYank"),
         (("vim::PushOperator", "Replace"), "vim::PushReplace"),
-        (("vim::PushOperator", "DeleteSurrounds"), "vim::PushDeleteSurrounds"),
+        (
+            ("vim::PushOperator", "DeleteSurrounds"),
+            "vim::PushDeleteSurrounds",
+        ),
         (("vim::PushOperator", "Mark"), "vim::PushMark"),
         (("vim::PushOperator", "Indent"), "vim::PushIndent"),
         (("vim::PushOperator", "Outdent"), "vim::PushOutdent"),
         (("vim::PushOperator", "AutoIndent"), "vim::PushAutoIndent"),
         (("vim::PushOperator", "Rewrap"), "vim::PushRewrap"),
-        (("vim::PushOperator", "ShellCommand"), "vim::PushShellCommand"),
+        (
+            ("vim::PushOperator", "ShellCommand"),
+            "vim::PushShellCommand",
+        ),
         (("vim::PushOperator", "Lowercase"), "vim::PushLowercase"),
         (("vim::PushOperator", "Uppercase"), "vim::PushUppercase"),
-        (("vim::PushOperator", "OppositeCase"), "vim::PushOppositeCase"),
+        (
+            ("vim::PushOperator", "OppositeCase"),
+            "vim::PushOppositeCase",
+        ),
         (("vim::PushOperator", "Register"), "vim::PushRegister"),
-        (("vim::PushOperator", "RecordRegister"), "vim::PushRecordRegister"),
-        (("vim::PushOperator", "ReplayRegister"), "vim::PushReplayRegister"),
-        (("vim::PushOperator", "ReplaceWithRegister"), "vim::PushReplaceWithRegister"),
-        (("vim::PushOperator", "ToggleComments"), "vim::PushToggleComments"),
+        (
+            ("vim::PushOperator", "RecordRegister"),
+            "vim::PushRecordRegister",
+        ),
+        (
+            ("vim::PushOperator", "ReplayRegister"),
+            "vim::PushReplayRegister",
+        ),
+        (
+            ("vim::PushOperator", "ReplaceWithRegister"),
+            "vim::PushReplaceWithRegister",
+        ),
+        (
+            ("vim::PushOperator", "ToggleComments"),
+            "vim::PushToggleComments",
+        ),
         // vim switch
         (("vim::SwitchMode", "Normal"), "vim::SwitchToNormalMode"),
         (("vim::SwitchMode", "Insert"), "vim::SwitchToInsertMode"),
         (("vim::SwitchMode", "Replace"), "vim::SwitchToReplaceMode"),
         (("vim::SwitchMode", "Visual"), "vim::SwitchToVisualMode"),
-        (("vim::SwitchMode", "VisualLine"), "vim::SwitchToVisualLineMode"),
-        (("vim::SwitchMode", "VisualBlock"), "vim::SwitchToVisualBlockMode"),
-        (("vim::SwitchMode", "HelixNormal"), "vim::SwitchToHelixNormalMode"),
+        (
+            ("vim::SwitchMode", "VisualLine"),
+            "vim::SwitchToVisualLineMode",
+        ),
+        (
+            ("vim::SwitchMode", "VisualBlock"),
+            "vim::SwitchToVisualBlockMode",
+        ),
+        (
+            ("vim::SwitchMode", "HelixNormal"),
+            "vim::SwitchToHelixNormalMode",
+        ),
         // vim resize
         (("vim::ResizePane", "Widen"), "vim::ResizePaneRight"),
         (("vim::ResizePane", "Narrow"), "vim::ResizePaneLeft"),
@@ -225,10 +285,10 @@ fn replace_action_argument_object_with_single_value(
     mat: &QueryMatch,
     query: &Query,
 ) -> Option<(Range<usize>, String)> {
-    let array_ix = query.capture_index_for_name("array").unwrap();
-    let action_name_ix = query.capture_index_for_name("action_name").unwrap();
-    let action_key_ix = query.capture_index_for_name("action_key").unwrap();
-    let argument_ix = query.capture_index_for_name("argument").unwrap();
+    let array_ix = query.capture_index_for_name("array")?;
+    let action_name_ix = query.capture_index_for_name("action_name")?;
+    let action_key_ix = query.capture_index_for_name("action_key")?;
+    let argument_ix = query.capture_index_for_name("argument")?;
 
     let action_name = contents.get(
         mat.nodes_for_capture_index(action_name_ix)
@@ -253,7 +313,7 @@ fn replace_action_argument_object_with_single_value(
     Some((range_to_replace, replacement))
 }
 
-// "ctrl-k ctrl-1": [ "editor::PushOperator", { "Object": {} } ] -> [ "editor::vim::PushObject", {} ]
+/// "ctrl-k ctrl-1": [ "editor::PushOperator", { "Object": {} } ] -> [ "editor::vim::PushObject", {} ]
 static UNWRAP_OBJECTS: LazyLock<HashMap<&str, HashMap<&str, &str>>> = LazyLock::new(|| {
     HashMap::from_iter([
         (
@@ -278,7 +338,7 @@ static UNWRAP_OBJECTS: LazyLock<HashMap<&str, HashMap<&str, &str>>> = LazyLock::
     ])
 });
 
-static KEYMAP_MIGRATION_REPLACEMENT_PATTERNS: MigrationPatterns = &[(
+const KEYMAP_MIGRATION_REPLACEMENT_PATTERNS: MigrationPatterns = &[(
     ACTION_ARGUMENT_SNAKE_CASE_PATTERN,
     action_argument_snake_case,
 )];
@@ -318,7 +378,7 @@ fn rename_string_action(
     mat: &QueryMatch,
     query: &Query,
 ) -> Option<(Range<usize>, String)> {
-    let action_name_ix = query.capture_index_for_name("action_name").unwrap();
+    let action_name_ix = query.capture_index_for_name("action_name")?;
     let action_name_range = mat
         .nodes_for_capture_index(action_name_ix)
         .next()?
@@ -328,17 +388,31 @@ fn rename_string_action(
     Some((action_name_range, new_action_name.to_string()))
 }
 
-// "ctrl-k ctrl-1": "inline_completion::ToggleMenu" -> "edit_prediction::ToggleMenu"
-#[rustfmt::skip]
+/// "ctrl-k ctrl-1": "inline_completion::ToggleMenu" -> "edit_prediction::ToggleMenu"
 static STRING_REPLACE: LazyLock<HashMap<&str, &str>> = LazyLock::new(|| {
     HashMap::from_iter([
-        ("inline_completion::ToggleMenu", "edit_prediction::ToggleMenu"),
+        (
+            "inline_completion::ToggleMenu",
+            "edit_prediction::ToggleMenu",
+        ),
         ("editor::NextInlineCompletion", "editor::NextEditPrediction"),
-        ("editor::PreviousInlineCompletion", "editor::PreviousEditPrediction"),
-        ("editor::AcceptPartialInlineCompletion", "editor::AcceptPartialEditPrediction"),
+        (
+            "editor::PreviousInlineCompletion",
+            "editor::PreviousEditPrediction",
+        ),
+        (
+            "editor::AcceptPartialInlineCompletion",
+            "editor::AcceptPartialEditPrediction",
+        ),
         ("editor::ShowInlineCompletion", "editor::ShowEditPrediction"),
-        ("editor::AcceptInlineCompletion", "editor::AcceptEditPrediction"),
-        ("editor::ToggleInlineCompletions", "editor::ToggleEditPrediction"),
+        (
+            "editor::AcceptInlineCompletion",
+            "editor::AcceptEditPrediction",
+        ),
+        (
+            "editor::ToggleInlineCompletions",
+            "editor::ToggleEditPrediction",
+        ),
     ])
 });
 
@@ -359,7 +433,7 @@ fn rename_context_key(
     mat: &QueryMatch,
     query: &Query,
 ) -> Option<(Range<usize>, String)> {
-    let context_predicate_ix = query.capture_index_for_name("context_predicate").unwrap();
+    let context_predicate_ix = query.capture_index_for_name("context_predicate")?;
     let context_predicate_range = mat
         .nodes_for_capture_index(context_predicate_ix)
         .next()?
@@ -415,10 +489,10 @@ fn action_argument_snake_case(
     mat: &QueryMatch,
     query: &Query,
 ) -> Option<(Range<usize>, String)> {
-    let array_ix = query.capture_index_for_name("array").unwrap();
-    let action_name_ix = query.capture_index_for_name("action_name").unwrap();
-    let argument_key_ix = query.capture_index_for_name("argument_key").unwrap();
-    let argument_value_ix = query.capture_index_for_name("argument_value").unwrap();
+    let array_ix = query.capture_index_for_name("array")?;
+    let action_name_ix = query.capture_index_for_name("action_name")?;
+    let argument_key_ix = query.capture_index_for_name("argument_key")?;
+    let argument_value_ix = query.capture_index_for_name("argument_value")?;
     let action_name = contents.get(
         mat.nodes_for_capture_index(action_name_ix)
             .next()?
@@ -463,7 +537,7 @@ fn action_argument_snake_case(
     Some((range_to_replace, replacement))
 }
 
-// "context": "Editor && inline_completion && !showing_completions" -> "Editor && edit_prediction && !showing_completions"
+/// "context": "Editor && inline_completion && !showing_completions" -> "Editor && edit_prediction && !showing_completions"
 pub static CONTEXT_REPLACE: LazyLock<HashMap<&str, &str>> = LazyLock::new(|| {
     HashMap::from_iter([
         ("inline_completion", "edit_prediction"),
@@ -474,7 +548,7 @@ pub static CONTEXT_REPLACE: LazyLock<HashMap<&str, &str>> = LazyLock::new(|| {
     ])
 });
 
-static SETTINGS_MIGRATION_PATTERNS: MigrationPatterns = &[
+const SETTINGS_MIGRATION_PATTERNS: MigrationPatterns = &[
     (SETTINGS_STRING_REPLACE_QUERY, replace_setting_name),
     (SETTINGS_REPLACE_NESTED_KEY, replace_setting_nested_key),
     (
@@ -494,7 +568,7 @@ static SETTINGS_MIGRATION_QUERY: LazyLock<Query> = LazyLock::new(|| {
     .unwrap()
 });
 
-static SETTINGS_STRING_REPLACE_QUERY: &str = r#"(document
+const SETTINGS_STRING_REPLACE_QUERY: &str = r#"(document
     (object
         (pair
             key: (string (string_content) @name)
@@ -508,7 +582,7 @@ fn replace_setting_name(
     mat: &QueryMatch,
     query: &Query,
 ) -> Option<(Range<usize>, String)> {
-    let setting_capture_ix = query.capture_index_for_name("name").unwrap();
+    let setting_capture_ix = query.capture_index_for_name("name")?;
     let setting_name_range = mat
         .nodes_for_capture_index(setting_capture_ix)
         .next()?
@@ -518,17 +592,23 @@ fn replace_setting_name(
     Some((setting_name_range, new_setting_name.to_string()))
 }
 
-#[rustfmt::skip]
-pub static SETTINGS_STRING_REPLACE: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
-    HashMap::from_iter([
-        ("show_inline_completions_in_menu", "show_edit_predictions_in_menu"),
-        ("show_inline_completions", "show_edit_predictions"),
-        ("inline_completions_disabled_in", "edit_predictions_disabled_in"),
-        ("inline_completions", "edit_predictions")
-    ])
-});
+pub static SETTINGS_STRING_REPLACE: LazyLock<HashMap<&'static str, &'static str>> =
+    LazyLock::new(|| {
+        HashMap::from_iter([
+            (
+                "show_inline_completions_in_menu",
+                "show_edit_predictions_in_menu",
+            ),
+            ("show_inline_completions", "show_edit_predictions"),
+            (
+                "inline_completions_disabled_in",
+                "edit_predictions_disabled_in",
+            ),
+            ("inline_completions", "edit_predictions"),
+        ])
+    });
 
-static SETTINGS_REPLACE_NESTED_KEY: &str = r#"
+const SETTINGS_REPLACE_NESTED_KEY: &str = r#"
 (object
   (pair
     key: (string (string_content) @parent_key)
@@ -547,14 +627,14 @@ fn replace_setting_nested_key(
     mat: &QueryMatch,
     query: &Query,
 ) -> Option<(Range<usize>, String)> {
-    let parent_object_capture_ix = query.capture_index_for_name("parent_key").unwrap();
+    let parent_object_capture_ix = query.capture_index_for_name("parent_key")?;
     let parent_object_range = mat
         .nodes_for_capture_index(parent_object_capture_ix)
         .next()?
         .byte_range();
     let parent_object_name = contents.get(parent_object_range.clone())?;
 
-    let setting_name_ix = query.capture_index_for_name("setting_name").unwrap();
+    let setting_name_ix = query.capture_index_for_name("setting_name")?;
     let setting_range = mat
         .nodes_for_capture_index(setting_name_ix)
         .next()?
@@ -568,9 +648,11 @@ fn replace_setting_nested_key(
     Some((setting_range, new_setting_name.to_string()))
 }
 
-// "features": {
-//   "inline_completion_provider": "copilot"
-// },
+/// ```json
+/// "features": {
+///   "inline_completion_provider": "copilot"
+/// },
+/// ```
 pub static SETTINGS_NESTED_STRING_REPLACE: LazyLock<
     HashMap<&'static str, HashMap<&'static str, &'static str>>,
 > = LazyLock::new(|| {
@@ -580,7 +662,7 @@ pub static SETTINGS_NESTED_STRING_REPLACE: LazyLock<
     )])
 });
 
-static SETTINGS_REPLACE_IN_LANGUAGES_QUERY: &str = r#"
+const SETTINGS_REPLACE_IN_LANGUAGES_QUERY: &str = r#"
 (object
   (pair
     key: (string (string_content) @languages)
@@ -604,7 +686,7 @@ fn replace_setting_in_languages(
     mat: &QueryMatch,
     query: &Query,
 ) -> Option<(Range<usize>, String)> {
-    let setting_capture_ix = query.capture_index_for_name("setting_name").unwrap();
+    let setting_capture_ix = query.capture_index_for_name("setting_name")?;
     let setting_name_range = mat
         .nodes_for_capture_index(setting_capture_ix)
         .next()?
@@ -615,27 +697,28 @@ fn replace_setting_in_languages(
     Some((setting_name_range, new_setting_name.to_string()))
 }
 
-#[rustfmt::skip]
-static LANGUAGE_SETTINGS_REPLACE: LazyLock<
-    HashMap<&'static str, &'static str>,
-> = LazyLock::new(|| {
-    HashMap::from_iter([
-        ("show_inline_completions", "show_edit_predictions"),
-        ("inline_completions_disabled_in", "edit_predictions_disabled_in"),
-    ])
-});
+static LANGUAGE_SETTINGS_REPLACE: LazyLock<HashMap<&'static str, &'static str>> =
+    LazyLock::new(|| {
+        HashMap::from_iter([
+            ("show_inline_completions", "show_edit_predictions"),
+            (
+                "inline_completions_disabled_in",
+                "edit_predictions_disabled_in",
+            ),
+        ])
+    });
 
 #[cfg(test)]
 mod tests {
     use super::*;
 
     fn assert_migrate_keymap(input: &str, output: Option<&str>) {
-        let migrated = migrate_keymap(&input);
+        let migrated = migrate_keymap(&input).unwrap();
         pretty_assertions::assert_eq!(migrated.as_deref(), output);
     }
 
     fn assert_migrate_settings(input: &str, output: Option<&str>) {
-        let migrated = migrate_settings(&input);
+        let migrated = migrate_settings(&input).unwrap();
         pretty_assertions::assert_eq!(migrated.as_deref(), output);
     }
 

crates/repl/src/notebook/notebook_ui.rs πŸ”—

@@ -738,7 +738,7 @@ impl Item for NotebookEditor {
         Label::new(title)
             .single_line()
             .color(params.text_color())
-            .italic(params.preview)
+            .when(params.preview, |this| this.italic())
             .into_any_element()
     }
 

crates/storybook/src/story_selector.rs πŸ”—

@@ -25,7 +25,6 @@ pub enum ComponentStory {
     Icon,
     IconButton,
     Keybinding,
-    Label,
     List,
     ListHeader,
     ListItem,
@@ -61,7 +60,6 @@ impl ComponentStory {
             Self::Icon => cx.new(|_| ui::IconStory).into(),
             Self::IconButton => cx.new(|_| ui::IconButtonStory).into(),
             Self::Keybinding => cx.new(|_| ui::KeybindingStory).into(),
-            Self::Label => cx.new(|_| ui::LabelStory).into(),
             Self::List => cx.new(|_| ui::ListStory).into(),
             Self::ListHeader => cx.new(|_| ui::ListHeaderStory).into(),
             Self::ListItem => cx.new(|_| ui::ListItemStory).into(),

crates/ui/src/components/avatar/avatar.rs πŸ”—

@@ -97,6 +97,7 @@ impl RenderOnce for Avatar {
     }
 }
 
+// View this component preview using `workspace: open component-preview`
 impl ComponentPreview for Avatar {
     fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
         let example_avatar = "https://avatars.githubusercontent.com/u/1714999?v=4";

crates/ui/src/components/button/button.rs πŸ”—

@@ -456,6 +456,7 @@ impl RenderOnce for Button {
     }
 }
 
+// View this component preview using `workspace: open component-preview`
 impl ComponentPreview for Button {
     fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
         v_flex()

crates/ui/src/components/content_group.rs πŸ”—

@@ -88,6 +88,7 @@ impl RenderOnce for ContentGroup {
     }
 }
 
+// View this component preview using `workspace: open component-preview`
 impl ComponentPreview for ContentGroup {
     fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
         example_group(vec![

crates/ui/src/components/icon.rs πŸ”—

@@ -490,6 +490,7 @@ impl RenderOnce for IconWithIndicator {
     }
 }
 
+// View this component preview using `workspace: open component-preview`
 impl ComponentPreview for Icon {
     fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
         v_flex()

crates/ui/src/components/icon/decorated_icon.rs πŸ”—

@@ -24,6 +24,7 @@ impl RenderOnce for DecoratedIcon {
     }
 }
 
+// View this component preview using `workspace: open component-preview`
 impl ComponentPreview for DecoratedIcon {
     fn preview(_window: &mut Window, cx: &App) -> AnyElement {
         let decoration_x = IconDecoration::new(

crates/ui/src/components/keybinding_hint.rs πŸ”—

@@ -205,6 +205,7 @@ impl RenderOnce for KeybindingHint {
     }
 }
 
+// View this component preview using `workspace: open component-preview`
 impl ComponentPreview for KeybindingHint {
     fn preview(window: &mut Window, _cx: &App) -> AnyElement {
         let enter_fallback = gpui::KeyBinding::new("enter", menu::Confirm, None);

crates/ui/src/components/label/highlighted_label.rs πŸ”—

@@ -46,13 +46,13 @@ impl LabelCommon for HighlightedLabel {
         self
     }
 
-    fn strikethrough(mut self, strikethrough: bool) -> Self {
-        self.base = self.base.strikethrough(strikethrough);
+    fn strikethrough(mut self) -> Self {
+        self.base = self.base.strikethrough();
         self
     }
 
-    fn italic(mut self, italic: bool) -> Self {
-        self.base = self.base.italic(italic);
+    fn italic(mut self) -> Self {
+        self.base = self.base.italic();
         self
     }
 
@@ -61,8 +61,8 @@ impl LabelCommon for HighlightedLabel {
         self
     }
 
-    fn underline(mut self, underline: bool) -> Self {
-        self.base = self.base.underline(underline);
+    fn underline(mut self) -> Self {
+        self.base = self.base.underline();
         self
     }
 

crates/ui/src/components/label/label.rs πŸ”—

@@ -1,8 +1,5 @@
-#![allow(missing_docs)]
-
-use gpui::{AnyElement, App, StyleRefinement, Window};
-
-use crate::{prelude::*, LabelCommon, LabelLike, LabelSize, LineHeightStyle};
+use crate::{prelude::*, LabelLike};
+use gpui::StyleRefinement;
 
 /// A struct representing a label element in the UI.
 ///
@@ -56,6 +53,9 @@ impl Label {
     }
 }
 
+// nate: If we are going to do this, we might as well just
+// impl Styled for Label and not constrain styles
+
 // Style methods.
 impl Label {
     fn style(&mut self) -> &mut StyleRefinement {
@@ -82,6 +82,15 @@ impl LabelCommon for Label {
         self
     }
 
+    /// Sets the weight of the label using a [`FontWeight`].
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use ui::prelude::*;
+    ///
+    /// let my_label = Label::new("Hello, World!").weight(FontWeight::Bold);
+    /// ```
     fn weight(mut self, weight: gpui::FontWeight) -> Self {
         self.base = self.base.weight(weight);
         self
@@ -124,8 +133,8 @@ impl LabelCommon for Label {
     ///
     /// let my_label = Label::new("Hello, World!").strikethrough(true);
     /// ```
-    fn strikethrough(mut self, strikethrough: bool) -> Self {
-        self.base = self.base.strikethrough(strikethrough);
+    fn strikethrough(mut self) -> Self {
+        self.base = self.base.strikethrough();
         self
     }
 
@@ -138,8 +147,8 @@ impl LabelCommon for Label {
     ///
     /// let my_label = Label::new("Hello, World!").italic(true);
     /// ```
-    fn italic(mut self, italic: bool) -> Self {
-        self.base = self.base.italic(italic);
+    fn italic(mut self) -> Self {
+        self.base = self.base.italic();
         self
     }
 
@@ -157,8 +166,8 @@ impl LabelCommon for Label {
         self
     }
 
-    fn underline(mut self, underline: bool) -> Self {
-        self.base = self.base.underline(underline);
+    fn underline(mut self) -> Self {
+        self.base = self.base.underline();
         self
     }
 
@@ -185,52 +194,57 @@ impl RenderOnce for Label {
     }
 }
 
-impl ComponentPreview for Label {
-    fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
-        v_flex()
-            .gap_6()
-            .children(vec![
-                example_group_with_title(
-                    "Sizes",
-                    vec![
-                        single_example("Default", Label::new("Default Label").into_any_element()),
-                        single_example("Small", Label::new("Small Label").size(LabelSize::Small).into_any_element()),
-                        single_example("Large", Label::new("Large Label").size(LabelSize::Large).into_any_element()),
-                    ],
-                ),
-                example_group_with_title(
-                    "Colors",
-                    vec![
-                        single_example("Default", Label::new("Default Color").into_any_element()),
-                        single_example("Accent", Label::new("Accent Color").color(Color::Accent).into_any_element()),
-                        single_example("Error", Label::new("Error Color").color(Color::Error).into_any_element()),
-                    ],
-                ),
-                example_group_with_title(
-                    "Styles",
-                    vec![
-                        single_example("Default", Label::new("Default Style").into_any_element()),
-                        single_example("Bold", Label::new("Bold Style").weight(gpui::FontWeight::BOLD).into_any_element()),
-                        single_example("Italic", Label::new("Italic Style").italic(true).into_any_element()),
-                        single_example("Strikethrough", Label::new("Strikethrough Style").strikethrough(true).into_any_element()),
-                        single_example("Underline", Label::new("Underline Style").underline(true).into_any_element()),
-                    ],
-                ),
-                example_group_with_title(
-                    "Line Height Styles",
-                    vec![
-                        single_example("Default", Label::new("Default Line Height").into_any_element()),
-                        single_example("UI Label", Label::new("UI Label Line Height").line_height_style(LineHeightStyle::UiLabel).into_any_element()),
-                    ],
-                ),
-                example_group_with_title(
-                    "Special Cases",
-                    vec![
-                        single_example("Single Line", Label::new("Single\nLine\nText").single_line().into_any_element()),
-                        single_example("Text Ellipsis", Label::new("This is a very long text that should be truncated with an ellipsis").text_ellipsis().into_any_element()),
-                    ],
-                ),
-            ])
-            .into_any_element()
+mod label_preview {
+    use crate::prelude::*;
+
+    // View this component preview using `workspace: open component-preview`
+    impl ComponentPreview for Label {
+        fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+            v_flex()
+                .gap_6()
+                .children(vec![
+                    example_group_with_title(
+                        "Sizes",
+                        vec![
+                            single_example("Default", Label::new("Project Explorer").into_any_element()),
+                            single_example("Small", Label::new("File: main.rs").size(LabelSize::Small).into_any_element()),
+                            single_example("Large", Label::new("Welcome to Zed").size(LabelSize::Large).into_any_element()),
+                        ],
+                    ),
+                    example_group_with_title(
+                        "Colors",
+                        vec![
+                            single_example("Default", Label::new("Status: Ready").into_any_element()),
+                            single_example("Accent", Label::new("New Update Available").color(Color::Accent).into_any_element()),
+                            single_example("Error", Label::new("Build Failed").color(Color::Error).into_any_element()),
+                        ],
+                    ),
+                    example_group_with_title(
+                        "Styles",
+                        vec![
+                            single_example("Default", Label::new("Normal Text").into_any_element()),
+                            single_example("Bold", Label::new("Important Notice").weight(gpui::FontWeight::BOLD).into_any_element()),
+                            single_example("Italic", Label::new("Code Comment").italic().into_any_element()),
+                            single_example("Strikethrough", Label::new("Deprecated Feature").strikethrough().into_any_element()),
+                            single_example("Underline", Label::new("Clickable Link").underline().into_any_element()),
+                        ],
+                    ),
+                    example_group_with_title(
+                        "Line Height Styles",
+                        vec![
+                            single_example("Default", Label::new("Multi-line\nText\nExample").into_any_element()),
+                            single_example("UI Label", Label::new("Compact\nUI\nLabel").line_height_style(LineHeightStyle::UiLabel).into_any_element()),
+                        ],
+                    ),
+                    example_group_with_title(
+                        "Special Cases",
+                        vec![
+                            single_example("Single Line", Label::new("Line 1\nLine 2\nLine 3").single_line().into_any_element()),
+                            single_example("Text Ellipsis", div().max_w_24().child(Label::new("This is a very long file name that should be truncated: very_long_file_name_with_many_words.rs").text_ellipsis()).into_any_element()),
+                        ],
+                    ),
+                ])
+                .into_any_element()
+        }
     }
 }

crates/ui/src/components/label/label_like.rs πŸ”—

@@ -1,23 +1,29 @@
-#![allow(missing_docs)]
-
-use gpui::{relative, AnyElement, FontWeight, StyleRefinement, Styled, UnderlineStyle};
+use crate::prelude::*;
+use gpui::{FontWeight, StyleRefinement, UnderlineStyle};
 use settings::Settings;
 use smallvec::SmallVec;
 use theme::ThemeSettings;
 
-use crate::prelude::*;
-
+/// Sets the size of a label
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
 pub enum LabelSize {
+    /// The default size of a label.
     #[default]
     Default,
+    /// The large size of a label.
     Large,
+    /// The small size of a label.
     Small,
+    /// The extra small size of a label.
     XSmall,
 }
 
+/// Sets the line height of a label
 #[derive(Default, PartialEq, Copy, Clone)]
 pub enum LineHeightStyle {
+    /// The default line height style of a label,
+    /// set by either the UI's default line height,
+    /// or the developer's default buffer line height.
     #[default]
     TextLabel,
     /// Sets the line height to 1.
@@ -39,13 +45,13 @@ pub trait LabelCommon {
     fn color(self, color: Color) -> Self;
 
     /// Sets the strikethrough property of the label.
-    fn strikethrough(self, strikethrough: bool) -> Self;
+    fn strikethrough(self) -> Self;
 
     /// Sets the italic property of the label.
-    fn italic(self, italic: bool) -> Self;
+    fn italic(self) -> Self;
 
     /// Sets the underline property of the label
-    fn underline(self, underline: bool) -> Self;
+    fn underline(self) -> Self;
 
     /// Sets the alpha property of the label, overwriting the alpha value of the color.
     fn alpha(self, alpha: f32) -> Self;
@@ -60,6 +66,11 @@ pub trait LabelCommon {
     fn buffer_font(self, cx: &App) -> Self;
 }
 
+/// A label-like element that can be used to create a custom label when
+/// prebuilt labels are not sufficient. Use this sparingly, as it is
+/// unconstrained and may make the UI feel less consistent.
+///
+/// This is also used to build the prebuilt labels.
 #[derive(IntoElement)]
 pub struct LabelLike {
     pub(super) base: Div,
@@ -83,6 +94,8 @@ impl Default for LabelLike {
 }
 
 impl LabelLike {
+    /// Creates a new, fully custom label.
+    /// Prefer using [`Label`] or [`HighlightedLabel`] where possible.
     pub fn new() -> Self {
         Self {
             base: div(),
@@ -133,18 +146,18 @@ impl LabelCommon for LabelLike {
         self
     }
 
-    fn strikethrough(mut self, strikethrough: bool) -> Self {
-        self.strikethrough = strikethrough;
+    fn strikethrough(mut self) -> Self {
+        self.strikethrough = true;
         self
     }
 
-    fn italic(mut self, italic: bool) -> Self {
-        self.italic = italic;
+    fn italic(mut self) -> Self {
+        self.italic = true;
         self
     }
 
-    fn underline(mut self, underline: bool) -> Self {
-        self.underline = underline;
+    fn underline(mut self) -> Self {
+        self.underline = true;
         self
     }
 

crates/ui/src/components/stories.rs πŸ”—

@@ -8,7 +8,6 @@ mod disclosure;
 mod icon;
 mod icon_button;
 mod keybinding;
-mod label;
 mod list;
 mod list_header;
 mod list_item;
@@ -23,7 +22,6 @@ pub use disclosure::*;
 pub use icon::*;
 pub use icon_button::*;
 pub use keybinding::*;
-pub use label::*;
 pub use list::*;
 pub use list_header::*;
 pub use list_item::*;

crates/ui/src/components/stories/label.rs πŸ”—

@@ -1,38 +0,0 @@
-use std::time::Duration;
-
-use crate::{prelude::*, HighlightedLabel, Label};
-use gpui::{pulsating_between, Animation, AnimationExt, Render};
-use story::Story;
-
-pub struct LabelStory;
-
-impl Render for LabelStory {
-    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
-        Story::container()
-            .child(Story::title_for::<Label>())
-            .child(Story::label("Default"))
-            .child(Label::new("Hello, world!"))
-            .child(Story::label("Highlighted"))
-            .child(HighlightedLabel::new(
-                "Hello, world!",
-                vec![0, 1, 2, 7, 8, 12],
-            ))
-            .child(HighlightedLabel::new(
-                "HΓ©llo, world!",
-                vec![0, 1, 3, 8, 9, 13],
-            ))
-            .child(Story::label("Highlighted with `color`"))
-            .child(
-                HighlightedLabel::new("Hello, world!", vec![0, 1, 2, 7, 8, 12]).color(Color::Error),
-            )
-            .child(
-                Label::new("This text is pulsating").with_animation(
-                    "pulsating-label",
-                    Animation::new(Duration::from_secs(2))
-                        .repeat()
-                        .with_easing(pulsating_between(0.4, 0.8)),
-                    |label, delta| label.alpha(delta),
-                ),
-            )
-    }
-}

crates/ui/src/components/tab.rs πŸ”—

@@ -172,6 +172,7 @@ impl RenderOnce for Tab {
     }
 }
 
+// View this component preview using `workspace: open component-preview`
 impl ComponentPreview for Tab {
     fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
         v_flex()

crates/ui/src/components/table.rs πŸ”—

@@ -151,6 +151,7 @@ where
     }
 }
 
+// View this component preview using `workspace: open component-preview`
 impl ComponentPreview for Table {
     fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
         v_flex()

crates/ui/src/components/toggle.rs πŸ”—

@@ -508,6 +508,7 @@ impl RenderOnce for SwitchWithLabel {
     }
 }
 
+// View this component preview using `workspace: open component-preview`
 impl ComponentPreview for Checkbox {
     fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
         v_flex()
@@ -592,6 +593,7 @@ impl ComponentPreview for Checkbox {
     }
 }
 
+// View this component preview using `workspace: open component-preview`
 impl ComponentPreview for Switch {
     fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
         v_flex()
@@ -654,6 +656,7 @@ impl ComponentPreview for Switch {
     }
 }
 
+// View this component preview using `workspace: open component-preview`
 impl ComponentPreview for CheckboxWithLabel {
     fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
         v_flex()

crates/ui/src/components/tooltip.rs πŸ”—

@@ -206,6 +206,7 @@ impl Render for LinkPreview {
     }
 }
 
+// View this component preview using `workspace: open component-preview`
 impl ComponentPreview for Tooltip {
     fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
         example_group(vec![single_example(

crates/ui/src/prelude.rs πŸ”—

@@ -2,8 +2,9 @@
 
 pub use gpui::prelude::*;
 pub use gpui::{
-    div, px, relative, rems, AbsoluteLength, App, Context, DefiniteLength, Div, Element, ElementId,
-    InteractiveElement, ParentElement, Pixels, Rems, RenderOnce, SharedString, Styled, Window,
+    div, px, relative, rems, AbsoluteLength, AnyElement, App, Context, DefiniteLength, Div,
+    Element, ElementId, InteractiveElement, ParentElement, Pixels, Rems, RenderOnce, SharedString,
+    Styled, Window,
 };
 
 pub use component::{example_group, example_group_with_title, single_example, ComponentPreview};

crates/ui/src/styles/typography.rs πŸ”—

@@ -233,6 +233,7 @@ impl Headline {
     }
 }
 
+// View this component preview using `workspace: open component-preview`
 impl ComponentPreview for Headline {
     fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
         v_flex()

crates/welcome/src/welcome.rs πŸ”—

@@ -104,7 +104,7 @@ impl Render for WelcomePage {
                                 h_flex().w_full().justify_center().child(
                                     Label::new("The editor for what's next")
                                         .color(Color::Muted)
-                                        .italic(true),
+                                        .italic(),
                                 ),
                             ),
                     )

crates/zed/src/zed/migrate.rs πŸ”—

@@ -8,14 +8,17 @@ pub fn should_migrate_settings(settings: &serde_json::Value) -> bool {
     let Ok(old_text) = serde_json::to_string(settings) else {
         return false;
     };
-    migrator::migrate_settings(&old_text).is_some()
+    migrator::migrate_settings(&old_text)
+        .ok()
+        .flatten()
+        .is_some()
 }
 
 pub fn migrate_settings(fs: Arc<dyn Fs>, cx: &mut gpui::App) {
     cx.background_executor()
         .spawn(async move {
             let old_text = SettingsStore::load_settings(&fs).await?;
-            let Some(new_text) = migrator::migrate_settings(&old_text) else {
+            let Some(new_text) = migrator::migrate_settings(&old_text)? else {
                 return anyhow::Ok(());
             };
             let settings_path = paths::settings_file().as_path();
@@ -49,12 +52,12 @@ pub fn should_migrate_keymap(keymap_file: KeymapFile) -> bool {
     let Ok(old_text) = serde_json::to_string(&keymap_file) else {
         return false;
     };
-    migrator::migrate_keymap(&old_text).is_some()
+    migrator::migrate_keymap(&old_text).ok().flatten().is_some()
 }
 
 pub async fn migrate_keymap(fs: Arc<dyn Fs>) -> anyhow::Result<()> {
     let old_text = KeymapFile::load_keymap_file(&fs).await?;
-    let Some(new_text) = migrator::migrate_keymap(&old_text) else {
+    let Some(new_text) = migrator::migrate_keymap(&old_text)? else {
         return Ok(());
     };
     let keymap_path = paths::keymap_file().as_path();

script/mitm-proxy.sh πŸ”—

@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+set -e
+
+CONTAINER_ID=$(docker run -d --rm -it -v ~/.mitmproxy:/home/mitmproxy/.mitmproxy -p 9876:8080 mitmproxy/mitmproxy mitmdump)
+
+trap 'docker stop '"$CONTAINER_ID"' 1> /dev/null || true; exit 1' SIGINT
+
+echo "Add the root certificate created in ~/.mitmproxy to your certificate chain for HTTP"
+echo "on macOS:"
+echo "sudo security add-trusted-cert -d -p ssl -p basic -k /Library/Keychains/System.keychain ~/.mitmproxy/mitmproxy-ca-cert.pem"
+echo "Press enter to continue"
+read
+
+http_proxy=http://localhost:9876 cargo run
+
+# Clean up detached proxy after running
+docker stop "$CONTAINER_ID" 2>/dev/null || true