diff --git a/assets/settings/default.json b/assets/settings/default.json index a12cf44d94..4ebc1e702f 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -74,6 +74,10 @@ "hard_tabs": false, // How many columns a tab should occupy. "tab_size": 4, + // Git gutter behavior configuration. Remove this item to disable git gutters entirely. + "git_gutter": { + "files_included": "all" + }, // Settings specific to the terminal "terminal": { // What shell to use when opening a terminal. May take 3 values: diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 53bfe4a10c..6ecfbc7e62 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -46,10 +46,16 @@ pub use {tree_sitter_rust, tree_sitter_typescript}; pub use lsp::DiagnosticSeverity; +struct GitDiffStatus { + diff: git::BufferDiff, + update_in_progress: bool, + update_requested: bool, +} + pub struct Buffer { text: TextBuffer, head_text: Option>, - git_diff: git::BufferDiff, + git_diff_status: GitDiffStatus, file: Option>, saved_version: clock::Global, saved_version_fingerprint: String, @@ -77,7 +83,7 @@ pub struct Buffer { pub struct BufferSnapshot { text: text::BufferSnapshot, - pub git_diff_snapshot: git::BufferDiff, + pub git_diff: git::BufferDiff, pub(crate) syntax: SyntaxSnapshot, file: Option>, diagnostics: DiagnosticSet, @@ -433,7 +439,11 @@ impl Buffer { was_dirty_before_starting_transaction: None, text: buffer, head_text, - git_diff: git::BufferDiff::new(), + git_diff_status: GitDiffStatus { + diff: git::BufferDiff::new(), + update_in_progress: false, + update_requested: false, + }, file, syntax_map: Mutex::new(SyntaxMap::new()), parsing_in_background: false, @@ -464,7 +474,7 @@ impl Buffer { BufferSnapshot { text, syntax, - git_diff_snapshot: self.git_diff.clone(), + git_diff: self.git_diff_status.diff.clone(), file: self.file.clone(), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), @@ -653,15 +663,20 @@ impl Buffer { } pub fn needs_git_update(&self) -> bool { - self.git_diff.needs_update(self) + self.git_diff_status.diff.needs_update(self) } pub fn update_git(&mut self, cx: &mut ModelContext) { + if self.git_diff_status.update_in_progress { + self.git_diff_status.update_requested = true; + return; + } + if let Some(head_text) = &self.head_text { let snapshot = self.snapshot(); let head_text = head_text.clone(); - let mut diff = self.git_diff.clone(); + let mut diff = self.git_diff_status.diff.clone(); let diff = cx.background().spawn(async move { diff.update(&head_text, &snapshot).await; diff @@ -671,9 +686,14 @@ impl Buffer { let buffer_diff = diff.await; if let Some(this) = this.upgrade(&cx) { this.update(&mut cx, |this, cx| { - this.git_diff = buffer_diff; + this.git_diff_status.diff = buffer_diff; this.git_diff_update_count += 1; cx.notify(); + + this.git_diff_status.update_in_progress = false; + if this.git_diff_status.update_requested { + this.update_git(cx); + } }) } }) @@ -2195,7 +2215,7 @@ impl BufferSnapshot { &'a self, query_row_range: Range, ) -> impl 'a + Iterator> { - self.git_diff_snapshot.hunks_in_range(query_row_range, self) + self.git_diff.hunks_in_range(query_row_range, self) } pub fn diagnostics_in_range<'a, T, O>( @@ -2275,7 +2295,7 @@ impl Clone for BufferSnapshot { fn clone(&self) -> Self { Self { text: self.text.clone(), - git_diff_snapshot: self.git_diff_snapshot.clone(), + git_diff: self.git_diff.clone(), syntax: self.syntax.clone(), file: self.file.clone(), remote_selections: self.remote_selections.clone(), diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index e346ff60e6..adb2892b36 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -61,6 +61,22 @@ pub struct EditorSettings { pub format_on_save: Option, pub formatter: Option, pub enable_language_server: Option, + pub git_gutter: Option, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)] +pub struct GitGutterConfig { + pub files_included: GitGutterLevel, + pub debounce_delay_millis: Option, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum GitGutterLevel { + #[default] + All, + OnlyTracked, + None, } #[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] @@ -250,6 +266,7 @@ impl Settings { format_on_save: required(defaults.editor.format_on_save), formatter: required(defaults.editor.formatter), enable_language_server: required(defaults.editor.enable_language_server), + git_gutter: defaults.editor.git_gutter, }, editor_overrides: Default::default(), terminal_defaults: Default::default(), @@ -378,6 +395,7 @@ impl Settings { format_on_save: Some(FormatOnSave::On), formatter: Some(Formatter::LanguageServer), enable_language_server: Some(true), + git_gutter: Default::default(), }, editor_overrides: Default::default(), terminal_defaults: Default::default(), diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index e28e4d66d1..9e8338d289 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -734,18 +734,41 @@ impl ItemHandle for ViewHandle { ); } - const GIT_DELAY: Duration = Duration::from_millis(10); + let debounce_delay = cx + .global::() + .editor_overrides + .git_gutter + .unwrap_or_default() + .debounce_delay_millis; let item = item.clone(); - pending_git_update.fire_new( - GIT_DELAY, - workspace, - cx, - |project, mut cx| async move { - cx.update(|cx| item.update_git(project, cx)) - .await - .log_err(); - }, - ); + + if let Some(delay) = debounce_delay { + const MIN_GIT_DELAY: u64 = 50; + + let delay = delay.max(MIN_GIT_DELAY); + let duration = Duration::from_millis(delay); + + pending_git_update.fire_new( + duration, + workspace, + cx, + |project, mut cx| async move { + cx.update(|cx| item.update_git(project, cx)) + .await + .log_err(); + }, + ); + } else { + let project = workspace.project().downgrade(); + cx.spawn_weak(|_, mut cx| async move { + if let Some(project) = project.upgrade(&cx) { + cx.update(|cx| item.update_git(project, cx)) + .await + .log_err(); + } + }) + .detach(); + } } _ => {}