diff --git a/Cargo.lock b/Cargo.lock index eb872d156e57c747e717f64525112280323d8828..e2cf9c75697c57f19cb0babd1600ccaa23830eb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7878,6 +7878,7 @@ dependencies = [ name = "migrator" version = "0.1.0" dependencies = [ + "anyhow", "collections", "convert_case 0.7.1", "log", diff --git a/assets/settings/default.json b/assets/settings/default.json index 884583167cd54a7d4835148885a623b628dbcfde..2b07b3610a5ed3b33a706c8ab36cc3e26461776d 100644 --- a/assets/settings/default.json +++ b/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": { diff --git a/crates/collab/src/db/queries/users.rs b/crates/collab/src/db/queries/users.rs index 12aa97b41a0aed2d9d92b19e7ac76af8297acb95..02958556b7897735ba44b89f4fd8b0f9af1f996a 100644 --- a/crates/collab/src/db/queries/users.rs +++ b/crates/collab/src/db/queries/users.rs @@ -133,26 +133,23 @@ impl Database { initial_channel_id: Option, tx: &DatabaseTransaction, ) -> Result { - 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> { + 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> { diff --git a/crates/collab_ui/src/channel_view.rs b/crates/collab_ui/src/channel_view.rs index a4e86a9f2f518b750f139c24725177820466dcb6..46e9d04967c5e84a9f531586b30f5133ac622155 100644 --- a/crates/collab_ui/src/channel_view.rs +++ b/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( diff --git a/crates/component/src/component.rs b/crates/component/src/component.rs index e4a2ae7921f7cee42d99c11517b23e67aa6d3653..35ab8751e5c1961b63910efa5ea5efeacab7088c 100644 --- a/crates/component/src/component.rs +++ b/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(), ) diff --git a/crates/component_preview/src/component_preview.rs b/crates/component_preview/src/component_preview.rs index 84e00f751c76d656ec977355f8ed2c4d5aa350bb..e8edd392fdc551625f63d712a72720cf3b4d9a06 100644 --- a/crates/component_preview/src/component_preview.rs +++ b/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() diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 15414e5b111f89adef4d9a33d7bc3d38b681a60e..e356cf8f762e98d5aeff1a62b16059fa9d27be0d 100644 --- a/crates/editor/src/editor.rs +++ b/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::(0..search_start) - .collect::>(); - diagnostics.reverse(); - } else { - diagnostics = buffer - .diagnostics_in_range::(search_start..buffer.len()) - .collect::>(); - }; - 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::(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::>(); + 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::(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::>(); + 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); } } } diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index 9203a8f95366dbd3bde7cb95f63d2bd78c7ed7a6..73966f4452b189eacf41bb9faf84401b21c51dd2 100644 --- a/crates/editor/src/editor_settings.rs +++ b/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, - /// 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, - /// Jupyter REPL settings. pub jupyter: Option, } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 0cc4ac46fa990400674902f81aec058eb208a51e..8b89a9f0faa60c7abafc929ead093c9517b717ff 100644 --- a/crates/editor/src/editor_tests.rs +++ b/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, |_| {}); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index d53b95b007e733bd81f5d3c7f9d536b8a0f52c98..a643be3b57f10c9fe4e2bb561c16a883b9183483 100644 --- a/crates/editor/src/items.rs +++ b/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( diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 0de9859dcad53c1e6dda0d66381d151e1b94d9b9..b3675f249438fe212aeeda99b3c8e8468522ac23 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/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()), ), ), ) diff --git a/crates/git_ui/src/project_diff.rs b/crates/git_ui/src/project_diff.rs index 7d4a8c7a46a4bedb8c1e696f64f1dfacb5a63c5c..485750b9b28c053f4111d93c394fbbc888501b7a 100644 --- a/crates/git_ui/src/project_diff.rs +++ b/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) -> Option> { diff --git a/crates/image_viewer/src/image_viewer.rs b/crates/image_viewer/src/image_viewer.rs index 178200933411263c3df4540b296520edb4816f83..a440ae680e03d26ee09619b4947493a7a9d0ee9c 100644 --- a/crates/image_viewer/src/image_viewer.rs +++ b/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() } diff --git a/crates/inline_completion_button/src/inline_completion_button.rs b/crates/inline_completion_button/src/inline_completion_button.rs index b6a0e6841f3a4a921cd441642afe63846f1f8b36..4056e9acd08eecf3f10315960954a782f3ede98d 100644 --- a/crates/inline_completion_button/src/inline_completion_button.rs +++ b/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() }); } diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 48438757fbf6cc143465db63190c0e76d37a8ba2..374698ff26ea7698252e57f730c61af62f464bd0 100644 --- a/crates/language/src/language.rs +++ b/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; diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index a882677a8505d7e6a51a6f3ad86db200e6abfb62..856341dbe4199e2afccd502ca01ee7db793fb174 100644 --- a/crates/language/src/language_settings.rs +++ b/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, - /// 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>, - /// 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, diff --git a/crates/migrator/Cargo.toml b/crates/migrator/Cargo.toml index 865f4278fe5b94def083f25c09b1b32cd5fa6f03..49de22856418a408739e6004b549ed3ad7fb128b 100644 --- a/crates/migrator/Cargo.toml +++ b/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 diff --git a/crates/migrator/src/migrator.rs b/crates/migrator/src/migrator.rs index 72016ecdfeead3ce63da034b75108fbde1107c3c..c4eca930093df3c2707833b17ef3bb2a85419dd1 100644 --- a/crates/migrator/src/migrator.rs +++ b/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 { +fn migrate(text: &str, patterns: MigrationPatterns, query: &Query) -> Result> { 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 Option Option { +pub fn migrate_keymap(text: &str) -> Result> { 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 { +pub fn migrate_settings(text: &str) -> Result> { migrate( &text, SETTINGS_MIGRATION_PATTERNS, @@ -72,7 +73,7 @@ type MigrationPatterns = &'static [( fn(&str, &QueryMatch, &Query) -> Option<(Range, 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, 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> = 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, 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>> = LazyLock::new(|| { HashMap::from_iter([ ( @@ -278,7 +338,7 @@ static UNWRAP_OBJECTS: LazyLock>> = 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, 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> = 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, 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, 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> = LazyLock::new(|| { HashMap::from_iter([ ("inline_completion", "edit_prediction"), @@ -474,7 +548,7 @@ pub static CONTEXT_REPLACE: LazyLock> = 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 = 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, 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> = 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> = + 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, 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, 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> = + 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); } diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index ffdab5bcb0b9c6af5c43b9673bf5f1ec117f2bc8..cf83d7b72692adff400f6b7deb10c41c53f27716 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -3994,7 +3994,7 @@ impl ProjectPanel { .when( index == active_index && (is_active || is_marked), - |this| this.underline(true), + |this| this.underline(), ), ); diff --git a/crates/repl/src/notebook/notebook_ui.rs b/crates/repl/src/notebook/notebook_ui.rs index dde11a6aabea8a2a1de28af52da997476a4d9221..9b40b973d8d058fb09c334a7b1dcb32fc235246b 100644 --- a/crates/repl/src/notebook/notebook_ui.rs +++ b/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() } diff --git a/crates/storybook/src/story_selector.rs b/crates/storybook/src/story_selector.rs index 6f844a65b5808cb990d9c79af2e3f6609220b32f..9bae13f7bf1e147e628ebea8c921610011495a58 100644 --- a/crates/storybook/src/story_selector.rs +++ b/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(), diff --git a/crates/ui/src/components/avatar/avatar.rs b/crates/ui/src/components/avatar/avatar.rs index 82f3ea7ae2e4764471c8eb0df67827666848640b..65622f8c3f2ffddca1c48e6b4f63130504a3f530 100644 --- a/crates/ui/src/components/avatar/avatar.rs +++ b/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"; diff --git a/crates/ui/src/components/button/button.rs b/crates/ui/src/components/button/button.rs index 0209fd3d17ccf9c66ee8f565eb60d38270f404e5..58a3d5ae9175eea9ab72df0721200344f462064c 100644 --- a/crates/ui/src/components/button/button.rs +++ b/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() diff --git a/crates/ui/src/components/content_group.rs b/crates/ui/src/components/content_group.rs index 1a57838c2e8102234f18f9241edc2349d8fb724a..30c115b2a557acb768730c72981f3699b65a297b 100644 --- a/crates/ui/src/components/content_group.rs +++ b/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![ diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index d8043f74001a483e0ced274e6f08e00291542931..3288c03fc34eb59ef79d06a56fa3d6af59b41f0a 100644 --- a/crates/ui/src/components/icon.rs +++ b/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() diff --git a/crates/ui/src/components/icon/decorated_icon.rs b/crates/ui/src/components/icon/decorated_icon.rs index c973dc60961103aff4dd31286a82303d7496757b..641fb82f193e1768b47055aedc926547c77da74b 100644 --- a/crates/ui/src/components/icon/decorated_icon.rs +++ b/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( diff --git a/crates/ui/src/components/keybinding_hint.rs b/crates/ui/src/components/keybinding_hint.rs index b10e541ba504ae8550ed9e00323bd71b4f88861e..ef355336e6aa8d08a8952b62934e18b25bbdd99e 100644 --- a/crates/ui/src/components/keybinding_hint.rs +++ b/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); diff --git a/crates/ui/src/components/label/highlighted_label.rs b/crates/ui/src/components/label/highlighted_label.rs index 14ea7a5cf165a8867d95e15f6c136577980a3a58..a8be4624039eed53f2220f41c409ca457936f3cf 100644 --- a/crates/ui/src/components/label/highlighted_label.rs +++ b/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 } diff --git a/crates/ui/src/components/label/label.rs b/crates/ui/src/components/label/label.rs index 59243998df40dd5fc857f6f3d9c26dffc4972476..b9d7b1f63f9f4595a59965cd6719ddf77e98618c 100644 --- a/crates/ui/src/components/label/label.rs +++ b/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() + } } } diff --git a/crates/ui/src/components/label/label_like.rs b/crates/ui/src/components/label/label_like.rs index fad24d8699c0cbc81034cce48389d10d5e71b223..f5d0eeaf73f687145f3be5b299f7460a06a15ee5 100644 --- a/crates/ui/src/components/label/label_like.rs +++ b/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 } diff --git a/crates/ui/src/components/stories.rs b/crates/ui/src/components/stories.rs index 9161b14b47df7d278eb0fe2e41af2cf07bb1f269..9fa380c70319a800ad1455a150fd30f0e68ea8fb 100644 --- a/crates/ui/src/components/stories.rs +++ b/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::*; diff --git a/crates/ui/src/components/stories/label.rs b/crates/ui/src/components/stories/label.rs deleted file mode 100644 index 63b8091b20becc444a88e409c070b0738336f09e..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/stories/label.rs +++ /dev/null @@ -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) -> impl IntoElement { - Story::container() - .child(Story::title_for::