{
use text::ToPoint as _;
@@ -5643,6 +5737,7 @@ impl Editor {
let preview = h_flex()
.gap_1()
+ .min_w_16()
.child(styled_text)
.when(len_total > first_line_len, |parent| parent.child("…"));
@@ -5653,7 +5748,16 @@ impl Editor {
Icon::new(IconName::ZedPredict).into_any_element()
};
- Some(h_flex().flex_1().gap_3().child(left).child(preview))
+ Some(
+ h_flex()
+ .h_full()
+ .flex_1()
+ .gap_2()
+ .pr_1()
+ .overflow_x_hidden()
+ .child(left)
+ .child(preview),
+ )
}
InlineCompletion::Move {
@@ -5666,71 +5770,87 @@ impl Editor {
None,
&style.syntax,
);
+ let base = h_flex().gap_3().flex_1().child(render_relative_row_jump(
+ "Jump ",
+ cursor_point.row,
+ target.text_anchor.to_point(&snapshot).row,
+ ));
+
+ if highlighted_text.text.is_empty() {
+ return Some(base);
+ }
+
let cursor_color = self.current_user_player_color(cx).cursor;
let start_point = range_around_target.start.to_point(&snapshot);
let end_point = range_around_target.end.to_point(&snapshot);
let target_point = target.text_anchor.to_point(&snapshot);
- let start_column_x =
- line_layouts[start_point.row as usize].x_for_index(start_point.column as usize);
- let target_column_x = line_layouts[target_point.row as usize]
- .x_for_index(target_point.column as usize);
- let cursor_relative_position = target_column_x - start_column_x;
+ let styled_text = highlighted_text.to_styled_text(&style.text);
+ let text_len = highlighted_text.text.len();
+
+ let cursor_relative_position = window
+ .text_system()
+ .layout_line(
+ highlighted_text.text,
+ style.text.font_size.to_pixels(window.rem_size()),
+ // We don't need to include highlights
+ // because we are only using this for the cursor position
+ &[TextRun {
+ len: text_len,
+ font: style.text.font(),
+ color: style.text.color,
+ background_color: None,
+ underline: None,
+ strikethrough: None,
+ }],
+ )
+ .log_err()
+ .map(|line| {
+ line.x_for_index(
+ target_point.column.saturating_sub(start_point.column) as usize
+ )
+ });
let fade_before = start_point.column > 0;
let fade_after = end_point.column < snapshot.line_len(end_point.row);
let background = cx.theme().colors().elevated_surface_background;
- Some(
- h_flex()
- .gap_3()
- .flex_1()
- .child(render_relative_row_jump(
- "Jump ",
- cursor_point.row,
- target.text_anchor.to_point(&snapshot).row,
+ let preview = h_flex()
+ .relative()
+ .child(styled_text)
+ .when(fade_before, |parent| {
+ parent.child(div().absolute().top_0().left_0().w_4().h_full().bg(
+ linear_gradient(
+ 90.,
+ linear_color_stop(background, 0.),
+ linear_color_stop(background.opacity(0.), 1.),
+ ),
))
- .when(!highlighted_text.text.is_empty(), |parent| {
- parent.child(
- h_flex()
- .relative()
- .child(highlighted_text.to_styled_text(&style.text))
- .when(fade_before, |parent| {
- parent.child(
- div().absolute().top_0().left_0().w_4().h_full().bg(
- linear_gradient(
- 90.,
- linear_color_stop(background, 0.),
- linear_color_stop(background.opacity(0.), 1.),
- ),
- ),
- )
- })
- .when(fade_after, |parent| {
- parent.child(
- div().absolute().top_0().right_0().w_4().h_full().bg(
- linear_gradient(
- -90.,
- linear_color_stop(background, 0.),
- linear_color_stop(background.opacity(0.), 1.),
- ),
- ),
- )
- })
- .child(
- div()
- .w(px(2.))
- .h_full()
- .bg(cursor_color)
- .absolute()
- .top_0()
- .left(cursor_relative_position),
- ),
- )
- }),
- )
+ })
+ .when(fade_after, |parent| {
+ parent.child(div().absolute().top_0().right_0().w_4().h_full().bg(
+ linear_gradient(
+ -90.,
+ linear_color_stop(background, 0.),
+ linear_color_stop(background.opacity(0.), 1.),
+ ),
+ ))
+ })
+ .when_some(cursor_relative_position, |parent, position| {
+ parent.child(
+ div()
+ .w(px(2.))
+ .h_full()
+ .bg(cursor_color)
+ .absolute()
+ .top_0()
+ .left(position),
+ )
+ });
+
+ Some(base.child(preview))
}
}
}
@@ -5780,9 +5900,7 @@ impl Editor {
self.completion_tasks.clear();
let context_menu = self.context_menu.borrow_mut().take();
self.stale_inline_completion_in_menu.take();
- if context_menu.is_some() {
- self.update_visible_inline_completion(window, cx);
- }
+ self.update_visible_inline_completion(window, cx);
context_menu
}
@@ -6686,11 +6804,12 @@ impl Editor {
cx: &mut App,
) -> Option<()> {
let buffer = self.buffer.read(cx);
- let change_set = buffer.change_set_for(hunk.buffer_id)?;
+ let diff = buffer.diff_for(hunk.buffer_id)?;
let buffer = buffer.buffer(hunk.buffer_id)?;
let buffer = buffer.read(cx);
- let original_text = change_set
+ let original_text = diff
.read(cx)
+ .snapshot
.base_text
.as_ref()?
.as_rope()
@@ -10135,12 +10254,12 @@ impl Editor {
let mut diagnostics;
if direction == Direction::Prev {
diagnostics = buffer
- .diagnostics_in_range::<_, usize>(0..search_start)
+ .diagnostics_in_range::
(0..search_start)
.collect::>();
diagnostics.reverse();
} else {
diagnostics = buffer
- .diagnostics_in_range::<_, usize>(search_start..buffer.len())
+ .diagnostics_in_range::(search_start..buffer.len())
.collect::>();
};
let group = diagnostics
@@ -10164,14 +10283,26 @@ impl Editor {
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))
- } else {
- None
+ let entry_group = entry.diagnostic.group_id;
+ let in_next_group = self.active_diagnostics.as_ref().map_or(
+ true,
+ |active| match direction {
+ Direction::Prev => {
+ entry_group != active.group_id
+ && (active.group_id == 0 || entry_group < active.group_id)
+ }
+ Direction::Next => {
+ entry_group != active.group_id
+ && (entry_group == 0 || entry_group > active.group_id)
+ }
+ },
+ );
+ if in_next_group {
+ return Some((entry.range, entry.diagnostic.group_id));
+ }
}
+ None
});
if let Some((primary_range, group_id)) = group {
@@ -11304,18 +11435,21 @@ impl Editor {
}
fn cancel_language_server_work(
- &mut self,
+ workspace: &mut Workspace,
_: &actions::CancelLanguageServerWork,
_: &mut Window,
- cx: &mut Context,
+ cx: &mut Context,
) {
- if let Some(project) = self.project.clone() {
- self.buffer.update(cx, |multi_buffer, cx| {
- project.update(cx, |project, cx| {
- project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
- });
- })
- }
+ let project = workspace.project();
+ let buffers = workspace
+ .active_item(cx)
+ .and_then(|item| item.act_as::(cx))
+ .map_or(HashSet::default(), |editor| {
+ editor.read(cx).buffer.read(cx).all_buffers()
+ });
+ project.update(cx, |project, cx| {
+ project.cancel_language_server_work_for_buffers(buffers, cx);
+ });
}
fn show_character_palette(
@@ -11331,8 +11465,9 @@ impl Editor {
if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
let buffer = self.buffer.read(cx).snapshot(cx);
let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
+ let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
let is_valid = buffer
- .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone())
+ .diagnostics_in_range::(primary_range_start..primary_range_end)
.any(|entry| {
entry.diagnostic.is_primary
&& !entry.range.is_empty()
@@ -11673,7 +11808,7 @@ impl Editor {
return;
}
- let fold_at_level = fold_at.level;
+ let fold_at_level = fold_at.0;
let snapshot = self.buffer.read(cx).snapshot(cx);
let mut to_fold = Vec::new();
let mut stack = vec![(0, snapshot.max_row().0, 1)];
@@ -12145,11 +12280,19 @@ impl Editor {
cx: &mut Context<'_, Editor>,
) {
self.buffer.update(cx, |buffer, cx| {
- if buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx) {
- buffer.collapse_diff_hunks(ranges, cx)
- } else {
- buffer.expand_diff_hunks(ranges, cx)
- }
+ let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
+ buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
+ })
+ }
+
+ fn toggle_diff_hunks_in_ranges_narrow(
+ &mut self,
+ ranges: Vec>,
+ cx: &mut Context<'_, Editor>,
+ ) {
+ self.buffer.update(cx, |buffer, cx| {
+ let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
+ buffer.expand_or_collapse_diff_hunks_narrow(ranges, expand, cx);
})
}
@@ -13640,9 +13783,9 @@ impl Editor {
} => {
self.tasks_update_task = Some(self.refresh_runnables(window, cx));
let buffer_id = buffer.read(cx).remote_id();
- if self.buffer.read(cx).change_set_for(buffer_id).is_none() {
+ if self.buffer.read(cx).diff_for(buffer_id).is_none() {
if let Some(project) = &self.project {
- get_unstaged_changes_for_buffers(
+ get_uncommitted_diff_for_buffer(
project,
[buffer.clone()],
self.buffer.clone(),
@@ -14059,13 +14202,14 @@ impl Editor {
.get("vim_mode")
== Some(&serde_json::Value::Bool(true));
- let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
- == language::language_settings::InlineCompletionProvider::Copilot;
+ let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
+ let copilot_enabled = edit_predictions_provider
+ == language::language_settings::EditPredictionProvider::Copilot;
let copilot_enabled_for_language = self
.buffer
.read(cx)
.settings_at(0, cx)
- .show_inline_completions;
+ .show_edit_predictions;
let project = project.read(cx);
telemetry::event!(
@@ -14074,6 +14218,7 @@ impl Editor {
vim_mode,
copilot_enabled,
copilot_enabled_for_language,
+ edit_predictions_provider,
is_via_ssh = project.is_via_ssh(),
);
}
@@ -14364,10 +14509,11 @@ impl Editor {
Some(gpui::Point::new(source_x, source_y))
}
- pub fn has_active_completions_menu(&self) -> bool {
- self.context_menu.borrow().as_ref().map_or(false, |menu| {
- menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
- })
+ pub fn has_visible_completions_menu(&self) -> bool {
+ !self.previewing_inline_completion
+ && self.context_menu.borrow().as_ref().map_or(false, |menu| {
+ menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
+ })
}
pub fn register_addon(&mut self, instance: T) {
@@ -14386,7 +14532,7 @@ impl Editor {
.and_then(|item| item.to_any().downcast_ref::())
}
- fn character_size(&self, window: &mut Window) -> gpui::Point {
+ fn character_size(&self, window: &mut Window) -> gpui::Size {
let text_layout_details = self.text_layout_details(window);
let style = &text_layout_details.editor_style;
let font_id = window.text_system().resolve_font(&style.text.font());
@@ -14394,11 +14540,11 @@ impl Editor {
let line_height = style.text.line_height_in_pixels(window.rem_size());
let em_width = window.text_system().em_width(font_id, font_size).unwrap();
- gpui::Point::new(em_width, line_height)
+ gpui::Size::new(em_width, line_height)
}
}
-fn get_unstaged_changes_for_buffers(
+fn get_uncommitted_diff_for_buffer(
project: &Entity,
buffers: impl IntoIterator- >,
buffer: Entity,
@@ -14407,17 +14553,15 @@ fn get_unstaged_changes_for_buffers(
let mut tasks = Vec::new();
project.update(cx, |project, cx| {
for buffer in buffers {
- tasks.push(project.open_unstaged_changes(buffer.clone(), cx))
+ tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
}
});
cx.spawn(|mut cx| async move {
- let change_sets = futures::future::join_all(tasks).await;
+ let diffs = futures::future::join_all(tasks).await;
buffer
.update(&mut cx, |buffer, cx| {
- for change_set in change_sets {
- if let Some(change_set) = change_set.log_err() {
- buffer.add_change_set(change_set, cx);
- }
+ for diff in diffs.into_iter().flatten() {
+ buffer.add_diff(diff, cx);
}
})
.ok();
@@ -15902,9 +16046,9 @@ impl EntityInputHandler for Editor {
cx: &mut Context,
) -> Option> {
let text_layout_details = self.text_layout_details(window);
- let gpui::Point {
- x: em_width,
- y: line_height,
+ let gpui::Size {
+ width: em_width,
+ height: line_height,
} = self.character_size(window);
let snapshot = self.snapshot(window, cx);
@@ -15922,6 +16066,24 @@ impl EntityInputHandler for Editor {
size: size(em_width, line_height),
})
}
+
+ fn character_index_for_point(
+ &mut self,
+ point: gpui::Point,
+ _window: &mut Window,
+ _cx: &mut Context,
+ ) -> Option {
+ let position_map = self.last_position_map.as_ref()?;
+ if !position_map.text_hitbox.contains(&point) {
+ return None;
+ }
+ let display_point = position_map.point_for_position(point).previous_valid;
+ let anchor = position_map
+ .snapshot
+ .display_point_to_anchor(display_point, Bias::Left);
+ let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
+ Some(utf16_offset.0)
+ }
}
trait SelectionExt {
diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs
index 098ff62dae5f5d03642fdb943a3d3273348aab5b..9203a8f95366dbd3bde7cb95f63d2bd78c7ed7a6 100644
--- a/crates/editor/src/editor_settings.rs
+++ b/crates/editor/src/editor_settings.rs
@@ -35,7 +35,7 @@ pub struct EditorSettings {
pub auto_signature_help: bool,
pub show_signature_help_after_edits: bool,
pub jupyter: Jupyter,
- pub show_inline_completions_in_menu: bool,
+ pub show_edit_predictions_in_menu: bool,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
@@ -372,7 +372,7 @@ pub struct EditorSettingsContent {
/// Only has an effect if edit prediction provider supports it.
///
/// Default: true
- pub show_inline_completions_in_menu: Option,
+ 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 2ce221fbda6d80a0fb056095abcf02c1e95365d3..5247c629c06919ce503c02545193de7143e7cafc 100644
--- a/crates/editor/src/editor_tests.rs
+++ b/crates/editor/src/editor_tests.rs
@@ -7,6 +7,7 @@ use crate::{
},
JoinLines,
};
+use diff::{BufferDiff, DiffHunkStatus};
use futures::StreamExt;
use gpui::{
div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
@@ -26,7 +27,7 @@ use language_settings::{Formatter, FormatterList, IndentGuideSettings};
use multi_buffer::IndentGuide;
use parking_lot::Mutex;
use pretty_assertions::{assert_eq, assert_ne};
-use project::{buffer_store::BufferChangeSet, FakeFs};
+use project::FakeFs;
use project::{
lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
project_settings::{LspSettings, ProjectSettings},
@@ -40,8 +41,9 @@ use std::{
use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
use unindent::Unindent;
use util::{
- assert_set_eq,
+ assert_set_eq, path,
test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
+ uri,
};
use workspace::{
item::{FollowEvent, FollowableItem, Item, ItemHandle},
@@ -1157,7 +1159,7 @@ fn test_fold_at_level(cx: &mut TestAppContext) {
});
_ = editor.update(cx, |editor, window, cx| {
- editor.fold_at_level(&FoldAtLevel { level: 2 }, window, cx);
+ editor.fold_at_level(&FoldAtLevel(2), window, cx);
assert_eq!(
editor.display_text(cx),
"
@@ -1181,7 +1183,7 @@ fn test_fold_at_level(cx: &mut TestAppContext) {
.unindent(),
);
- editor.fold_at_level(&FoldAtLevel { level: 1 }, window, cx);
+ editor.fold_at_level(&FoldAtLevel(1), window, cx);
assert_eq!(
editor.display_text(cx),
"
@@ -1196,7 +1198,7 @@ fn test_fold_at_level(cx: &mut TestAppContext) {
);
editor.unfold_all(&UnfoldAll, window, cx);
- editor.fold_at_level(&FoldAtLevel { level: 0 }, window, cx);
+ editor.fold_at_level(&FoldAtLevel(0), window, cx);
assert_eq!(
editor.display_text(cx),
"
@@ -5619,13 +5621,13 @@ async fn test_fold_function_bodies(cx: &mut gpui::TestAppContext) {
let base_text = r#"
impl A {
- // this is an unstaged comment
+ // this is an uncommitted comment
fn b() {
c();
}
- // this is another unstaged comment
+ // this is another uncommitted comment
fn d() {
// e
@@ -5668,13 +5670,13 @@ async fn test_fold_function_bodies(cx: &mut gpui::TestAppContext) {
cx.assert_state_with_diff(
"
ˇimpl A {
- - // this is an unstaged comment
+ - // this is an uncommitted comment
fn b() {
c();
}
- - // this is another unstaged comment
+ - // this is another uncommitted comment
-
fn d() {
// e
@@ -5691,13 +5693,13 @@ async fn test_fold_function_bodies(cx: &mut gpui::TestAppContext) {
let expected_display_text = "
impl A {
- // this is an unstaged comment
+ // this is an uncommitted comment
fn b() {
⋯
}
- // this is another unstaged comment
+ // this is another uncommitted comment
fn d() {
⋯
@@ -7074,9 +7076,9 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let fs = FakeFs::new(cx.executor());
- fs.insert_file("/file.rs", Default::default()).await;
+ fs.insert_file(path!("/file.rs"), Default::default()).await;
- let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
+ let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(rust_lang());
@@ -7092,7 +7094,9 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
);
let buffer = project
- .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
+ .update(cx, |project, cx| {
+ project.open_local_buffer(path!("/file.rs"), cx)
+ })
.await
.unwrap();
@@ -7117,7 +7121,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
.handle_request::(move |params, _| async move {
assert_eq!(
params.text_document.uri,
- lsp::Url::from_file_path("/file.rs").unwrap()
+ lsp::Url::from_file_path(path!("/file.rs")).unwrap()
);
assert_eq!(params.options.tab_size, 4);
Ok(Some(vec![lsp::TextEdit::new(
@@ -7145,7 +7149,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
fake_server.handle_request::(move |params, _| async move {
assert_eq!(
params.text_document.uri,
- lsp::Url::from_file_path("/file.rs").unwrap()
+ lsp::Url::from_file_path(path!("/file.rs")).unwrap()
);
futures::future::pending::<()>().await;
unreachable!()
@@ -7202,7 +7206,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
.handle_request::(move |params, _| async move {
assert_eq!(
params.text_document.uri,
- lsp::Url::from_file_path("/file.rs").unwrap()
+ lsp::Url::from_file_path(path!("/file.rs")).unwrap()
);
assert_eq!(params.options.tab_size, 8);
Ok(Some(vec![]))
@@ -7237,7 +7241,7 @@ async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
- "/a",
+ path!("/a"),
json!({
"main.rs": sample_text_1,
"other.rs": sample_text_2,
@@ -7246,7 +7250,7 @@ async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
)
.await;
- let project = Project::test(fs, ["/a".as_ref()], cx).await;
+ let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
@@ -7421,20 +7425,20 @@ async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
assert_eq!(
multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
- "a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
+ uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}"),
);
buffer_1.update(cx, |buffer, _| {
assert!(!buffer.is_dirty());
assert_eq!(
buffer.text(),
- "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
+ uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
)
});
buffer_2.update(cx, |buffer, _| {
assert!(!buffer.is_dirty());
assert_eq!(
buffer.text(),
- "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
+ uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
)
});
buffer_3.update(cx, |buffer, _| {
@@ -7448,9 +7452,9 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let fs = FakeFs::new(cx.executor());
- fs.insert_file("/file.rs", Default::default()).await;
+ fs.insert_file(path!("/file.rs"), Default::default()).await;
- let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
+ let project = Project::test(fs, [path!("/").as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(rust_lang());
@@ -7466,7 +7470,9 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
);
let buffer = project
- .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
+ .update(cx, |project, cx| {
+ project.open_local_buffer(path!("/file.rs"), cx)
+ })
.await
.unwrap();
@@ -7491,7 +7497,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
.handle_request::(move |params, _| async move {
assert_eq!(
params.text_document.uri,
- lsp::Url::from_file_path("/file.rs").unwrap()
+ lsp::Url::from_file_path(path!("/file.rs")).unwrap()
);
assert_eq!(params.options.tab_size, 4);
Ok(Some(vec![lsp::TextEdit::new(
@@ -7519,7 +7525,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
move |params, _| async move {
assert_eq!(
params.text_document.uri,
- lsp::Url::from_file_path("/file.rs").unwrap()
+ lsp::Url::from_file_path(path!("/file.rs")).unwrap()
);
futures::future::pending::<()>().await;
unreachable!()
@@ -7577,7 +7583,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
.handle_request::(move |params, _| async move {
assert_eq!(
params.text_document.uri,
- lsp::Url::from_file_path("/file.rs").unwrap()
+ lsp::Url::from_file_path(path!("/file.rs")).unwrap()
);
assert_eq!(params.options.tab_size, 8);
Ok(Some(vec![]))
@@ -7597,9 +7603,9 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
});
let fs = FakeFs::new(cx.executor());
- fs.insert_file("/file.rs", Default::default()).await;
+ fs.insert_file(path!("/file.rs"), Default::default()).await;
- let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
+ let project = Project::test(fs, [path!("/").as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(Arc::new(Language::new(
@@ -7633,7 +7639,9 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
);
let buffer = project
- .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
+ .update(cx, |project, cx| {
+ project.open_local_buffer(path!("/file.rs"), cx)
+ })
.await
.unwrap();
@@ -7663,7 +7671,7 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
.handle_request::(move |params, _| async move {
assert_eq!(
params.text_document.uri,
- lsp::Url::from_file_path("/file.rs").unwrap()
+ lsp::Url::from_file_path(path!("/file.rs")).unwrap()
);
assert_eq!(params.options.tab_size, 4);
Ok(Some(vec![lsp::TextEdit::new(
@@ -7687,7 +7695,7 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
fake_server.handle_request::(move |params, _| async move {
assert_eq!(
params.text_document.uri,
- lsp::Url::from_file_path("/file.rs").unwrap()
+ lsp::Url::from_file_path(path!("/file.rs")).unwrap()
);
futures::future::pending::<()>().await;
unreachable!()
@@ -8727,14 +8735,14 @@ async fn test_multiline_completion(cx: &mut gpui::TestAppContext) {
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
- "/a",
+ path!("/a"),
json!({
"main.ts": "a",
}),
)
.await;
- let project = Project::test(fs, ["/a".as_ref()], cx).await;
+ let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
let typescript_language = Arc::new(Language::new(
LanguageConfig {
@@ -8794,7 +8802,7 @@ async fn test_multiline_completion(cx: &mut gpui::TestAppContext) {
.unwrap();
let _buffer = project
.update(cx, |project, cx| {
- project.open_local_buffer_with_lsp("/a/main.ts", cx)
+ project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
})
.await
.unwrap();
@@ -10570,7 +10578,7 @@ async fn go_to_prev_overlapping_diagnostic(
.update_diagnostics(
LanguageServerId(0),
lsp::PublishDiagnosticsParams {
- uri: lsp::Url::from_file_path("/root/file").unwrap(),
+ uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
version: None,
diagnostics: vec![
lsp::Diagnostic {
@@ -10645,6 +10653,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, |_| {});
@@ -10663,7 +10841,7 @@ async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
lsp_store.update_diagnostics(
LanguageServerId(0),
lsp::PublishDiagnosticsParams {
- uri: lsp::Url::from_file_path("/root/file").unwrap(),
+ uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
version: None,
diagnostics: vec![lsp::Diagnostic {
range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
@@ -10923,14 +11101,14 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
- "/a",
+ path!("/a"),
json!({
"main.rs": "fn main() { let a = 5; }",
"other.rs": "// Test file",
}),
)
.await;
- let project = Project::test(fs, ["/a".as_ref()], cx).await;
+ let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(Arc::new(Language::new(
@@ -10982,7 +11160,7 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
let buffer = project
.update(cx, |project, cx| {
- project.open_local_buffer("/a/main.rs", cx)
+ project.open_local_buffer(path!("/a/main.rs"), cx)
})
.await
.unwrap();
@@ -11002,7 +11180,7 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
fake_server.handle_request::(|params, _| async move {
assert_eq!(
params.text_document_position.text_document.uri,
- lsp::Url::from_file_path("/a/main.rs").unwrap(),
+ lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
);
assert_eq!(
params.text_document_position.position,
@@ -11040,7 +11218,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
- "/a",
+ path!("/a"),
json!({
"main.rs": "fn main() { let a = 5; }",
"other.rs": "// Test file",
@@ -11048,7 +11226,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test
)
.await;
- let project = Project::test(fs, ["/a".as_ref()], cx).await;
+ let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
let server_restarts = Arc::new(AtomicUsize::new(0));
let closure_restarts = Arc::clone(&server_restarts);
@@ -11088,7 +11266,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test
let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
let _buffer = project
.update(cx, |project, cx| {
- project.open_local_buffer_with_lsp("/a/main.rs", cx)
+ project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
})
.await
.unwrap();
@@ -11861,9 +12039,9 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
});
let fs = FakeFs::new(cx.executor());
- fs.insert_file("/file.ts", Default::default()).await;
+ fs.insert_file(path!("/file.ts"), Default::default()).await;
- let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
+ let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(Arc::new(Language::new(
@@ -11895,7 +12073,9 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
let buffer = project
- .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
+ .update(cx, |project, cx| {
+ project.open_local_buffer(path!("/file.ts"), cx)
+ })
.await
.unwrap();
@@ -12431,11 +12611,10 @@ async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
(buffer_2.clone(), base_text_2),
(buffer_3.clone(), base_text_3),
] {
- let change_set = cx
- .new(|cx| BufferChangeSet::new_with_base_text(diff_base.to_string(), &buffer, cx));
+ let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
editor
.buffer
- .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
+ .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
}
});
cx.executor().run_until_parked();
@@ -13125,12 +13304,10 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext)
(buffer_2.clone(), file_2_old),
(buffer_3.clone(), file_3_old),
] {
- let change_set = cx.new(|cx| {
- BufferChangeSet::new_with_base_text(diff_base.to_string(), &buffer, cx)
- });
+ let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
editor
.buffer
- .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
+ .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
}
})
.unwrap();
@@ -13212,7 +13389,7 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext
init_test(cx, |_| {});
let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
- let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
+ let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
let multi_buffer = cx.new(|cx| {
@@ -13225,7 +13402,11 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext
primary: None,
},
ExcerptRange {
- context: Point::new(5, 0)..Point::new(7, 0),
+ context: Point::new(4, 0)..Point::new(7, 0),
+ primary: None,
+ },
+ ExcerptRange {
+ context: Point::new(9, 0)..Point::new(10, 0),
primary: None,
},
],
@@ -13239,11 +13420,10 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext
});
editor
.update(cx, |editor, _window, cx| {
- let change_set =
- cx.new(|cx| BufferChangeSet::new_with_base_text(base.to_string(), &buffer, cx));
+ let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
editor
.buffer
- .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx))
+ .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
})
.unwrap();
@@ -13255,14 +13435,22 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext
});
cx.executor().run_until_parked();
+ // When the start of a hunk coincides with the start of its excerpt,
+ // the hunk is expanded. When the start of a a hunk is earlier than
+ // the start of its excerpt, the hunk is not expanded.
cx.assert_state_with_diff(
"
ˇaaa
- bbb
+ BBB
+ - ddd
+ - eee
+ + DDD
+ EEE
fff
+
+ iii
"
.unindent(),
);
@@ -13500,8 +13688,8 @@ async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
cx.set_state(indoc! { "
one
- TWO
- ˇthree
+ ˇTWO
+ three
four
five
"});
@@ -13514,15 +13702,14 @@ async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
indoc! { "
one
- two
- + TWO
- ˇthree
+ + ˇTWO
+ three
four
five
"}
.to_string(),
);
cx.update_editor(|editor, window, cx| {
- editor.move_up(&Default::default(), window, cx);
editor.move_up(&Default::default(), window, cx);
editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
});
@@ -14402,15 +14589,10 @@ async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContex
editor.buffer().update(cx, |multibuffer, cx| {
let buffer = multibuffer.as_singleton().unwrap();
- let change_set = cx.new(|cx| {
- let mut change_set = BufferChangeSet::new(&buffer, cx);
- let _ =
- change_set.set_base_text(base_text.into(), buffer.read(cx).text_snapshot(), cx);
- change_set
- });
+ let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
multibuffer.set_all_diff_hunks_expanded(cx);
- multibuffer.add_change_set(change_set, cx);
+ multibuffer.add_diff(diff, cx);
buffer.read(cx).remote_id()
})
@@ -14863,7 +15045,7 @@ async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
- "/a",
+ path!("/a"),
json!({
"first.rs": sample_text_1,
"second.rs": sample_text_2,
@@ -14871,7 +15053,7 @@ async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
}),
)
.await;
- let project = Project::test(fs, ["/a".as_ref()], cx).await;
+ let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
let worktree = project.update(cx, |project, cx| {
@@ -15047,7 +15229,7 @@ async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
- "/a",
+ path!("/a"),
json!({
"first.rs": sample_text_1,
"second.rs": sample_text_2,
@@ -15055,7 +15237,7 @@ async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext
}),
)
.await;
- let project = Project::test(fs, ["/a".as_ref()], cx).await;
+ let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
let worktree = project.update(cx, |project, cx| {
@@ -15194,13 +15376,13 @@ async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppCon
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
- "/a",
+ path!("/a"),
json!({
"main.rs": sample_text,
}),
)
.await;
- let project = Project::test(fs, ["/a".as_ref()], cx).await;
+ let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
let worktree = project.update(cx, |project, cx| {
diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs
index 4b57f8654495ce8f7b676ee50f8822bd9bbb76bd..04bcf722625bfb40016f4d7b16b0b246f97c877e 100644
--- a/crates/editor/src/element.rs
+++ b/crates/editor/src/element.rs
@@ -26,8 +26,9 @@ use crate::{
};
use client::ParticipantIndex;
use collections::{BTreeMap, HashMap, HashSet};
+use diff::DiffHunkStatus;
use file_icons::FileIcons;
-use git::{blame::BlameEntry, diff::DiffHunkStatus, Oid};
+use git::{blame::BlameEntry, Oid};
use gpui::{
anchored, deferred, div, fill, linear_color_stop, linear_gradient, outline, point, px, quad,
relative, size, svg, transparent_black, Action, AnyElement, App, AvailableSpace, Axis, Bounds,
@@ -430,7 +431,6 @@ impl EditorElement {
}
});
register_action(editor, window, Editor::restart_language_server);
- register_action(editor, window, Editor::cancel_language_server_work);
register_action(editor, window, Editor::show_character_palette);
register_action(editor, window, |editor, action, window, cx| {
if let Some(task) = editor.confirm_completion(action, window, cx) {
@@ -475,8 +475,8 @@ impl EditorElement {
}
});
register_action(editor, window, Editor::show_signature_help);
- register_action(editor, window, Editor::next_inline_completion);
- register_action(editor, window, Editor::previous_inline_completion);
+ register_action(editor, window, Editor::next_edit_prediction);
+ register_action(editor, window, Editor::previous_edit_prediction);
register_action(editor, window, Editor::show_inline_completion);
register_action(editor, window, Editor::context_menu_first);
register_action(editor, window, Editor::context_menu_prev);
@@ -486,7 +486,7 @@ impl EditorElement {
register_action(editor, window, Editor::unique_lines_case_insensitive);
register_action(editor, window, Editor::unique_lines_case_sensitive);
register_action(editor, window, Editor::accept_partial_inline_completion);
- register_action(editor, window, Editor::accept_inline_completion);
+ register_action(editor, window, Editor::accept_edit_prediction);
register_action(editor, window, Editor::revert_file);
register_action(editor, window, Editor::revert_selected_hunks);
register_action(editor, window, Editor::apply_all_diff_hunks);
@@ -503,7 +503,6 @@ impl EditorElement {
let position_map = layout.position_map.clone();
window.on_key_event({
let editor = self.editor.clone();
- let text_hitbox = layout.text_hitbox.clone();
move |event: &ModifiersChangedEvent, phase, window, cx| {
if phase != DispatchPhase::Bubble {
return;
@@ -512,7 +511,7 @@ impl EditorElement {
if editor.hover_state.focused(window, cx) {
return;
}
- Self::modifiers_changed(editor, event, &position_map, &text_hitbox, window, cx)
+ Self::modifiers_changed(editor, event, &position_map, window, cx)
})
}
});
@@ -522,19 +521,18 @@ impl EditorElement {
editor: &mut Editor,
event: &ModifiersChangedEvent,
position_map: &PositionMap,
- text_hitbox: &Hitbox,
window: &mut Window,
cx: &mut Context,
) {
editor.update_inline_completion_preview(&event.modifiers, window, cx);
let mouse_position = window.mouse_position();
- if !text_hitbox.is_hovered(window) {
+ if !position_map.text_hitbox.is_hovered(window) {
return;
}
editor.update_hovered_link(
- position_map.point_for_position(text_hitbox.bounds, mouse_position),
+ position_map.point_for_position(mouse_position),
&position_map.snapshot,
event.modifiers,
window,
@@ -542,14 +540,11 @@ impl EditorElement {
)
}
- #[allow(clippy::too_many_arguments)]
fn mouse_left_down(
editor: &mut Editor,
event: &MouseDownEvent,
hovered_hunk: Option>,
position_map: &PositionMap,
- text_hitbox: &Hitbox,
- gutter_hitbox: &Hitbox,
line_numbers: &HashMap,
window: &mut Window,
cx: &mut Context,
@@ -558,11 +553,13 @@ impl EditorElement {
return;
}
+ let text_hitbox = &position_map.text_hitbox;
+ let gutter_hitbox = &position_map.gutter_hitbox;
let mut click_count = event.click_count;
let mut modifiers = event.modifiers;
if let Some(hovered_hunk) = hovered_hunk {
- editor.toggle_diff_hunks_in_ranges(vec![hovered_hunk], cx);
+ editor.toggle_diff_hunks_in_ranges_narrow(vec![hovered_hunk], cx);
cx.notify();
return;
} else if gutter_hitbox.is_hovered(window) {
@@ -614,8 +611,7 @@ impl EditorElement {
}
}
- let point_for_position =
- position_map.point_for_position(text_hitbox.bounds, event.position);
+ let point_for_position = position_map.point_for_position(event.position);
let position = point_for_position.previous_valid;
if modifiers.shift && modifiers.alt {
editor.select(
@@ -690,15 +686,13 @@ impl EditorElement {
editor: &mut Editor,
event: &MouseDownEvent,
position_map: &PositionMap,
- text_hitbox: &Hitbox,
window: &mut Window,
cx: &mut Context,
) {
- if !text_hitbox.is_hovered(window) {
+ if !position_map.text_hitbox.is_hovered(window) {
return;
}
- let point_for_position =
- position_map.point_for_position(text_hitbox.bounds, event.position);
+ let point_for_position = position_map.point_for_position(event.position);
mouse_context_menu::deploy_context_menu(
editor,
Some(event.position),
@@ -713,16 +707,14 @@ impl EditorElement {
editor: &mut Editor,
event: &MouseDownEvent,
position_map: &PositionMap,
- text_hitbox: &Hitbox,
window: &mut Window,
cx: &mut Context,
) {
- if !text_hitbox.is_hovered(window) || window.default_prevented() {
+ if !position_map.text_hitbox.is_hovered(window) || window.default_prevented() {
return;
}
- let point_for_position =
- position_map.point_for_position(text_hitbox.bounds, event.position);
+ let point_for_position = position_map.point_for_position(event.position);
let position = point_for_position.previous_valid;
editor.select(
@@ -739,15 +731,11 @@ impl EditorElement {
fn mouse_up(
editor: &mut Editor,
event: &MouseUpEvent,
- #[cfg_attr(
- not(any(target_os = "linux", target_os = "freebsd")),
- allow(unused_variables)
- )]
position_map: &PositionMap,
- text_hitbox: &Hitbox,
window: &mut Window,
cx: &mut Context,
) {
+ let text_hitbox = &position_map.text_hitbox;
let end_selection = editor.has_pending_selection();
let pending_nonempty_selections = editor.has_pending_nonempty_selection();
@@ -767,8 +755,7 @@ impl EditorElement {
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
if EditorSettings::get_global(cx).middle_click_paste {
if let Some(text) = cx.read_from_primary().and_then(|item| item.text()) {
- let point_for_position =
- position_map.point_for_position(text_hitbox.bounds, event.position);
+ let point_for_position = position_map.point_for_position(event.position);
let position = point_for_position.previous_valid;
editor.select(
@@ -791,10 +778,10 @@ impl EditorElement {
editor: &mut Editor,
event: &ClickEvent,
position_map: &PositionMap,
- text_hitbox: &Hitbox,
window: &mut Window,
cx: &mut Context,
) {
+ let text_hitbox = &position_map.text_hitbox;
let pending_nonempty_selections = editor.has_pending_nonempty_selection();
let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
@@ -804,7 +791,7 @@ impl EditorElement {
};
if !pending_nonempty_selections && multi_cursor_modifier && text_hitbox.is_hovered(window) {
- let point = position_map.point_for_position(text_hitbox.bounds, event.up.position);
+ let point = position_map.point_for_position(event.up.position);
editor.handle_click_hovered_link(point, event.modifiers(), window, cx);
cx.stop_propagation();
@@ -815,7 +802,6 @@ impl EditorElement {
editor: &mut Editor,
event: &MouseMoveEvent,
position_map: &PositionMap,
- text_bounds: Bounds,
window: &mut Window,
cx: &mut Context,
) {
@@ -823,7 +809,8 @@ impl EditorElement {
return;
}
- let point_for_position = position_map.point_for_position(text_bounds, event.position);
+ let text_bounds = position_map.text_hitbox.bounds;
+ let point_for_position = position_map.point_for_position(event.position);
let mut scroll_delta = gpui::Point::::default();
let vertical_margin = position_map.line_height.min(text_bounds.size.height / 3.0);
let top = text_bounds.origin.y + vertical_margin;
@@ -870,19 +857,18 @@ impl EditorElement {
editor: &mut Editor,
event: &MouseMoveEvent,
position_map: &PositionMap,
- text_hitbox: &Hitbox,
- gutter_hitbox: &Hitbox,
window: &mut Window,
cx: &mut Context,
) {
+ let text_hitbox = &position_map.text_hitbox;
+ let gutter_hitbox = &position_map.gutter_hitbox;
let modifiers = event.modifiers;
let gutter_hovered = gutter_hitbox.is_hovered(window);
editor.set_gutter_hovered(gutter_hovered, cx);
// Don't trigger hover popover if mouse is hovering over context menu
if text_hitbox.is_hovered(window) {
- let point_for_position =
- position_map.point_for_position(text_hitbox.bounds, event.position);
+ let point_for_position = position_map.point_for_position(event.position);
editor.update_hovered_link(
point_for_position,
@@ -1668,7 +1654,7 @@ impl EditorElement {
if let Some(inline_completion) = editor.active_inline_completion.as_ref() {
match &inline_completion.completion {
InlineCompletion::Edit {
- display_mode: EditDisplayMode::TabAccept(_),
+ display_mode: EditDisplayMode::TabAccept,
..
} => padding += INLINE_ACCEPT_SUGGESTION_EM_WIDTHS,
_ => {}
@@ -2418,35 +2404,18 @@ impl EditorElement {
height,
} => {
let selected = selected_buffer_ids.contains(&first_excerpt.buffer_id);
- let icon_offset = gutter_dimensions.width
- - (gutter_dimensions.left_padding + gutter_dimensions.margin);
let mut result = v_flex().id(block_id).w_full();
if let Some(prev_excerpt) = prev_excerpt {
if *show_excerpt_controls {
- result =
- result.child(
- h_flex()
- .id("expand_down_hit_area")
- .w(icon_offset)
- .h(MULTI_BUFFER_EXCERPT_HEADER_HEIGHT as f32
- * window.line_height())
- .flex_none()
- .justify_end()
- .child(self.render_expand_excerpt_button(
- IconName::ArrowDownFromLine,
- None,
- cx,
- ))
- .on_click(window.listener_for(&self.editor, {
- let excerpt_id = prev_excerpt.id;
- let direction = ExpandExcerptDirection::Down;
- move |editor, _, _, cx| {
- editor.expand_excerpt(excerpt_id, direction, cx);
- cx.stop_propagation();
- }
- })),
- );
+ result = result.child(self.render_expand_excerpt_control(
+ block_id,
+ ExpandExcerptDirection::Down,
+ prev_excerpt.id,
+ gutter_dimensions,
+ window,
+ cx,
+ ));
}
}
@@ -2470,65 +2439,19 @@ impl EditorElement {
height,
starts_new_buffer,
} => {
- let icon_offset = gutter_dimensions.width
- - (gutter_dimensions.left_padding + gutter_dimensions.margin);
- let header_height =
- MULTI_BUFFER_EXCERPT_HEADER_HEIGHT as f32 * window.line_height();
let color = cx.theme().colors().clone();
- let hover_color = color.border_variant.opacity(0.5);
- let focus_handle = self.editor.focus_handle(cx).clone();
-
let mut result = v_flex().id(block_id).w_full();
- let expand_area = |id: SharedString| {
- h_flex()
- .id(id)
- .w_full()
- .cursor_pointer()
- .block_mouse_down()
- .on_mouse_move(|_, _, cx| cx.stop_propagation())
- .hover(|style| style.bg(hover_color))
- .tooltip({
- let focus_handle = focus_handle.clone();
- move |window, cx| {
- Tooltip::for_action_in(
- "Expand Excerpt",
- &ExpandExcerpts { lines: 0 },
- &focus_handle,
- window,
- cx,
- )
- }
- })
- };
if let Some(prev_excerpt) = prev_excerpt {
if *show_excerpt_controls {
- let group_name = "expand-down";
-
- result = result.child(
- expand_area(format!("block-{}-down", block_id).into())
- .group(group_name)
- .child(
- h_flex()
- .w(icon_offset)
- .h(header_height)
- .flex_none()
- .justify_end()
- .child(self.render_expand_excerpt_button(
- IconName::ArrowDownFromLine,
- Some(group_name.to_string()),
- cx,
- )),
- )
- .on_click(window.listener_for(&self.editor, {
- let excerpt_id = prev_excerpt.id;
- let direction = ExpandExcerptDirection::Down;
- move |editor, _, _, cx| {
- editor.expand_excerpt(excerpt_id, direction, cx);
- cx.stop_propagation();
- }
- })),
- );
+ result = result.child(self.render_expand_excerpt_control(
+ block_id,
+ ExpandExcerptDirection::Down,
+ prev_excerpt.id,
+ gutter_dimensions,
+ window,
+ cx,
+ ));
}
}
@@ -2554,43 +2477,20 @@ impl EditorElement {
}
if *show_excerpt_controls {
- let group_name = "expand-up-first";
-
- result = result.child(
- h_flex().group(group_name).child(
- expand_area(format!("block-{}-up-first", block_id).into())
- .h(header_height)
- .child(
- h_flex()
- .w(icon_offset)
- .h(header_height)
- .flex_none()
- .justify_end()
- .child(self.render_expand_excerpt_button(
- IconName::ArrowUpFromLine,
- Some(group_name.to_string()),
- cx,
- )),
- )
- .on_click(window.listener_for(&self.editor, {
- let excerpt_id = next_excerpt.id;
- let direction = ExpandExcerptDirection::Up;
- move |editor, _, _, cx| {
- editor.expand_excerpt(excerpt_id, direction, cx);
- cx.stop_propagation();
- }
- })),
- ),
- );
+ result = result.child(self.render_expand_excerpt_control(
+ block_id,
+ ExpandExcerptDirection::Up,
+ next_excerpt.id,
+ gutter_dimensions,
+ window,
+ cx,
+ ));
}
} else {
- let group_name = "expand-up-subsequent";
-
if *show_excerpt_controls {
result = result.child(
h_flex()
.relative()
- .group(group_name)
.child(
div()
.top(px(0.))
@@ -2599,55 +2499,14 @@ impl EditorElement {
.h_px()
.bg(color.border_variant),
)
- .child(
- expand_area(format!("block-{}-up", block_id).into())
- .h(header_height)
- .child(
- h_flex()
- .w(icon_offset)
- .h(header_height)
- .flex_none()
- .justify_end()
- .child(if *show_excerpt_controls {
- self.render_expand_excerpt_button(
- IconName::ArrowUpFromLine,
- Some(group_name.to_string()),
- cx,
- )
- } else {
- ButtonLike::new("jump-icon")
- .style(ButtonStyle::Transparent)
- .child(
- svg()
- .path(
- IconName::ArrowUpRight
- .path(),
- )
- .size(IconSize::XSmall.rems())
- .text_color(
- color.border_variant,
- )
- .group_hover(
- group_name,
- |style| {
- style.text_color(
- color.border,
- )
- },
- ),
- )
- }),
- )
- .on_click(window.listener_for(&self.editor, {
- let excerpt_id = next_excerpt.id;
- let direction = ExpandExcerptDirection::Up;
- move |editor, _, _, cx| {
- editor
- .expand_excerpt(excerpt_id, direction, cx);
- cx.stop_propagation();
- }
- })),
- ),
+ .child(self.render_expand_excerpt_control(
+ block_id,
+ ExpandExcerptDirection::Up,
+ next_excerpt.id,
+ gutter_dimensions,
+ window,
+ cx,
+ )),
);
}
};
@@ -2775,6 +2634,16 @@ impl EditorElement {
),
)
})
+ .children(
+ self.editor
+ .read(cx)
+ .addons
+ .values()
+ .filter_map(|addon| {
+ addon.render_buffer_header_controls(for_excerpt, window, cx)
+ })
+ .take(1),
+ )
.child(
h_flex()
.cursor_pointer()
@@ -2824,26 +2693,93 @@ impl EditorElement {
)
}
- fn render_expand_excerpt_button(
+ fn render_expand_excerpt_control(
&self,
- icon: IconName,
- group_name: impl Into