Improve context expansion (#10957)
Release Notes: - Improved expand excerpt indicators to allow unidirectional expansion. Also added the `editor::ExpandExcerptsUp` and `editor::ExpandExcerptsDown` actions, which can both take a `lines` parameter. Also added a `expand_excerpt_lines` setting which controls the default number of lines that the indicators and actions use. --------- Co-authored-by: conrad <conrad@zed.dev>
This commit is contained in:
parent
a0f91299dd
commit
a9e3d4ec4e
27 changed files with 904 additions and 328 deletions
1
assets/icons/arrow_down_from_line.svg
Normal file
1
assets/icons/arrow_down_from_line.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-from-line"><path d="M19 3H5"/><path d="M12 21V7"/><path d="m6 15 6 6 6-6"/></svg>
|
After Width: | Height: | Size: 295 B |
1
assets/icons/arrow_up_from_line.svg
Normal file
1
assets/icons/arrow_up_from_line.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-from-line"><path d="m18 9-6-6-6 6"/><path d="M12 3v14"/><path d="M5 21h14"/></svg>
|
After Width: | Height: | Size: 294 B |
|
@ -124,6 +124,8 @@
|
||||||
"wrap_guides": [],
|
"wrap_guides": [],
|
||||||
// Hide the values of in variables from visual display in private files
|
// Hide the values of in variables from visual display in private files
|
||||||
"redact_private_values": false,
|
"redact_private_values": false,
|
||||||
|
// The default number of lines to expand excerpts in the multibuffer by.
|
||||||
|
"expand_excerpt_lines": 3,
|
||||||
// Globs to match against file paths to determine if a file is private.
|
// Globs to match against file paths to determine if a file is private.
|
||||||
"private_files": [
|
"private_files": [
|
||||||
"**/.env*",
|
"**/.env*",
|
||||||
|
|
|
@ -253,7 +253,7 @@ impl ToolView for AnnotationResultView {
|
||||||
MultiBuffer::new(0, language::Capability::ReadWrite).with_title(String::new())
|
MultiBuffer::new(0, language::Capability::ReadWrite).with_title(String::new())
|
||||||
});
|
});
|
||||||
let editor = cx.new_view(|cx| {
|
let editor = cx.new_view(|cx| {
|
||||||
Editor::for_multibuffer(multibuffer.clone(), Some(self.project.clone()), cx)
|
Editor::for_multibuffer(multibuffer.clone(), Some(self.project.clone()), true, cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
self.editor = Some(editor.clone());
|
self.editor = Some(editor.clone());
|
||||||
|
|
|
@ -237,8 +237,9 @@ fn view_release_notes_locally(workspace: &mut Workspace, cx: &mut ViewContext<Wo
|
||||||
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
|
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||||
|
|
||||||
let tab_description = SharedString::from(body.title.to_string());
|
let tab_description = SharedString::from(body.title.to_string());
|
||||||
let editor = cx
|
let editor = cx.new_view(|cx| {
|
||||||
.new_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx));
|
Editor::for_multibuffer(buffer, Some(project), true, cx)
|
||||||
|
});
|
||||||
let workspace_handle = workspace.weak_handle();
|
let workspace_handle = workspace.weak_handle();
|
||||||
let view: View<MarkdownPreviewView> = MarkdownPreviewView::new(
|
let view: View<MarkdownPreviewView> = MarkdownPreviewView::new(
|
||||||
MarkdownPreviewMode::Default,
|
MarkdownPreviewMode::Default,
|
||||||
|
|
|
@ -308,8 +308,9 @@ async fn test_basic_following(
|
||||||
result
|
result
|
||||||
});
|
});
|
||||||
let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
|
let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
|
||||||
let editor =
|
let editor = cx.new_view(|cx| {
|
||||||
cx.new_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
|
Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), true, cx)
|
||||||
|
});
|
||||||
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, cx);
|
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, cx);
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
|
|
|
@ -781,7 +781,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
multibuffer
|
multibuffer
|
||||||
});
|
});
|
||||||
let editor = cx.add_window(|cx| Editor::for_multibuffer(multibuffer, None, cx));
|
let editor = cx.add_window(|cx| Editor::for_multibuffer(multibuffer, None, true, cx));
|
||||||
editor.update(cx, |editor, cx| editor.focus(cx)).unwrap();
|
editor.update(cx, |editor, cx| editor.focus(cx)).unwrap();
|
||||||
let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
|
let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
|
||||||
editor
|
editor
|
||||||
|
@ -811,7 +811,7 @@ mod tests {
|
||||||
assert!(editor.has_active_inline_completion(cx));
|
assert!(editor.has_active_inline_completion(cx));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor.display_text(cx),
|
editor.display_text(cx),
|
||||||
"\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
|
"\n\n\na = 1\nb = 2 + a\n\n\n\n\n\nc = 3\nd = 4\n\n"
|
||||||
);
|
);
|
||||||
assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
|
assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
|
||||||
});
|
});
|
||||||
|
@ -833,7 +833,7 @@ mod tests {
|
||||||
assert!(!editor.has_active_inline_completion(cx));
|
assert!(!editor.has_active_inline_completion(cx));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor.display_text(cx),
|
editor.display_text(cx),
|
||||||
"\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
|
"\n\n\na = 1\nb = 2\n\n\n\n\n\nc = 3\nd = 4\n\n"
|
||||||
);
|
);
|
||||||
assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
|
assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
|
||||||
|
|
||||||
|
@ -842,7 +842,7 @@ mod tests {
|
||||||
assert!(!editor.has_active_inline_completion(cx));
|
assert!(!editor.has_active_inline_completion(cx));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor.display_text(cx),
|
editor.display_text(cx),
|
||||||
"\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
|
"\n\n\na = 1\nb = 2\n\n\n\n\n\nc = 3\nd = 4 \n\n"
|
||||||
);
|
);
|
||||||
assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
|
assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
|
||||||
});
|
});
|
||||||
|
@ -853,7 +853,7 @@ mod tests {
|
||||||
assert!(editor.has_active_inline_completion(cx));
|
assert!(editor.has_active_inline_completion(cx));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor.display_text(cx),
|
editor.display_text(cx),
|
||||||
"\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
|
"\n\n\na = 1\nb = 2\n\n\n\n\n\nc = 3\nd = 4 + c\n\n"
|
||||||
);
|
);
|
||||||
assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
|
assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
|
||||||
});
|
});
|
||||||
|
@ -1032,7 +1032,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
multibuffer
|
multibuffer
|
||||||
});
|
});
|
||||||
let editor = cx.add_window(|cx| Editor::for_multibuffer(multibuffer, None, cx));
|
let editor = cx.add_window(|cx| Editor::for_multibuffer(multibuffer, None, true, cx));
|
||||||
let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
|
let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
|
||||||
editor
|
editor
|
||||||
.update(cx, |editor, cx| {
|
.update(cx, |editor, cx| {
|
||||||
|
|
|
@ -161,7 +161,7 @@ impl ProjectDiagnosticsEditor {
|
||||||
});
|
});
|
||||||
let editor = cx.new_view(|cx| {
|
let editor = cx.new_view(|cx| {
|
||||||
let mut editor =
|
let mut editor =
|
||||||
Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), cx);
|
Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), false, cx);
|
||||||
editor.set_vertical_scroll_margin(5, cx);
|
editor.set_vertical_scroll_margin(5, cx);
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
|
@ -792,13 +792,15 @@ impl Item for ProjectDiagnosticsEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DIAGNOSTIC_HEADER: &'static str = "diagnostic header";
|
||||||
|
|
||||||
fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
|
fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
|
||||||
let (message, code_ranges) = highlight_diagnostic_message(&diagnostic);
|
let (message, code_ranges) = highlight_diagnostic_message(&diagnostic);
|
||||||
let message: SharedString = message;
|
let message: SharedString = message;
|
||||||
Box::new(move |cx| {
|
Box::new(move |cx| {
|
||||||
let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into();
|
let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into();
|
||||||
h_flex()
|
h_flex()
|
||||||
.id("diagnostic header")
|
.id(DIAGNOSTIC_HEADER)
|
||||||
.py_2()
|
.py_2()
|
||||||
.pl_10()
|
.pl_10()
|
||||||
.pr_5()
|
.pr_5()
|
||||||
|
|
|
@ -158,11 +158,11 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor_blocks(&editor, cx),
|
editor_blocks(&editor, cx),
|
||||||
[
|
[
|
||||||
(DisplayRow(0), "path header block".into()),
|
(DisplayRow(0), FILE_HEADER.into()),
|
||||||
(DisplayRow(2), "diagnostic header".into()),
|
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
|
||||||
(DisplayRow(15), "collapsed context".into()),
|
(DisplayRow(15), EXCERPT_HEADER.into()),
|
||||||
(DisplayRow(16), "diagnostic header".into()),
|
(DisplayRow(16), DIAGNOSTIC_HEADER.into()),
|
||||||
(DisplayRow(25), "collapsed context".into()),
|
(DisplayRow(25), EXCERPT_HEADER.into()),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -243,13 +243,13 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor_blocks(&editor, cx),
|
editor_blocks(&editor, cx),
|
||||||
[
|
[
|
||||||
(DisplayRow(0), "path header block".into()),
|
(DisplayRow(0), FILE_HEADER.into()),
|
||||||
(DisplayRow(2), "diagnostic header".into()),
|
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
|
||||||
(DisplayRow(7), "path header block".into()),
|
(DisplayRow(7), FILE_HEADER.into()),
|
||||||
(DisplayRow(9), "diagnostic header".into()),
|
(DisplayRow(9), DIAGNOSTIC_HEADER.into()),
|
||||||
(DisplayRow(22), "collapsed context".into()),
|
(DisplayRow(22), EXCERPT_HEADER.into()),
|
||||||
(DisplayRow(23), "diagnostic header".into()),
|
(DisplayRow(23), DIAGNOSTIC_HEADER.into()),
|
||||||
(DisplayRow(32), "collapsed context".into()),
|
(DisplayRow(32), EXCERPT_HEADER.into()),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -355,15 +355,15 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor_blocks(&editor, cx),
|
editor_blocks(&editor, cx),
|
||||||
[
|
[
|
||||||
(DisplayRow(0), "path header block".into()),
|
(DisplayRow(0), FILE_HEADER.into()),
|
||||||
(DisplayRow(2), "diagnostic header".into()),
|
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
|
||||||
(DisplayRow(7), "collapsed context".into()),
|
(DisplayRow(7), EXCERPT_HEADER.into()),
|
||||||
(DisplayRow(8), "diagnostic header".into()),
|
(DisplayRow(8), DIAGNOSTIC_HEADER.into()),
|
||||||
(DisplayRow(13), "path header block".into()),
|
(DisplayRow(13), FILE_HEADER.into()),
|
||||||
(DisplayRow(15), "diagnostic header".into()),
|
(DisplayRow(15), DIAGNOSTIC_HEADER.into()),
|
||||||
(DisplayRow(28), "collapsed context".into()),
|
(DisplayRow(28), EXCERPT_HEADER.into()),
|
||||||
(DisplayRow(29), "diagnostic header".into()),
|
(DisplayRow(29), DIAGNOSTIC_HEADER.into()),
|
||||||
(DisplayRow(38), "collapsed context".into()),
|
(DisplayRow(38), EXCERPT_HEADER.into()),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -493,8 +493,8 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor_blocks(&editor, cx),
|
editor_blocks(&editor, cx),
|
||||||
[
|
[
|
||||||
(DisplayRow(0), "path header block".into()),
|
(DisplayRow(0), FILE_HEADER.into()),
|
||||||
(DisplayRow(2), "diagnostic header".into()),
|
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -539,10 +539,10 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor_blocks(&editor, cx),
|
editor_blocks(&editor, cx),
|
||||||
[
|
[
|
||||||
(DisplayRow(0), "path header block".into()),
|
(DisplayRow(0), FILE_HEADER.into()),
|
||||||
(DisplayRow(2), "diagnostic header".into()),
|
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
|
||||||
(DisplayRow(6), "collapsed context".into()),
|
(DisplayRow(6), EXCERPT_HEADER.into()),
|
||||||
(DisplayRow(7), "diagnostic header".into()),
|
(DisplayRow(7), DIAGNOSTIC_HEADER.into()),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -605,10 +605,10 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor_blocks(&editor, cx),
|
editor_blocks(&editor, cx),
|
||||||
[
|
[
|
||||||
(DisplayRow(0), "path header block".into()),
|
(DisplayRow(0), FILE_HEADER.into()),
|
||||||
(DisplayRow(2), "diagnostic header".into()),
|
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
|
||||||
(DisplayRow(7), "collapsed context".into()),
|
(DisplayRow(7), EXCERPT_HEADER.into()),
|
||||||
(DisplayRow(8), "diagnostic header".into()),
|
(DisplayRow(8), DIAGNOSTIC_HEADER.into()),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -661,10 +661,10 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor_blocks(&editor, cx),
|
editor_blocks(&editor, cx),
|
||||||
[
|
[
|
||||||
(DisplayRow(0), "path header block".into()),
|
(DisplayRow(0), FILE_HEADER.into()),
|
||||||
(DisplayRow(2), "diagnostic header".into()),
|
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
|
||||||
(DisplayRow(7), "collapsed context".into()),
|
(DisplayRow(7), EXCERPT_HEADER.into()),
|
||||||
(DisplayRow(8), "diagnostic header".into()),
|
(DisplayRow(8), DIAGNOSTIC_HEADER.into()),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -958,6 +958,10 @@ fn random_diagnostic(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FILE_HEADER: &'static str = "file header";
|
||||||
|
const EXCERPT_HEADER: &'static str = "excerpt header";
|
||||||
|
const EXCERPT_FOOTER: &'static str = "excerpt footer";
|
||||||
|
|
||||||
fn editor_blocks(
|
fn editor_blocks(
|
||||||
editor: &View<Editor>,
|
editor: &View<Editor>,
|
||||||
cx: &mut VisualTestContext,
|
cx: &mut VisualTestContext,
|
||||||
|
@ -996,11 +1000,12 @@ fn editor_blocks(
|
||||||
starts_new_buffer, ..
|
starts_new_buffer, ..
|
||||||
} => {
|
} => {
|
||||||
if *starts_new_buffer {
|
if *starts_new_buffer {
|
||||||
"path header block".into()
|
FILE_HEADER.into()
|
||||||
} else {
|
} else {
|
||||||
"collapsed context".into()
|
EXCERPT_HEADER.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TransformBlock::ExcerptFooter { .. } => EXCERPT_FOOTER.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Some((row, name))
|
Some((row, name))
|
||||||
|
|
|
@ -114,12 +114,26 @@ pub struct ExpandExcerpts {
|
||||||
pub(super) lines: u32,
|
pub(super) lines: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||||
|
pub struct ExpandExcerptsUp {
|
||||||
|
#[serde(default)]
|
||||||
|
pub(super) lines: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||||
|
pub struct ExpandExcerptsDown {
|
||||||
|
#[serde(default)]
|
||||||
|
pub(super) lines: u32,
|
||||||
|
}
|
||||||
|
|
||||||
impl_actions!(
|
impl_actions!(
|
||||||
editor,
|
editor,
|
||||||
[
|
[
|
||||||
ConfirmCodeAction,
|
ConfirmCodeAction,
|
||||||
ConfirmCompletion,
|
ConfirmCompletion,
|
||||||
ExpandExcerpts,
|
ExpandExcerpts,
|
||||||
|
ExpandExcerptsUp,
|
||||||
|
ExpandExcerptsDown,
|
||||||
FoldAt,
|
FoldAt,
|
||||||
MoveDownByLines,
|
MoveDownByLines,
|
||||||
MovePageDown,
|
MovePageDown,
|
||||||
|
|
|
@ -112,8 +112,10 @@ impl DisplayMap {
|
||||||
font: Font,
|
font: Font,
|
||||||
font_size: Pixels,
|
font_size: Pixels,
|
||||||
wrap_width: Option<Pixels>,
|
wrap_width: Option<Pixels>,
|
||||||
|
show_excerpt_controls: bool,
|
||||||
buffer_header_height: u8,
|
buffer_header_height: u8,
|
||||||
excerpt_header_height: u8,
|
excerpt_header_height: u8,
|
||||||
|
excerpt_footer_height: u8,
|
||||||
fold_placeholder: FoldPlaceholder,
|
fold_placeholder: FoldPlaceholder,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -124,8 +126,15 @@ impl DisplayMap {
|
||||||
let (fold_map, snapshot) = FoldMap::new(snapshot);
|
let (fold_map, snapshot) = FoldMap::new(snapshot);
|
||||||
let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
|
let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
|
||||||
let (wrap_map, snapshot) = WrapMap::new(snapshot, font, font_size, wrap_width, cx);
|
let (wrap_map, snapshot) = WrapMap::new(snapshot, font, font_size, wrap_width, cx);
|
||||||
let block_map = BlockMap::new(snapshot, buffer_header_height, excerpt_header_height);
|
let block_map = BlockMap::new(
|
||||||
|
snapshot,
|
||||||
|
show_excerpt_controls,
|
||||||
|
buffer_header_height,
|
||||||
|
excerpt_header_height,
|
||||||
|
excerpt_footer_height,
|
||||||
|
);
|
||||||
let flap_map = FlapMap::default();
|
let flap_map = FlapMap::default();
|
||||||
|
|
||||||
cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
|
cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
|
||||||
|
|
||||||
DisplayMap {
|
DisplayMap {
|
||||||
|
@ -380,6 +389,10 @@ impl DisplayMap {
|
||||||
pub fn is_rewrapping(&self, cx: &gpui::AppContext) -> bool {
|
pub fn is_rewrapping(&self, cx: &gpui::AppContext) -> bool {
|
||||||
self.wrap_map.read(cx).is_rewrapping()
|
self.wrap_map.read(cx).is_rewrapping()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn show_excerpt_controls(&self) -> bool {
|
||||||
|
self.block_map.show_excerpt_controls()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
@ -1098,8 +1111,10 @@ pub mod tests {
|
||||||
font("Helvetica"),
|
font("Helvetica"),
|
||||||
font_size,
|
font_size,
|
||||||
wrap_width,
|
wrap_width,
|
||||||
|
true,
|
||||||
buffer_start_excerpt_header_height,
|
buffer_start_excerpt_header_height,
|
||||||
excerpt_header_height,
|
excerpt_header_height,
|
||||||
|
0,
|
||||||
FoldPlaceholder::test(),
|
FoldPlaceholder::test(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -1344,8 +1359,10 @@ pub mod tests {
|
||||||
font("Helvetica"),
|
font("Helvetica"),
|
||||||
font_size,
|
font_size,
|
||||||
wrap_width,
|
wrap_width,
|
||||||
|
true,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
0,
|
||||||
FoldPlaceholder::test(),
|
FoldPlaceholder::test(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -1453,8 +1470,10 @@ pub mod tests {
|
||||||
font("Helvetica"),
|
font("Helvetica"),
|
||||||
font_size,
|
font_size,
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
0,
|
||||||
FoldPlaceholder::test(),
|
FoldPlaceholder::test(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -1549,6 +1568,8 @@ pub mod tests {
|
||||||
font("Helvetica"),
|
font("Helvetica"),
|
||||||
font_size,
|
font_size,
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
|
1,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
FoldPlaceholder::test(),
|
FoldPlaceholder::test(),
|
||||||
|
@ -1650,8 +1671,10 @@ pub mod tests {
|
||||||
font("Courier"),
|
font("Courier"),
|
||||||
font_size,
|
font_size,
|
||||||
Some(px(40.0)),
|
Some(px(40.0)),
|
||||||
|
true,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
0,
|
||||||
FoldPlaceholder::test(),
|
FoldPlaceholder::test(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -1732,6 +1755,8 @@ pub mod tests {
|
||||||
font("Courier"),
|
font("Courier"),
|
||||||
font_size,
|
font_size,
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
|
1,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
FoldPlaceholder::test(),
|
FoldPlaceholder::test(),
|
||||||
|
@ -1856,8 +1881,10 @@ pub mod tests {
|
||||||
font("Helvetica"),
|
font("Helvetica"),
|
||||||
font_size,
|
font_size,
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
0,
|
||||||
FoldPlaceholder::test(),
|
FoldPlaceholder::test(),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -1893,8 +1920,10 @@ pub mod tests {
|
||||||
font("Helvetica"),
|
font("Helvetica"),
|
||||||
font_size,
|
font_size,
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
0,
|
||||||
FoldPlaceholder::test(),
|
FoldPlaceholder::test(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -1968,8 +1997,10 @@ pub mod tests {
|
||||||
font("Helvetica"),
|
font("Helvetica"),
|
||||||
font_size,
|
font_size,
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
0,
|
||||||
FoldPlaceholder::test(),
|
FoldPlaceholder::test(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,7 +12,7 @@ use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
cmp::{self, Ordering},
|
cmp::{self, Ordering},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
ops::{Deref, DerefMut, Range},
|
ops::{Deref, DerefMut, Range, RangeBounds},
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicUsize, Ordering::SeqCst},
|
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||||
Arc,
|
Arc,
|
||||||
|
@ -31,8 +31,10 @@ pub struct BlockMap {
|
||||||
wrap_snapshot: RefCell<WrapSnapshot>,
|
wrap_snapshot: RefCell<WrapSnapshot>,
|
||||||
blocks: Vec<Arc<Block>>,
|
blocks: Vec<Arc<Block>>,
|
||||||
transforms: RefCell<SumTree<Transform>>,
|
transforms: RefCell<SumTree<Transform>>,
|
||||||
|
show_excerpt_controls: bool,
|
||||||
buffer_header_height: u8,
|
buffer_header_height: u8,
|
||||||
excerpt_header_height: u8,
|
excerpt_header_height: u8,
|
||||||
|
excerpt_footer_height: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlockMapWriter<'a>(&'a mut BlockMap);
|
pub struct BlockMapWriter<'a>(&'a mut BlockMap);
|
||||||
|
@ -92,6 +94,7 @@ pub struct BlockContext<'a, 'b> {
|
||||||
pub editor_style: &'b EditorStyle,
|
pub editor_style: &'b EditorStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the block should be considered above or below the anchor line
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum BlockDisposition {
|
pub enum BlockDisposition {
|
||||||
Above,
|
Above,
|
||||||
|
@ -104,6 +107,17 @@ struct Transform {
|
||||||
block: Option<TransformBlock>,
|
block: Option<TransformBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) enum BlockType {
|
||||||
|
Custom(BlockId),
|
||||||
|
Header,
|
||||||
|
Footer,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait BlockLike {
|
||||||
|
fn block_type(&self) -> BlockType;
|
||||||
|
fn disposition(&self) -> BlockDisposition;
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum TransformBlock {
|
pub enum TransformBlock {
|
||||||
|
@ -114,7 +128,27 @@ pub enum TransformBlock {
|
||||||
range: ExcerptRange<text::Anchor>,
|
range: ExcerptRange<text::Anchor>,
|
||||||
height: u8,
|
height: u8,
|
||||||
starts_new_buffer: bool,
|
starts_new_buffer: bool,
|
||||||
|
show_excerpt_controls: bool,
|
||||||
},
|
},
|
||||||
|
ExcerptFooter {
|
||||||
|
id: ExcerptId,
|
||||||
|
disposition: BlockDisposition,
|
||||||
|
height: u8,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockLike for TransformBlock {
|
||||||
|
fn block_type(&self) -> BlockType {
|
||||||
|
match self {
|
||||||
|
TransformBlock::Custom(block) => BlockType::Custom(block.id),
|
||||||
|
TransformBlock::ExcerptHeader { .. } => BlockType::Header,
|
||||||
|
TransformBlock::ExcerptFooter { .. } => BlockType::Footer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disposition(&self) -> BlockDisposition {
|
||||||
|
self.disposition()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransformBlock {
|
impl TransformBlock {
|
||||||
|
@ -122,6 +156,7 @@ impl TransformBlock {
|
||||||
match self {
|
match self {
|
||||||
TransformBlock::Custom(block) => block.disposition,
|
TransformBlock::Custom(block) => block.disposition,
|
||||||
TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above,
|
TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above,
|
||||||
|
TransformBlock::ExcerptFooter { disposition, .. } => *disposition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +164,7 @@ impl TransformBlock {
|
||||||
match self {
|
match self {
|
||||||
TransformBlock::Custom(block) => block.height,
|
TransformBlock::Custom(block) => block.height,
|
||||||
TransformBlock::ExcerptHeader { height, .. } => *height,
|
TransformBlock::ExcerptHeader { height, .. } => *height,
|
||||||
|
TransformBlock::ExcerptFooter { height, .. } => *height,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,9 +173,23 @@ impl Debug for TransformBlock {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(),
|
Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(),
|
||||||
Self::ExcerptHeader { buffer, .. } => f
|
Self::ExcerptHeader {
|
||||||
|
buffer,
|
||||||
|
starts_new_buffer,
|
||||||
|
id,
|
||||||
|
..
|
||||||
|
} => f
|
||||||
.debug_struct("ExcerptHeader")
|
.debug_struct("ExcerptHeader")
|
||||||
|
.field("id", &id)
|
||||||
.field("path", &buffer.file().map(|f| f.path()))
|
.field("path", &buffer.file().map(|f| f.path()))
|
||||||
|
.field("starts_new_buffer", &starts_new_buffer)
|
||||||
|
.finish(),
|
||||||
|
TransformBlock::ExcerptFooter {
|
||||||
|
id, disposition, ..
|
||||||
|
} => f
|
||||||
|
.debug_struct("ExcerptFooter")
|
||||||
|
.field("id", &id)
|
||||||
|
.field("disposition", &disposition)
|
||||||
.finish(),
|
.finish(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,8 +220,10 @@ pub struct BlockBufferRows<'a> {
|
||||||
impl BlockMap {
|
impl BlockMap {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
wrap_snapshot: WrapSnapshot,
|
wrap_snapshot: WrapSnapshot,
|
||||||
|
show_excerpt_controls: bool,
|
||||||
buffer_header_height: u8,
|
buffer_header_height: u8,
|
||||||
excerpt_header_height: u8,
|
excerpt_header_height: u8,
|
||||||
|
excerpt_footer_height: u8,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let row_count = wrap_snapshot.max_point().row() + 1;
|
let row_count = wrap_snapshot.max_point().row() + 1;
|
||||||
let map = Self {
|
let map = Self {
|
||||||
|
@ -179,8 +231,10 @@ impl BlockMap {
|
||||||
blocks: Vec::new(),
|
blocks: Vec::new(),
|
||||||
transforms: RefCell::new(SumTree::from_item(Transform::isomorphic(row_count), &())),
|
transforms: RefCell::new(SumTree::from_item(Transform::isomorphic(row_count), &())),
|
||||||
wrap_snapshot: RefCell::new(wrap_snapshot.clone()),
|
wrap_snapshot: RefCell::new(wrap_snapshot.clone()),
|
||||||
|
show_excerpt_controls,
|
||||||
buffer_header_height,
|
buffer_header_height,
|
||||||
excerpt_header_height,
|
excerpt_header_height,
|
||||||
|
excerpt_footer_height,
|
||||||
};
|
};
|
||||||
map.sync(
|
map.sync(
|
||||||
&wrap_snapshot,
|
&wrap_snapshot,
|
||||||
|
@ -364,49 +418,20 @@ impl BlockMap {
|
||||||
(position.row(), TransformBlock::Custom(block.clone()))
|
(position.row(), TransformBlock::Custom(block.clone()))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
if buffer.show_headers() {
|
if buffer.show_headers() {
|
||||||
blocks_in_edit.extend(
|
blocks_in_edit.extend(BlockMap::header_blocks(
|
||||||
buffer
|
self.show_excerpt_controls,
|
||||||
.excerpt_boundaries_in_range((start_bound, end_bound))
|
self.excerpt_footer_height,
|
||||||
.map(|excerpt_boundary| {
|
self.buffer_header_height,
|
||||||
(
|
self.excerpt_header_height,
|
||||||
wrap_snapshot
|
buffer,
|
||||||
.make_wrap_point(
|
(start_bound, end_bound),
|
||||||
Point::new(excerpt_boundary.row.0, 0),
|
wrap_snapshot,
|
||||||
Bias::Left,
|
));
|
||||||
)
|
|
||||||
.row(),
|
|
||||||
TransformBlock::ExcerptHeader {
|
|
||||||
id: excerpt_boundary.id,
|
|
||||||
buffer: excerpt_boundary.buffer,
|
|
||||||
range: excerpt_boundary.range,
|
|
||||||
height: if excerpt_boundary.starts_new_buffer {
|
|
||||||
self.buffer_header_height
|
|
||||||
} else {
|
|
||||||
self.excerpt_header_height
|
|
||||||
},
|
|
||||||
starts_new_buffer: excerpt_boundary.starts_new_buffer,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place excerpt headers above custom blocks on the same row.
|
BlockMap::sort_blocks(&mut blocks_in_edit);
|
||||||
blocks_in_edit.sort_unstable_by(|(row_a, block_a), (row_b, block_b)| {
|
|
||||||
row_a.cmp(row_b).then_with(|| match (block_a, block_b) {
|
|
||||||
(
|
|
||||||
TransformBlock::ExcerptHeader { .. },
|
|
||||||
TransformBlock::ExcerptHeader { .. },
|
|
||||||
) => Ordering::Equal,
|
|
||||||
(TransformBlock::ExcerptHeader { .. }, _) => Ordering::Less,
|
|
||||||
(_, TransformBlock::ExcerptHeader { .. }) => Ordering::Greater,
|
|
||||||
(TransformBlock::Custom(block_a), TransformBlock::Custom(block_b)) => block_a
|
|
||||||
.disposition
|
|
||||||
.cmp(&block_b.disposition)
|
|
||||||
.then_with(|| block_a.id.cmp(&block_b.id)),
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
// For each of these blocks, insert a new isomorphic transform preceding the block,
|
// For each of these blocks, insert a new isomorphic transform preceding the block,
|
||||||
// and then insert the block itself.
|
// and then insert the block itself.
|
||||||
|
@ -449,6 +474,95 @@ impl BlockMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn show_excerpt_controls(&self) -> bool {
|
||||||
|
self.show_excerpt_controls
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_blocks<'a, 'b: 'a, 'c: 'a + 'b, R, T>(
|
||||||
|
show_excerpt_controls: bool,
|
||||||
|
excerpt_footer_height: u8,
|
||||||
|
buffer_header_height: u8,
|
||||||
|
excerpt_header_height: u8,
|
||||||
|
buffer: &'b multi_buffer::MultiBufferSnapshot,
|
||||||
|
range: R,
|
||||||
|
wrap_snapshot: &'c WrapSnapshot,
|
||||||
|
) -> impl Iterator<Item = (u32, TransformBlock)> + 'b
|
||||||
|
where
|
||||||
|
R: RangeBounds<T>,
|
||||||
|
T: multi_buffer::ToOffset,
|
||||||
|
{
|
||||||
|
buffer
|
||||||
|
.excerpt_boundaries_in_range(range)
|
||||||
|
.flat_map(move |excerpt_boundary| {
|
||||||
|
let wrap_row = wrap_snapshot
|
||||||
|
.make_wrap_point(Point::new(excerpt_boundary.row.0, 0), Bias::Left)
|
||||||
|
.row();
|
||||||
|
|
||||||
|
[
|
||||||
|
show_excerpt_controls
|
||||||
|
.then(|| {
|
||||||
|
excerpt_boundary.prev.as_ref().map(|prev| {
|
||||||
|
(
|
||||||
|
wrap_row,
|
||||||
|
TransformBlock::ExcerptFooter {
|
||||||
|
id: prev.id,
|
||||||
|
height: excerpt_footer_height,
|
||||||
|
disposition: if excerpt_boundary.next.is_some() {
|
||||||
|
BlockDisposition::Above
|
||||||
|
} else {
|
||||||
|
BlockDisposition::Below
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.flatten(),
|
||||||
|
excerpt_boundary.next.map(|next| {
|
||||||
|
let starts_new_buffer = excerpt_boundary
|
||||||
|
.prev
|
||||||
|
.map_or(true, |prev| prev.buffer_id != next.buffer_id);
|
||||||
|
|
||||||
|
(
|
||||||
|
wrap_row,
|
||||||
|
TransformBlock::ExcerptHeader {
|
||||||
|
id: next.id,
|
||||||
|
buffer: next.buffer,
|
||||||
|
range: next.range,
|
||||||
|
height: if starts_new_buffer {
|
||||||
|
buffer_header_height
|
||||||
|
} else {
|
||||||
|
excerpt_header_height
|
||||||
|
},
|
||||||
|
starts_new_buffer,
|
||||||
|
show_excerpt_controls,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sort_blocks<B: BlockLike>(blocks: &mut Vec<(u32, B)>) {
|
||||||
|
// Place excerpt headers and footers above custom blocks on the same row
|
||||||
|
blocks.sort_unstable_by(|(row_a, block_a), (row_b, block_b)| {
|
||||||
|
row_a.cmp(row_b).then_with(|| {
|
||||||
|
block_a
|
||||||
|
.disposition()
|
||||||
|
.cmp(&block_b.disposition())
|
||||||
|
.then_with(|| match ((block_a.block_type()), (block_b.block_type())) {
|
||||||
|
(BlockType::Footer, BlockType::Footer) => Ordering::Equal,
|
||||||
|
(BlockType::Footer, _) => Ordering::Less,
|
||||||
|
(_, BlockType::Footer) => Ordering::Greater,
|
||||||
|
(BlockType::Header, BlockType::Header) => Ordering::Equal,
|
||||||
|
(BlockType::Header, _) => Ordering::Less,
|
||||||
|
(_, BlockType::Header) => Ordering::Greater,
|
||||||
|
(BlockType::Custom(a_id), BlockType::Custom(b_id)) => a_id.cmp(&b_id),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_isomorphic(tree: &mut SumTree<Transform>, rows: u32) {
|
fn push_isomorphic(tree: &mut SumTree<Transform>, rows: u32) {
|
||||||
|
@ -996,6 +1110,8 @@ fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::env;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::display_map::inlay_map::InlayMap;
|
use crate::display_map::inlay_map::InlayMap;
|
||||||
use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
|
use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
|
||||||
|
@ -1003,7 +1119,6 @@ mod tests {
|
||||||
use multi_buffer::MultiBuffer;
|
use multi_buffer::MultiBuffer;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use std::env;
|
|
||||||
use util::RandomCharIter;
|
use util::RandomCharIter;
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
@ -1034,7 +1149,7 @@ mod tests {
|
||||||
let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
|
let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
|
||||||
let (wrap_map, wraps_snapshot) =
|
let (wrap_map, wraps_snapshot) =
|
||||||
cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
|
cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
|
||||||
let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
|
let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
|
||||||
|
|
||||||
let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
|
let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
|
||||||
let block_ids = writer.insert(vec![
|
let block_ids = writer.insert(vec![
|
||||||
|
@ -1206,7 +1321,7 @@ mod tests {
|
||||||
let (_, wraps_snapshot) = cx.update(|cx| {
|
let (_, wraps_snapshot) = cx.update(|cx| {
|
||||||
WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
|
WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
|
||||||
});
|
});
|
||||||
let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
|
let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 0);
|
||||||
|
|
||||||
let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
|
let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
|
||||||
writer.insert(vec![
|
writer.insert(vec![
|
||||||
|
@ -1252,9 +1367,11 @@ mod tests {
|
||||||
let font_size = px(14.0);
|
let font_size = px(14.0);
|
||||||
let buffer_start_header_height = rng.gen_range(1..=5);
|
let buffer_start_header_height = rng.gen_range(1..=5);
|
||||||
let excerpt_header_height = rng.gen_range(1..=5);
|
let excerpt_header_height = rng.gen_range(1..=5);
|
||||||
|
let excerpt_footer_height = rng.gen_range(1..=5);
|
||||||
|
|
||||||
log::info!("Wrap width: {:?}", wrap_width);
|
log::info!("Wrap width: {:?}", wrap_width);
|
||||||
log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
|
log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
|
||||||
|
log::info!("Excerpt Footer Height: {:?}", excerpt_footer_height);
|
||||||
|
|
||||||
let buffer = if rng.gen() {
|
let buffer = if rng.gen() {
|
||||||
let len = rng.gen_range(0..10);
|
let len = rng.gen_range(0..10);
|
||||||
|
@ -1273,8 +1390,10 @@ mod tests {
|
||||||
.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx));
|
.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx));
|
||||||
let mut block_map = BlockMap::new(
|
let mut block_map = BlockMap::new(
|
||||||
wraps_snapshot,
|
wraps_snapshot,
|
||||||
|
true,
|
||||||
buffer_start_header_height,
|
buffer_start_header_height,
|
||||||
excerpt_header_height,
|
excerpt_header_height,
|
||||||
|
excerpt_footer_height,
|
||||||
);
|
);
|
||||||
let mut custom_blocks = Vec::new();
|
let mut custom_blocks = Vec::new();
|
||||||
|
|
||||||
|
@ -1410,24 +1529,23 @@ mod tests {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
expected_blocks.extend(buffer_snapshot.excerpt_boundaries_in_range(0..).map(
|
|
||||||
|boundary| {
|
// Note that this needs to be synced with the related section in BlockMap::sync
|
||||||
let position =
|
expected_blocks.extend(
|
||||||
wraps_snapshot.make_wrap_point(Point::new(boundary.row.0, 0), Bias::Left);
|
BlockMap::header_blocks(
|
||||||
(
|
true,
|
||||||
position.row(),
|
excerpt_footer_height,
|
||||||
ExpectedBlock::ExcerptHeader {
|
buffer_start_header_height,
|
||||||
height: if boundary.starts_new_buffer {
|
excerpt_header_height,
|
||||||
buffer_start_header_height
|
&buffer_snapshot,
|
||||||
} else {
|
0..,
|
||||||
excerpt_header_height
|
&wraps_snapshot,
|
||||||
},
|
)
|
||||||
starts_new_buffer: boundary.starts_new_buffer,
|
.map(|(row, block)| (row, block.into())),
|
||||||
},
|
);
|
||||||
)
|
|
||||||
},
|
BlockMap::sort_blocks(&mut expected_blocks);
|
||||||
));
|
|
||||||
expected_blocks.sort_unstable();
|
|
||||||
let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
|
let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
|
||||||
|
|
||||||
let input_buffer_rows = buffer_snapshot
|
let input_buffer_rows = buffer_snapshot
|
||||||
|
@ -1593,12 +1711,16 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
enum ExpectedBlock {
|
enum ExpectedBlock {
|
||||||
ExcerptHeader {
|
ExcerptHeader {
|
||||||
height: u8,
|
height: u8,
|
||||||
starts_new_buffer: bool,
|
starts_new_buffer: bool,
|
||||||
},
|
},
|
||||||
|
ExcerptFooter {
|
||||||
|
height: u8,
|
||||||
|
disposition: BlockDisposition,
|
||||||
|
},
|
||||||
Custom {
|
Custom {
|
||||||
disposition: BlockDisposition,
|
disposition: BlockDisposition,
|
||||||
id: BlockId,
|
id: BlockId,
|
||||||
|
@ -1606,11 +1728,26 @@ mod tests {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BlockLike for ExpectedBlock {
|
||||||
|
fn block_type(&self) -> BlockType {
|
||||||
|
match self {
|
||||||
|
ExpectedBlock::Custom { id, .. } => BlockType::Custom(*id),
|
||||||
|
ExpectedBlock::ExcerptHeader { .. } => BlockType::Header,
|
||||||
|
ExpectedBlock::ExcerptFooter { .. } => BlockType::Footer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disposition(&self) -> BlockDisposition {
|
||||||
|
self.disposition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ExpectedBlock {
|
impl ExpectedBlock {
|
||||||
fn height(&self) -> u8 {
|
fn height(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
ExpectedBlock::ExcerptHeader { height, .. } => *height,
|
ExpectedBlock::ExcerptHeader { height, .. } => *height,
|
||||||
ExpectedBlock::Custom { height, .. } => *height,
|
ExpectedBlock::Custom { height, .. } => *height,
|
||||||
|
ExpectedBlock::ExcerptFooter { height, .. } => *height,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1618,6 +1755,7 @@ mod tests {
|
||||||
match self {
|
match self {
|
||||||
ExpectedBlock::ExcerptHeader { .. } => BlockDisposition::Above,
|
ExpectedBlock::ExcerptHeader { .. } => BlockDisposition::Above,
|
||||||
ExpectedBlock::Custom { disposition, .. } => *disposition,
|
ExpectedBlock::Custom { disposition, .. } => *disposition,
|
||||||
|
ExpectedBlock::ExcerptFooter { disposition, .. } => *disposition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1638,6 +1776,14 @@ mod tests {
|
||||||
height,
|
height,
|
||||||
starts_new_buffer,
|
starts_new_buffer,
|
||||||
},
|
},
|
||||||
|
TransformBlock::ExcerptFooter {
|
||||||
|
height,
|
||||||
|
disposition,
|
||||||
|
..
|
||||||
|
} => ExpectedBlock::ExcerptFooter {
|
||||||
|
height,
|
||||||
|
disposition,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1654,6 +1800,7 @@ mod tests {
|
||||||
match self {
|
match self {
|
||||||
TransformBlock::Custom(block) => Some(block),
|
TransformBlock::Custom(block) => Some(block),
|
||||||
TransformBlock::ExcerptHeader { .. } => None,
|
TransformBlock::ExcerptHeader { .. } => None,
|
||||||
|
TransformBlock::ExcerptFooter { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ pub use multi_buffer::{
|
||||||
Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
|
Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
|
||||||
ToPoint,
|
ToPoint,
|
||||||
};
|
};
|
||||||
use multi_buffer::{MultiBufferPoint, MultiBufferRow, ToOffsetUtf16};
|
use multi_buffer::{ExpandExcerptDirection, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use project::project_settings::{GitGutterSetting, ProjectSettings};
|
use project::project_settings::{GitGutterSetting, ProjectSettings};
|
||||||
|
@ -1529,19 +1529,25 @@ impl Editor {
|
||||||
pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
|
pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
|
||||||
let buffer = cx.new_model(|cx| Buffer::local("", cx));
|
let buffer = cx.new_model(|cx| Buffer::local("", cx));
|
||||||
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
|
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||||
Self::new(EditorMode::SingleLine, buffer, None, cx)
|
Self::new(EditorMode::SingleLine, buffer, None, false, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
|
pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
|
||||||
let buffer = cx.new_model(|cx| Buffer::local("", cx));
|
let buffer = cx.new_model(|cx| Buffer::local("", cx));
|
||||||
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
|
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||||
Self::new(EditorMode::Full, buffer, None, cx)
|
Self::new(EditorMode::Full, buffer, None, false, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
|
pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
|
||||||
let buffer = cx.new_model(|cx| Buffer::local("", cx));
|
let buffer = cx.new_model(|cx| Buffer::local("", cx));
|
||||||
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
|
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||||
Self::new(EditorMode::AutoHeight { max_lines }, buffer, None, cx)
|
Self::new(
|
||||||
|
EditorMode::AutoHeight { max_lines },
|
||||||
|
buffer,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn for_buffer(
|
pub fn for_buffer(
|
||||||
|
@ -1550,19 +1556,27 @@ impl Editor {
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
|
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||||
Self::new(EditorMode::Full, buffer, project, cx)
|
Self::new(EditorMode::Full, buffer, project, false, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn for_multibuffer(
|
pub fn for_multibuffer(
|
||||||
buffer: Model<MultiBuffer>,
|
buffer: Model<MultiBuffer>,
|
||||||
project: Option<Model<Project>>,
|
project: Option<Model<Project>>,
|
||||||
|
show_excerpt_controls: bool,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(EditorMode::Full, buffer, project, cx)
|
Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
|
pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
|
||||||
let mut clone = Self::new(self.mode, self.buffer.clone(), self.project.clone(), cx);
|
let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
|
||||||
|
let mut clone = Self::new(
|
||||||
|
self.mode,
|
||||||
|
self.buffer.clone(),
|
||||||
|
self.project.clone(),
|
||||||
|
show_excerpt_controls,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
self.display_map.update(cx, |display_map, cx| {
|
self.display_map.update(cx, |display_map, cx| {
|
||||||
let snapshot = display_map.snapshot(cx);
|
let snapshot = display_map.snapshot(cx);
|
||||||
clone.display_map.update(cx, |display_map, cx| {
|
clone.display_map.update(cx, |display_map, cx| {
|
||||||
|
@ -1579,6 +1593,7 @@ impl Editor {
|
||||||
mode: EditorMode,
|
mode: EditorMode,
|
||||||
buffer: Model<MultiBuffer>,
|
buffer: Model<MultiBuffer>,
|
||||||
project: Option<Model<Project>>,
|
project: Option<Model<Project>>,
|
||||||
|
show_excerpt_controls: bool,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let style = cx.text_style();
|
let style = cx.text_style();
|
||||||
|
@ -1615,12 +1630,16 @@ impl Editor {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
let display_map = cx.new_model(|cx| {
|
let display_map = cx.new_model(|cx| {
|
||||||
|
let file_header_size = if show_excerpt_controls { 3 } else { 2 };
|
||||||
|
|
||||||
DisplayMap::new(
|
DisplayMap::new(
|
||||||
buffer.clone(),
|
buffer.clone(),
|
||||||
style.font(),
|
style.font(),
|
||||||
font_size,
|
font_size,
|
||||||
None,
|
None,
|
||||||
2,
|
show_excerpt_controls,
|
||||||
|
file_header_size,
|
||||||
|
1,
|
||||||
1,
|
1,
|
||||||
fold_placeholder,
|
fold_placeholder,
|
||||||
cx,
|
cx,
|
||||||
|
@ -4287,7 +4306,7 @@ impl Editor {
|
||||||
workspace.update(&mut cx, |workspace, cx| {
|
workspace.update(&mut cx, |workspace, cx| {
|
||||||
let project = workspace.project().clone();
|
let project = workspace.project().clone();
|
||||||
let editor =
|
let editor =
|
||||||
cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
|
cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
|
||||||
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, cx);
|
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, cx);
|
||||||
editor.update(cx, |editor, cx| {
|
editor.update(cx, |editor, cx| {
|
||||||
editor.highlight_background::<Self>(
|
editor.highlight_background::<Self>(
|
||||||
|
@ -8127,9 +8146,34 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
|
pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
|
||||||
|
self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expand_excerpts_down(
|
||||||
|
&mut self,
|
||||||
|
action: &ExpandExcerptsDown,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
|
||||||
|
self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expand_excerpts_for_direction(
|
||||||
|
&mut self,
|
||||||
|
lines: u32,
|
||||||
|
direction: ExpandExcerptDirection,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
let selections = self.selections.disjoint_anchors();
|
let selections = self.selections.disjoint_anchors();
|
||||||
|
|
||||||
let lines = if action.lines == 0 { 3 } else { action.lines };
|
let lines = if lines == 0 {
|
||||||
|
EditorSettings::get_global(cx).expand_excerpt_lines
|
||||||
|
} else {
|
||||||
|
lines
|
||||||
|
};
|
||||||
|
|
||||||
self.buffer.update(cx, |buffer, cx| {
|
self.buffer.update(cx, |buffer, cx| {
|
||||||
buffer.expand_excerpts(
|
buffer.expand_excerpts(
|
||||||
|
@ -8138,14 +8182,22 @@ impl Editor {
|
||||||
.map(|selection| selection.head().excerpt_id)
|
.map(|selection| selection.head().excerpt_id)
|
||||||
.dedup(),
|
.dedup(),
|
||||||
lines,
|
lines,
|
||||||
|
direction,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_excerpt(&mut self, excerpt: ExcerptId, cx: &mut ViewContext<Self>) {
|
pub fn expand_excerpt(
|
||||||
self.buffer
|
&mut self,
|
||||||
.update(cx, |buffer, cx| buffer.expand_excerpts([excerpt], 3, cx))
|
excerpt: ExcerptId,
|
||||||
|
direction: ExpandExcerptDirection,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
|
||||||
|
self.buffer.update(cx, |buffer, cx| {
|
||||||
|
buffer.expand_excerpts([excerpt], lines, direction, cx)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
|
fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
|
||||||
|
@ -8792,7 +8844,7 @@ impl Editor {
|
||||||
});
|
});
|
||||||
|
|
||||||
let editor = cx.new_view(|cx| {
|
let editor = cx.new_view(|cx| {
|
||||||
Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
|
Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
|
||||||
});
|
});
|
||||||
editor.update(cx, |editor, cx| {
|
editor.update(cx, |editor, cx| {
|
||||||
editor.highlight_background::<Self>(
|
editor.highlight_background::<Self>(
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub struct EditorSettings {
|
||||||
pub seed_search_query_from_cursor: SeedQuerySetting,
|
pub seed_search_query_from_cursor: SeedQuerySetting,
|
||||||
pub multi_cursor_modifier: MultiCursorModifier,
|
pub multi_cursor_modifier: MultiCursorModifier,
|
||||||
pub redact_private_values: bool,
|
pub redact_private_values: bool,
|
||||||
|
pub expand_excerpt_lines: u32,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub double_click_in_multibuffer: DoubleClickInMultibuffer,
|
pub double_click_in_multibuffer: DoubleClickInMultibuffer,
|
||||||
}
|
}
|
||||||
|
@ -182,6 +183,11 @@ pub struct EditorSettingsContent {
|
||||||
/// Default: false
|
/// Default: false
|
||||||
pub redact_private_values: Option<bool>,
|
pub redact_private_values: Option<bool>,
|
||||||
|
|
||||||
|
/// How many lines to expand the multibuffer excerpts by default
|
||||||
|
///
|
||||||
|
/// Default: 3
|
||||||
|
pub expand_excerpt_lines: Option<u32>,
|
||||||
|
|
||||||
/// What to do when multibuffer is double clicked in some of its excerpts
|
/// What to do when multibuffer is double clicked in some of its excerpts
|
||||||
/// (parts of singleton buffers).
|
/// (parts of singleton buffers).
|
||||||
///
|
///
|
||||||
|
|
|
@ -4292,10 +4292,10 @@ async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
|
||||||
let mut cx = EditorTestContext::new_multibuffer(
|
let mut cx = EditorTestContext::new_multibuffer(
|
||||||
cx,
|
cx,
|
||||||
[
|
[
|
||||||
indoc! {
|
&indoc! {
|
||||||
"aaa\n«bbb\nccc\n»ddd"
|
"aaa\n«bbb\nccc\n»ddd"
|
||||||
},
|
},
|
||||||
indoc! {
|
&indoc! {
|
||||||
"aaa\n«bbb\nccc\n»ddd"
|
"aaa\n«bbb\nccc\n»ddd"
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -6033,8 +6033,15 @@ async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
|
||||||
);
|
);
|
||||||
multi_buffer
|
multi_buffer
|
||||||
});
|
});
|
||||||
let multi_buffer_editor =
|
let multi_buffer_editor = cx.new_view(|cx| {
|
||||||
cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
|
Editor::new(
|
||||||
|
EditorMode::Full,
|
||||||
|
multi_buffer,
|
||||||
|
Some(project.clone()),
|
||||||
|
true,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
multi_buffer_editor.update(cx, |editor, cx| {
|
multi_buffer_editor.update(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
|
editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
|
||||||
|
@ -9430,8 +9437,15 @@ async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
|
||||||
let project = Project::test(fs, ["/a".as_ref()], cx).await;
|
let project = Project::test(fs, ["/a".as_ref()], cx).await;
|
||||||
let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
|
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
|
||||||
let multi_buffer_editor =
|
let multi_buffer_editor = cx.new_view(|cx| {
|
||||||
cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
|
Editor::new(
|
||||||
|
EditorMode::Full,
|
||||||
|
multi_buffer,
|
||||||
|
Some(project.clone()),
|
||||||
|
true,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
let multibuffer_item_id = workspace
|
let multibuffer_item_id = workspace
|
||||||
.update(cx, |workspace, cx| {
|
.update(cx, |workspace, cx| {
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -10358,28 +10372,18 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext)
|
||||||
let project = Project::test(fs, ["/a".as_ref()], cx).await;
|
let project = Project::test(fs, ["/a".as_ref()], cx).await;
|
||||||
let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
|
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
|
||||||
let multi_buffer_editor =
|
let multi_buffer_editor = cx.new_view(|cx| {
|
||||||
cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
|
Editor::new(
|
||||||
|
EditorMode::Full,
|
||||||
|
multi_buffer,
|
||||||
|
Some(project.clone()),
|
||||||
|
true,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
cx.executor().run_until_parked();
|
cx.executor().run_until_parked();
|
||||||
|
|
||||||
let expected_all_hunks = vec![
|
let expected_all_hunks = vec![
|
||||||
(
|
|
||||||
"bbbb\n".to_string(),
|
|
||||||
DiffHunkStatus::Removed,
|
|
||||||
DisplayRow(3)..DisplayRow(3),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"nnnn\n".to_string(),
|
|
||||||
DiffHunkStatus::Modified,
|
|
||||||
DisplayRow(16)..DisplayRow(17),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"".to_string(),
|
|
||||||
DiffHunkStatus::Added,
|
|
||||||
DisplayRow(31)..DisplayRow(32),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
let expected_all_hunks_shifted = vec![
|
|
||||||
(
|
(
|
||||||
"bbbb\n".to_string(),
|
"bbbb\n".to_string(),
|
||||||
DiffHunkStatus::Removed,
|
DiffHunkStatus::Removed,
|
||||||
|
@ -10388,12 +10392,29 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext)
|
||||||
(
|
(
|
||||||
"nnnn\n".to_string(),
|
"nnnn\n".to_string(),
|
||||||
DiffHunkStatus::Modified,
|
DiffHunkStatus::Modified,
|
||||||
DisplayRow(18)..DisplayRow(19),
|
DisplayRow(21)..DisplayRow(22),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
DiffHunkStatus::Added,
|
DiffHunkStatus::Added,
|
||||||
DisplayRow(33)..DisplayRow(34),
|
DisplayRow(41)..DisplayRow(42),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
let expected_all_hunks_shifted = vec![
|
||||||
|
(
|
||||||
|
"bbbb\n".to_string(),
|
||||||
|
DiffHunkStatus::Removed,
|
||||||
|
DisplayRow(5)..DisplayRow(5),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"nnnn\n".to_string(),
|
||||||
|
DiffHunkStatus::Modified,
|
||||||
|
DisplayRow(23)..DisplayRow(24),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"".to_string(),
|
||||||
|
DiffHunkStatus::Added,
|
||||||
|
DisplayRow(43)..DisplayRow(44),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -10418,8 +10439,8 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expanded_hunks_background_highlights(editor, cx),
|
expanded_hunks_background_highlights(editor, cx),
|
||||||
vec![
|
vec![
|
||||||
DisplayRow(18)..=DisplayRow(18),
|
DisplayRow(23)..=DisplayRow(23),
|
||||||
DisplayRow(33)..=DisplayRow(33)
|
DisplayRow(43)..=DisplayRow(43)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
assert_eq!(all_hunks, expected_all_hunks_shifted);
|
assert_eq!(all_hunks, expected_all_hunks_shifted);
|
||||||
|
@ -10450,8 +10471,8 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expanded_hunks_background_highlights(editor, cx),
|
expanded_hunks_background_highlights(editor, cx),
|
||||||
vec![
|
vec![
|
||||||
DisplayRow(18)..=DisplayRow(18),
|
DisplayRow(23)..=DisplayRow(23),
|
||||||
DisplayRow(33)..=DisplayRow(33)
|
DisplayRow(43)..=DisplayRow(43)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
assert_eq!(all_hunks, expected_all_hunks_shifted);
|
assert_eq!(all_hunks, expected_all_hunks_shifted);
|
||||||
|
|
|
@ -30,11 +30,11 @@ use gpui::{
|
||||||
anchored, deferred, div, fill, outline, point, px, quad, relative, size, svg,
|
anchored, deferred, div, fill, outline, point, px, quad, relative, size, svg,
|
||||||
transparent_black, Action, AnchorCorner, AnyElement, AvailableSpace, Bounds, ClipboardItem,
|
transparent_black, Action, AnchorCorner, AnyElement, AvailableSpace, Bounds, ClipboardItem,
|
||||||
ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity,
|
ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity,
|
||||||
FontId, GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, ModifiersChangedEvent,
|
FontId, GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, Length,
|
||||||
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels,
|
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
|
||||||
ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveElement,
|
ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size,
|
||||||
Style, Styled, TextRun, TextStyle, TextStyleRefinement, View, ViewContext, WeakView,
|
StatefulInteractiveElement, Style, Styled, TextRun, TextStyle, TextStyleRefinement, View,
|
||||||
WindowContext,
|
ViewContext, WeakView, WindowContext,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::language_settings::{
|
use language::language_settings::{
|
||||||
|
@ -278,6 +278,8 @@ impl EditorElement {
|
||||||
register_action(view, cx, Editor::redo_selection);
|
register_action(view, cx, Editor::redo_selection);
|
||||||
if !view.read(cx).is_singleton(cx) {
|
if !view.read(cx).is_singleton(cx) {
|
||||||
register_action(view, cx, Editor::expand_excerpts);
|
register_action(view, cx, Editor::expand_excerpts);
|
||||||
|
register_action(view, cx, Editor::expand_excerpts_up);
|
||||||
|
register_action(view, cx, Editor::expand_excerpts_down);
|
||||||
}
|
}
|
||||||
register_action(view, cx, Editor::go_to_diagnostic);
|
register_action(view, cx, Editor::go_to_diagnostic);
|
||||||
register_action(view, cx, Editor::go_to_prev_diagnostic);
|
register_action(view, cx, Editor::go_to_prev_diagnostic);
|
||||||
|
@ -1893,6 +1895,7 @@ impl EditorElement {
|
||||||
.partition::<Vec<_>, _>(|(_, block)| match block {
|
.partition::<Vec<_>, _>(|(_, block)| match block {
|
||||||
TransformBlock::ExcerptHeader { .. } => false,
|
TransformBlock::ExcerptHeader { .. } => false,
|
||||||
TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed,
|
TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed,
|
||||||
|
TransformBlock::ExcerptFooter { .. } => false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let render_block = |block: &TransformBlock,
|
let render_block = |block: &TransformBlock,
|
||||||
|
@ -1933,6 +1936,7 @@ impl EditorElement {
|
||||||
starts_new_buffer,
|
starts_new_buffer,
|
||||||
height,
|
height,
|
||||||
id,
|
id,
|
||||||
|
show_excerpt_controls,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let include_root = self
|
let include_root = self
|
||||||
|
@ -1986,6 +1990,9 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let icon_offset = gutter_dimensions.width
|
||||||
|
- (gutter_dimensions.left_padding + gutter_dimensions.margin);
|
||||||
|
|
||||||
let element = if *starts_new_buffer {
|
let element = if *starts_new_buffer {
|
||||||
let path = buffer.resolve_file_path(cx, include_root);
|
let path = buffer.resolve_file_path(cx, include_root);
|
||||||
let mut filename = None;
|
let mut filename = None;
|
||||||
|
@ -1998,15 +2005,16 @@ impl EditorElement {
|
||||||
.map(|p| SharedString::from(p.to_string_lossy().to_string() + "/"));
|
.map(|p| SharedString::from(p.to_string_lossy().to_string() + "/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let header_padding = px(6.0);
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.id(("path header container", block_id))
|
.id(("path excerpt header", block_id))
|
||||||
.size_full()
|
.size_full()
|
||||||
.justify_center()
|
.p(header_padding)
|
||||||
.p(gpui::px(6.))
|
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
|
.flex_basis(Length::Definite(DefiniteLength::Fraction(0.667)))
|
||||||
.id("path header block")
|
.id("path header block")
|
||||||
.size_full()
|
|
||||||
.pl(gpui::px(12.))
|
.pl(gpui::px(12.))
|
||||||
.pr(gpui::px(8.))
|
.pr(gpui::px(8.))
|
||||||
.rounded_md()
|
.rounded_md()
|
||||||
|
@ -2059,9 +2067,56 @@ impl EditorElement {
|
||||||
}))
|
}))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
.children(show_excerpt_controls.then(|| {
|
||||||
|
h_flex()
|
||||||
|
.flex_basis(Length::Definite(DefiniteLength::Fraction(0.333)))
|
||||||
|
.pt_1()
|
||||||
|
.justify_end()
|
||||||
|
.flex_none()
|
||||||
|
.w(icon_offset - header_padding)
|
||||||
|
.child(
|
||||||
|
ButtonLike::new("expand-icon")
|
||||||
|
.style(ButtonStyle::Transparent)
|
||||||
|
.child(
|
||||||
|
svg()
|
||||||
|
.path(IconName::ArrowUpFromLine.path())
|
||||||
|
.size(IconSize::XSmall.rems())
|
||||||
|
.text_color(
|
||||||
|
cx.theme().colors().editor_line_number,
|
||||||
|
)
|
||||||
|
.group("")
|
||||||
|
.hover(|style| {
|
||||||
|
style.text_color(
|
||||||
|
cx.theme()
|
||||||
|
.colors()
|
||||||
|
.editor_active_line_number,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on_click(cx.listener_for(&self.editor, {
|
||||||
|
let id = *id;
|
||||||
|
move |editor, _, cx| {
|
||||||
|
editor.expand_excerpt(
|
||||||
|
id,
|
||||||
|
multi_buffer::ExpandExcerptDirection::Up,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.tooltip({
|
||||||
|
move |cx| {
|
||||||
|
Tooltip::for_action(
|
||||||
|
"Expand Excerpt",
|
||||||
|
&ExpandExcerpts { lines: 0 },
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}))
|
||||||
} else {
|
} else {
|
||||||
v_flex()
|
v_flex()
|
||||||
.id(("collapsed context", block_id))
|
.id(("excerpt header", block_id))
|
||||||
.size_full()
|
.size_full()
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
@ -2085,44 +2140,94 @@ impl EditorElement {
|
||||||
h_flex()
|
h_flex()
|
||||||
.justify_end()
|
.justify_end()
|
||||||
.flex_none()
|
.flex_none()
|
||||||
.w(
|
.w(icon_offset)
|
||||||
gutter_dimensions.width - (gutter_dimensions.left_padding), // + gutter_dimensions.right_padding)
|
|
||||||
)
|
|
||||||
.h_full()
|
.h_full()
|
||||||
.child(
|
.child(
|
||||||
ButtonLike::new("expand-icon")
|
show_excerpt_controls.then(|| {
|
||||||
.style(ButtonStyle::Transparent)
|
ButtonLike::new("expand-icon")
|
||||||
.child(
|
.style(ButtonStyle::Transparent)
|
||||||
svg()
|
.child(
|
||||||
.path(IconName::ExpandVertical.path())
|
svg()
|
||||||
.size(IconSize::XSmall.rems())
|
.path(IconName::ArrowUpFromLine.path())
|
||||||
.text_color(
|
.size(IconSize::XSmall.rems())
|
||||||
cx.theme().colors().editor_line_number,
|
.text_color(
|
||||||
)
|
cx.theme().colors().editor_line_number,
|
||||||
.group("")
|
|
||||||
.hover(|style| {
|
|
||||||
style.text_color(
|
|
||||||
cx.theme()
|
|
||||||
.colors()
|
|
||||||
.editor_active_line_number,
|
|
||||||
)
|
)
|
||||||
}),
|
.group("")
|
||||||
)
|
.hover(|style| {
|
||||||
.on_click(cx.listener_for(&self.editor, {
|
style.text_color(
|
||||||
let id = *id;
|
cx.theme()
|
||||||
move |editor, _, cx| {
|
.colors()
|
||||||
editor.expand_excerpt(id, cx);
|
.editor_active_line_number,
|
||||||
}
|
)
|
||||||
}))
|
}),
|
||||||
.tooltip({
|
)
|
||||||
move |cx| {
|
.on_click(cx.listener_for(&self.editor, {
|
||||||
Tooltip::for_action(
|
let id = *id;
|
||||||
"Expand Excerpt",
|
move |editor, _, cx| {
|
||||||
&ExpandExcerpts { lines: 0 },
|
editor.expand_excerpt(
|
||||||
cx,
|
id,
|
||||||
)
|
multi_buffer::ExpandExcerptDirection::Up,
|
||||||
}
|
cx,
|
||||||
}),
|
);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.tooltip({
|
||||||
|
move |cx| {
|
||||||
|
Tooltip::for_action(
|
||||||
|
"Expand Excerpt",
|
||||||
|
&ExpandExcerpts { lines: 0 },
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).unwrap_or_else(|| {
|
||||||
|
ButtonLike::new("jump-icon")
|
||||||
|
.style(ButtonStyle::Transparent)
|
||||||
|
.child(
|
||||||
|
svg()
|
||||||
|
.path(IconName::ArrowUpRight.path())
|
||||||
|
.size(IconSize::XSmall.rems())
|
||||||
|
.text_color(
|
||||||
|
cx.theme().colors().border_variant,
|
||||||
|
)
|
||||||
|
.group("excerpt-jump-action")
|
||||||
|
.group_hover("excerpt-jump-action", |style| {
|
||||||
|
style.text_color(
|
||||||
|
cx.theme().colors().border
|
||||||
|
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.when_some(jump_data.clone(), |this, jump_data| {
|
||||||
|
this.on_click(cx.listener_for(&self.editor, {
|
||||||
|
let path = jump_data.path.clone();
|
||||||
|
move |editor, _, cx| {
|
||||||
|
cx.stop_propagation();
|
||||||
|
|
||||||
|
editor.jump(
|
||||||
|
path.clone(),
|
||||||
|
jump_data.position,
|
||||||
|
jump_data.anchor,
|
||||||
|
jump_data.line_offset_from_top,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.tooltip(move |cx| {
|
||||||
|
Tooltip::for_action(
|
||||||
|
format!(
|
||||||
|
"Jump to {}:L{}",
|
||||||
|
jump_data.path.path.display(),
|
||||||
|
jump_data.position.row + 1
|
||||||
|
),
|
||||||
|
&OpenExcerpts,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.group("excerpt-jump-action")
|
.group("excerpt-jump-action")
|
||||||
|
@ -2157,6 +2262,53 @@ impl EditorElement {
|
||||||
};
|
};
|
||||||
element.into_any()
|
element.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransformBlock::ExcerptFooter { id, .. } => {
|
||||||
|
let element = v_flex().id(("excerpt footer", block_id)).size_full().child(
|
||||||
|
h_flex()
|
||||||
|
.justify_end()
|
||||||
|
.flex_none()
|
||||||
|
.w(gutter_dimensions.width
|
||||||
|
- (gutter_dimensions.left_padding + gutter_dimensions.margin))
|
||||||
|
.h_full()
|
||||||
|
.child(
|
||||||
|
ButtonLike::new("expand-icon")
|
||||||
|
.style(ButtonStyle::Transparent)
|
||||||
|
.child(
|
||||||
|
svg()
|
||||||
|
.path(IconName::ArrowDownFromLine.path())
|
||||||
|
.size(IconSize::XSmall.rems())
|
||||||
|
.text_color(cx.theme().colors().editor_line_number)
|
||||||
|
.group("")
|
||||||
|
.hover(|style| {
|
||||||
|
style.text_color(
|
||||||
|
cx.theme().colors().editor_active_line_number,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on_click(cx.listener_for(&self.editor, {
|
||||||
|
let id = *id;
|
||||||
|
move |editor, _, cx| {
|
||||||
|
editor.expand_excerpt(
|
||||||
|
id,
|
||||||
|
multi_buffer::ExpandExcerptDirection::Down,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.tooltip({
|
||||||
|
move |cx| {
|
||||||
|
Tooltip::for_action(
|
||||||
|
"Expand Excerpt",
|
||||||
|
&ExpandExcerpts { lines: 0 },
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
element.into_any()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let size = element.layout_as_root(available_space, cx);
|
let size = element.layout_as_root(available_space, cx);
|
||||||
|
@ -2184,6 +2336,7 @@ impl EditorElement {
|
||||||
let style = match block {
|
let style = match block {
|
||||||
TransformBlock::Custom(block) => block.style(),
|
TransformBlock::Custom(block) => block.style(),
|
||||||
TransformBlock::ExcerptHeader { .. } => BlockStyle::Sticky,
|
TransformBlock::ExcerptHeader { .. } => BlockStyle::Sticky,
|
||||||
|
TransformBlock::ExcerptFooter { .. } => BlockStyle::Sticky,
|
||||||
};
|
};
|
||||||
let width = match style {
|
let width = match style {
|
||||||
BlockStyle::Sticky => hitbox.size.width,
|
BlockStyle::Sticky => hitbox.size.width,
|
||||||
|
@ -5413,7 +5566,7 @@ mod tests {
|
||||||
init_test(cx, |_| {});
|
init_test(cx, |_| {});
|
||||||
let window = cx.add_window(|cx| {
|
let window = cx.add_window(|cx| {
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
|
||||||
Editor::new(EditorMode::Full, buffer, None, cx)
|
Editor::new(EditorMode::Full, buffer, None, true, cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
let editor = window.root(cx).unwrap();
|
let editor = window.root(cx).unwrap();
|
||||||
|
@ -5491,7 +5644,7 @@ mod tests {
|
||||||
|
|
||||||
let window = cx.add_window(|cx| {
|
let window = cx.add_window(|cx| {
|
||||||
let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx);
|
let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx);
|
||||||
Editor::new(EditorMode::Full, buffer, None, cx)
|
Editor::new(EditorMode::Full, buffer, None, true, cx)
|
||||||
});
|
});
|
||||||
let cx = &mut VisualTestContext::from_window(*window, cx);
|
let cx = &mut VisualTestContext::from_window(*window, cx);
|
||||||
let editor = window.root(cx).unwrap();
|
let editor = window.root(cx).unwrap();
|
||||||
|
@ -5556,21 +5709,26 @@ mod tests {
|
||||||
// multi-buffer support
|
// multi-buffer support
|
||||||
// in DisplayPoint coordinates, this is what we're dealing with:
|
// in DisplayPoint coordinates, this is what we're dealing with:
|
||||||
// 0: [[file
|
// 0: [[file
|
||||||
// 1: header]]
|
// 1: header
|
||||||
// 2: aaaaaa
|
// 2: section]]
|
||||||
// 3: bbbbbb
|
// 3: aaaaaa
|
||||||
// 4: cccccc
|
// 4: bbbbbb
|
||||||
// 5:
|
// 5: cccccc
|
||||||
// 6: ...
|
// 6:
|
||||||
// 7: ffffff
|
// 7: [[footer]]
|
||||||
// 8: gggggg
|
// 8: [[header]]
|
||||||
// 9: hhhhhh
|
// 9: ffffff
|
||||||
// 10:
|
// 10: gggggg
|
||||||
// 11: [[file
|
// 11: hhhhhh
|
||||||
// 12: header]]
|
// 12:
|
||||||
// 13: bbbbbb
|
// 13: [[footer]]
|
||||||
// 14: cccccc
|
// 14: [[file
|
||||||
// 15: dddddd
|
// 15: header
|
||||||
|
// 16: section]]
|
||||||
|
// 17: bbbbbb
|
||||||
|
// 18: cccccc
|
||||||
|
// 19: dddddd
|
||||||
|
// 20: [[footer]]
|
||||||
let window = cx.add_window(|cx| {
|
let window = cx.add_window(|cx| {
|
||||||
let buffer = MultiBuffer::build_multi(
|
let buffer = MultiBuffer::build_multi(
|
||||||
[
|
[
|
||||||
|
@ -5588,7 +5746,7 @@ mod tests {
|
||||||
],
|
],
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
Editor::new(EditorMode::Full, buffer, None, cx)
|
Editor::new(EditorMode::Full, buffer, None, true, cx)
|
||||||
});
|
});
|
||||||
let editor = window.root(cx).unwrap();
|
let editor = window.root(cx).unwrap();
|
||||||
let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
|
let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
|
||||||
|
@ -5613,21 +5771,21 @@ mod tests {
|
||||||
// and doesn't allow selection to bleed through
|
// and doesn't allow selection to bleed through
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
local_selections[0].range,
|
local_selections[0].range,
|
||||||
DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(6), 0)
|
DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(7), 0)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
local_selections[0].head,
|
local_selections[0].head,
|
||||||
DisplayPoint::new(DisplayRow(5), 0)
|
DisplayPoint::new(DisplayRow(6), 0)
|
||||||
);
|
);
|
||||||
// moves cursor on buffer boundary back two lines
|
// moves cursor on buffer boundary back two lines
|
||||||
// and doesn't allow selection to bleed through
|
// and doesn't allow selection to bleed through
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
local_selections[1].range,
|
local_selections[1].range,
|
||||||
DisplayPoint::new(DisplayRow(10), 0)..DisplayPoint::new(DisplayRow(11), 0)
|
DisplayPoint::new(DisplayRow(10), 0)..DisplayPoint::new(DisplayRow(13), 0)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
local_selections[1].head,
|
local_selections[1].head,
|
||||||
DisplayPoint::new(DisplayRow(10), 0)
|
DisplayPoint::new(DisplayRow(12), 0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5637,7 +5795,7 @@ mod tests {
|
||||||
|
|
||||||
let window = cx.add_window(|cx| {
|
let window = cx.add_window(|cx| {
|
||||||
let buffer = MultiBuffer::build_simple("", cx);
|
let buffer = MultiBuffer::build_simple("", cx);
|
||||||
Editor::new(EditorMode::Full, buffer, None, cx)
|
Editor::new(EditorMode::Full, buffer, None, true, cx)
|
||||||
});
|
});
|
||||||
let cx = &mut VisualTestContext::from_window(*window, cx);
|
let cx = &mut VisualTestContext::from_window(*window, cx);
|
||||||
let editor = window.root(cx).unwrap();
|
let editor = window.root(cx).unwrap();
|
||||||
|
@ -5835,7 +5993,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
let window = cx.add_window(|cx| {
|
let window = cx.add_window(|cx| {
|
||||||
let buffer = MultiBuffer::build_simple(&input_text, cx);
|
let buffer = MultiBuffer::build_simple(&input_text, cx);
|
||||||
Editor::new(editor_mode, buffer, None, cx)
|
Editor::new(editor_mode, buffer, None, true, cx)
|
||||||
});
|
});
|
||||||
let cx = &mut VisualTestContext::from_window(*window, cx);
|
let cx = &mut VisualTestContext::from_window(*window, cx);
|
||||||
let editor = window.root(cx).unwrap();
|
let editor = window.root(cx).unwrap();
|
||||||
|
|
|
@ -572,7 +572,7 @@ fn editor_with_deleted_text(
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut editor = Editor::for_multibuffer(multi_buffer, None, cx);
|
let mut editor = Editor::for_multibuffer(multi_buffer, None, true, cx);
|
||||||
editor.soft_wrap_mode_override = Some(language::language_settings::SoftWrap::None);
|
editor.soft_wrap_mode_override = Some(language::language_settings::SoftWrap::None);
|
||||||
editor.show_wrap_guides = Some(false);
|
editor.show_wrap_guides = Some(false);
|
||||||
editor.show_gutter = false;
|
editor.show_gutter = false;
|
||||||
|
|
|
@ -2662,8 +2662,8 @@ pub mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.executor().run_until_parked();
|
cx.executor().run_until_parked();
|
||||||
let editor =
|
let editor = cx
|
||||||
cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx));
|
.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), true, cx));
|
||||||
|
|
||||||
let editor_edited = Arc::new(AtomicBool::new(false));
|
let editor_edited = Arc::new(AtomicBool::new(false));
|
||||||
let fake_server = fake_servers.next().await.unwrap();
|
let fake_server = fake_servers.next().await.unwrap();
|
||||||
|
@ -2871,6 +2871,7 @@ pub mod tests {
|
||||||
"main hint #5".to_string(),
|
"main hint #5".to_string(),
|
||||||
"other hint(edited) #0".to_string(),
|
"other hint(edited) #0".to_string(),
|
||||||
"other hint(edited) #1".to_string(),
|
"other hint(edited) #1".to_string(),
|
||||||
|
"other hint(edited) #2".to_string(),
|
||||||
];
|
];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_hints,
|
expected_hints,
|
||||||
|
@ -2881,8 +2882,8 @@ pub mod tests {
|
||||||
assert_eq!(expected_hints, visible_hint_labels(editor, cx));
|
assert_eq!(expected_hints, visible_hint_labels(editor, cx));
|
||||||
|
|
||||||
let current_cache_version = editor.inlay_hint_cache().version;
|
let current_cache_version = editor.inlay_hint_cache().version;
|
||||||
// We expect two new hints for the excerpts from `other.rs`:
|
// We expect three new hints for the excerpts from `other.rs`:
|
||||||
let expected_version = last_scroll_update_version + 2;
|
let expected_version = last_scroll_update_version + 3;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
current_cache_version,
|
current_cache_version,
|
||||||
expected_version,
|
expected_version,
|
||||||
|
@ -2970,8 +2971,8 @@ pub mod tests {
|
||||||
assert!(!buffer_2_excerpts.is_empty());
|
assert!(!buffer_2_excerpts.is_empty());
|
||||||
|
|
||||||
cx.executor().run_until_parked();
|
cx.executor().run_until_parked();
|
||||||
let editor =
|
let editor = cx
|
||||||
cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx));
|
.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), true, cx));
|
||||||
let editor_edited = Arc::new(AtomicBool::new(false));
|
let editor_edited = Arc::new(AtomicBool::new(false));
|
||||||
let fake_server = fake_servers.next().await.unwrap();
|
let fake_server = fake_servers.next().await.unwrap();
|
||||||
let closure_editor_edited = Arc::clone(&editor_edited);
|
let closure_editor_edited = Arc::clone(&editor_edited);
|
||||||
|
|
|
@ -137,7 +137,7 @@ impl FollowableItem for Editor {
|
||||||
|
|
||||||
cx.new_view(|cx| {
|
cx.new_view(|cx| {
|
||||||
let mut editor =
|
let mut editor =
|
||||||
Editor::for_multibuffer(multibuffer, Some(project.clone()), cx);
|
Editor::for_multibuffer(multibuffer, Some(project.clone()), true, cx);
|
||||||
editor.remote_id = Some(remote_id);
|
editor.remote_id = Some(remote_id);
|
||||||
editor
|
editor
|
||||||
})
|
})
|
||||||
|
@ -1162,23 +1162,26 @@ impl SearchableItem for Editor {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for excerpt in buffer.excerpt_boundaries_in_range(0..buffer.len()) {
|
for excerpt in buffer.excerpt_boundaries_in_range(0..buffer.len()) {
|
||||||
let excerpt_range = excerpt.range.context.to_offset(&excerpt.buffer);
|
if let Some(next_excerpt) = excerpt.next {
|
||||||
ranges.extend(
|
let excerpt_range =
|
||||||
query
|
next_excerpt.range.context.to_offset(&next_excerpt.buffer);
|
||||||
.search(&excerpt.buffer, Some(excerpt_range.clone()))
|
ranges.extend(
|
||||||
.await
|
query
|
||||||
.into_iter()
|
.search(&next_excerpt.buffer, Some(excerpt_range.clone()))
|
||||||
.map(|range| {
|
.await
|
||||||
let start = excerpt
|
.into_iter()
|
||||||
.buffer
|
.map(|range| {
|
||||||
.anchor_after(excerpt_range.start + range.start);
|
let start = next_excerpt
|
||||||
let end = excerpt
|
.buffer
|
||||||
.buffer
|
.anchor_after(excerpt_range.start + range.start);
|
||||||
.anchor_before(excerpt_range.start + range.end);
|
let end = next_excerpt
|
||||||
buffer.anchor_in_excerpt(excerpt.id, start).unwrap()
|
.buffer
|
||||||
..buffer.anchor_in_excerpt(excerpt.id, end).unwrap()
|
.anchor_before(excerpt_range.start + range.end);
|
||||||
}),
|
buffer.anchor_in_excerpt(next_excerpt.id, start).unwrap()
|
||||||
);
|
..buffer.anchor_in_excerpt(next_excerpt.id, end).unwrap()
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ranges
|
ranges
|
||||||
|
|
|
@ -695,12 +695,15 @@ mod tests {
|
||||||
let font_size = px(14.0);
|
let font_size = px(14.0);
|
||||||
let buffer = MultiBuffer::build_simple(input_text, cx);
|
let buffer = MultiBuffer::build_simple(input_text, cx);
|
||||||
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
||||||
|
|
||||||
let display_map = cx.new_model(|cx| {
|
let display_map = cx.new_model(|cx| {
|
||||||
DisplayMap::new(
|
DisplayMap::new(
|
||||||
buffer,
|
buffer,
|
||||||
font,
|
font,
|
||||||
font_size,
|
font_size,
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
|
1,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
FoldPlaceholder::test(),
|
FoldPlaceholder::test(),
|
||||||
|
@ -917,8 +920,10 @@ mod tests {
|
||||||
font,
|
font,
|
||||||
px(14.0),
|
px(14.0),
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
2,
|
2,
|
||||||
2,
|
2,
|
||||||
|
0,
|
||||||
FoldPlaceholder::test(),
|
FoldPlaceholder::test(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
|
|
@ -109,7 +109,9 @@ pub fn expand_macro_recursively(
|
||||||
MultiBuffer::singleton(buffer, cx).with_title(macro_expansion.name)
|
MultiBuffer::singleton(buffer, cx).with_title(macro_expansion.name)
|
||||||
});
|
});
|
||||||
workspace.add_item_to_active_pane(
|
workspace.add_item_to_active_pane(
|
||||||
Box::new(cx.new_view(|cx| Editor::for_multibuffer(multibuffer, Some(project), cx))),
|
Box::new(
|
||||||
|
cx.new_view(|cx| Editor::for_multibuffer(multibuffer, Some(project), true, cx)),
|
||||||
|
),
|
||||||
None,
|
None,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
|
@ -39,6 +39,8 @@ pub fn marked_display_snapshot(
|
||||||
font,
|
font,
|
||||||
font_size,
|
font_size,
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
|
1,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
FoldPlaceholder::test(),
|
FoldPlaceholder::test(),
|
||||||
|
@ -74,7 +76,7 @@ pub fn assert_text_with_selections(
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub(crate) fn build_editor(buffer: Model<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
|
pub(crate) fn build_editor(buffer: Model<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
|
||||||
Editor::new(EditorMode::Full, buffer, None, cx)
|
Editor::new(EditorMode::Full, buffer, None, true, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_editor_with_project(
|
pub(crate) fn build_editor_with_project(
|
||||||
|
@ -82,7 +84,7 @@ pub(crate) fn build_editor_with_project(
|
||||||
buffer: Model<MultiBuffer>,
|
buffer: Model<MultiBuffer>,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> Editor {
|
) -> Editor {
|
||||||
Editor::new(EditorMode::Full, buffer, Some(project), cx)
|
Editor::new(EditorMode::Full, buffer, Some(project), true, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
|
|
@ -22,6 +22,7 @@ use std::{
|
||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use ui::Context;
|
use ui::Context;
|
||||||
use util::{
|
use util::{
|
||||||
assert_set_eq,
|
assert_set_eq,
|
||||||
|
@ -149,6 +150,10 @@ impl EditorTestContext {
|
||||||
self.multibuffer(|buffer, cx| buffer.snapshot(cx).text())
|
self.multibuffer(|buffer, cx| buffer.snapshot(cx).text())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn display_text(&mut self) -> String {
|
||||||
|
self.update_editor(|editor, cx| editor.display_text(cx))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn buffer<F, T>(&mut self, read: F) -> T
|
pub fn buffer<F, T>(&mut self, read: F) -> T
|
||||||
where
|
where
|
||||||
F: FnOnce(&Buffer, &AppContext) -> T,
|
F: FnOnce(&Buffer, &AppContext) -> T,
|
||||||
|
|
|
@ -18,6 +18,7 @@ use language::{
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{
|
||||||
|
any::type_name,
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cell::{Ref, RefCell},
|
cell::{Ref, RefCell},
|
||||||
cmp, fmt,
|
cmp, fmt,
|
||||||
|
@ -173,17 +174,40 @@ pub struct MultiBufferSnapshot {
|
||||||
show_headers: bool,
|
show_headers: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A boundary between [`Excerpt`]s in a [`MultiBuffer`]
|
pub struct ExcerptInfo {
|
||||||
pub struct ExcerptBoundary {
|
|
||||||
pub id: ExcerptId,
|
pub id: ExcerptId,
|
||||||
pub row: MultiBufferRow,
|
|
||||||
pub buffer: BufferSnapshot,
|
pub buffer: BufferSnapshot,
|
||||||
|
pub buffer_id: BufferId,
|
||||||
pub range: ExcerptRange<text::Anchor>,
|
pub range: ExcerptRange<text::Anchor>,
|
||||||
/// It's possible to have multiple excerpts in the same buffer,
|
}
|
||||||
/// and they are rendered together without a new File header.
|
|
||||||
///
|
impl std::fmt::Debug for ExcerptInfo {
|
||||||
/// This flag indicates that the excerpt is the first one in the buffer.
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
pub starts_new_buffer: bool,
|
f.debug_struct(type_name::<Self>())
|
||||||
|
.field("id", &self.id)
|
||||||
|
.field("buffer_id", &self.buffer_id)
|
||||||
|
.field("range", &self.range)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A boundary between [`Excerpt`]s in a [`MultiBuffer`]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ExcerptBoundary {
|
||||||
|
pub prev: Option<ExcerptInfo>,
|
||||||
|
pub next: Option<ExcerptInfo>,
|
||||||
|
/// The row in the `MultiBuffer` where the boundary is located
|
||||||
|
pub row: MultiBufferRow,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExcerptBoundary {
|
||||||
|
pub fn starts_new_buffer(&self) -> bool {
|
||||||
|
match (self.prev.as_ref(), self.next.as_ref()) {
|
||||||
|
(None, _) => true,
|
||||||
|
(Some(_), None) => false,
|
||||||
|
(Some(prev), Some(next)) => prev.buffer_id != next.buffer_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`].
|
/// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`].
|
||||||
|
@ -281,6 +305,30 @@ struct ExcerptBytes<'a> {
|
||||||
reversed: bool,
|
reversed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ExpandExcerptDirection {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
UpAndDown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExpandExcerptDirection {
|
||||||
|
pub fn should_expand_up(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
ExpandExcerptDirection::Up => true,
|
||||||
|
ExpandExcerptDirection::Down => false,
|
||||||
|
ExpandExcerptDirection::UpAndDown => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_expand_down(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
ExpandExcerptDirection::Up => false,
|
||||||
|
ExpandExcerptDirection::Down => true,
|
||||||
|
ExpandExcerptDirection::UpAndDown => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct MultiBufferIndentGuide {
|
pub struct MultiBufferIndentGuide {
|
||||||
pub multibuffer_row_range: Range<MultiBufferRow>,
|
pub multibuffer_row_range: Range<MultiBufferRow>,
|
||||||
|
@ -1610,6 +1658,7 @@ impl MultiBuffer {
|
||||||
&mut self,
|
&mut self,
|
||||||
ids: impl IntoIterator<Item = ExcerptId>,
|
ids: impl IntoIterator<Item = ExcerptId>,
|
||||||
line_count: u32,
|
line_count: u32,
|
||||||
|
direction: ExpandExcerptDirection,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) {
|
) {
|
||||||
if line_count == 0 {
|
if line_count == 0 {
|
||||||
|
@ -1630,26 +1679,40 @@ impl MultiBuffer {
|
||||||
let mut excerpt = cursor.item().unwrap().clone();
|
let mut excerpt = cursor.item().unwrap().clone();
|
||||||
let old_text_len = excerpt.text_summary.len;
|
let old_text_len = excerpt.text_summary.len;
|
||||||
|
|
||||||
|
let up_line_count = if direction.should_expand_up() {
|
||||||
|
line_count
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
let start_row = excerpt
|
let start_row = excerpt
|
||||||
.range
|
.range
|
||||||
.context
|
.context
|
||||||
.start
|
.start
|
||||||
.to_point(&excerpt.buffer)
|
.to_point(&excerpt.buffer)
|
||||||
.row
|
.row
|
||||||
.saturating_sub(line_count);
|
.saturating_sub(up_line_count);
|
||||||
let start_point = Point::new(start_row, 0);
|
let start_point = Point::new(start_row, 0);
|
||||||
excerpt.range.context.start = excerpt.buffer.anchor_before(start_point);
|
excerpt.range.context.start = excerpt.buffer.anchor_before(start_point);
|
||||||
|
|
||||||
let end_point = excerpt.buffer.clip_point(
|
let down_line_count = if direction.should_expand_down() {
|
||||||
excerpt.range.context.end.to_point(&excerpt.buffer) + Point::new(line_count, 0),
|
line_count
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut end_point = excerpt.buffer.clip_point(
|
||||||
|
excerpt.range.context.end.to_point(&excerpt.buffer)
|
||||||
|
+ Point::new(down_line_count, 0),
|
||||||
Bias::Left,
|
Bias::Left,
|
||||||
);
|
);
|
||||||
|
end_point.column = excerpt.buffer.line_len(end_point.row);
|
||||||
excerpt.range.context.end = excerpt.buffer.anchor_after(end_point);
|
excerpt.range.context.end = excerpt.buffer.anchor_after(end_point);
|
||||||
excerpt.max_buffer_row = end_point.row;
|
excerpt.max_buffer_row = end_point.row;
|
||||||
|
|
||||||
excerpt.text_summary = excerpt
|
excerpt.text_summary = excerpt
|
||||||
.buffer
|
.buffer
|
||||||
.text_summary_for_range(start_point..end_point);
|
.text_summary_for_range(excerpt.range.context.clone());
|
||||||
|
|
||||||
let new_start_offset = new_excerpts.summary().text.len;
|
let new_start_offset = new_excerpts.summary().text.len;
|
||||||
let old_start_offset = cursor.start().1;
|
let old_start_offset = cursor.start().1;
|
||||||
|
@ -1920,7 +1983,12 @@ impl MultiBuffer {
|
||||||
|
|
||||||
log::info!("Expanding excerpts {excerpts:?} by {line_count} lines");
|
log::info!("Expanding excerpts {excerpts:?} by {line_count} lines");
|
||||||
|
|
||||||
self.expand_excerpts(excerpts.iter().cloned(), line_count, cx);
|
self.expand_excerpts(
|
||||||
|
excerpts.iter().cloned(),
|
||||||
|
line_count,
|
||||||
|
ExpandExcerptDirection::UpAndDown,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3018,24 +3086,37 @@ impl MultiBufferSnapshot {
|
||||||
cursor.next(&());
|
cursor.next(&());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut prev_buffer_id = cursor.prev_item().map(|excerpt| excerpt.buffer_id);
|
let mut visited_end = false;
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
if self.singleton {
|
if self.singleton {
|
||||||
None
|
None
|
||||||
} else if bounds.contains(&cursor.start().0) {
|
} else if bounds.contains(&cursor.start().0) {
|
||||||
let excerpt = cursor.item()?;
|
let next = cursor.item().map(|excerpt| ExcerptInfo {
|
||||||
let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id;
|
|
||||||
let boundary = ExcerptBoundary {
|
|
||||||
id: excerpt.id,
|
id: excerpt.id,
|
||||||
row: MultiBufferRow(cursor.start().1.row),
|
|
||||||
buffer: excerpt.buffer.clone(),
|
buffer: excerpt.buffer.clone(),
|
||||||
|
buffer_id: excerpt.buffer_id,
|
||||||
range: excerpt.range.clone(),
|
range: excerpt.range.clone(),
|
||||||
starts_new_buffer,
|
});
|
||||||
};
|
|
||||||
|
if next.is_none() {
|
||||||
|
if visited_end {
|
||||||
|
return None;
|
||||||
|
} else {
|
||||||
|
visited_end = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let prev = cursor.prev_item().map(|prev_excerpt| ExcerptInfo {
|
||||||
|
id: prev_excerpt.id,
|
||||||
|
buffer: prev_excerpt.buffer.clone(),
|
||||||
|
buffer_id: prev_excerpt.buffer_id,
|
||||||
|
range: prev_excerpt.range.clone(),
|
||||||
|
});
|
||||||
|
let row = MultiBufferRow(cursor.start().1.row);
|
||||||
|
|
||||||
prev_buffer_id = Some(excerpt.buffer_id);
|
|
||||||
cursor.next(&());
|
cursor.next(&());
|
||||||
Some(boundary)
|
|
||||||
|
Some(ExcerptBoundary { row, prev, next })
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -4537,15 +4618,16 @@ where
|
||||||
.peekable();
|
.peekable();
|
||||||
while let Some(range) = range_iter.next() {
|
while let Some(range) = range_iter.next() {
|
||||||
let excerpt_start = Point::new(range.start.row.saturating_sub(context_line_count), 0);
|
let excerpt_start = Point::new(range.start.row.saturating_sub(context_line_count), 0);
|
||||||
// These + 1s ensure that we select the whole next line
|
let row = (range.end.row + context_line_count).min(max_point.row);
|
||||||
let mut excerpt_end = Point::new(range.end.row + 1 + context_line_count, 0).min(max_point);
|
let mut excerpt_end = Point::new(row, buffer.line_len(row));
|
||||||
|
|
||||||
let mut ranges_in_excerpt = 1;
|
let mut ranges_in_excerpt = 1;
|
||||||
|
|
||||||
while let Some(next_range) = range_iter.peek() {
|
while let Some(next_range) = range_iter.peek() {
|
||||||
if next_range.start.row <= excerpt_end.row + context_line_count {
|
if next_range.start.row <= excerpt_end.row + context_line_count {
|
||||||
excerpt_end =
|
let row = (next_range.end.row + context_line_count).min(max_point.row);
|
||||||
Point::new(next_range.end.row + 1 + context_line_count, 0).min(max_point);
|
excerpt_end = Point::new(row, buffer.line_len(row));
|
||||||
|
|
||||||
ranges_in_excerpt += 1;
|
ranges_in_excerpt += 1;
|
||||||
range_iter.next();
|
range_iter.next();
|
||||||
} else {
|
} else {
|
||||||
|
@ -4866,15 +4948,17 @@ mod tests {
|
||||||
) -> Vec<(MultiBufferRow, String, bool)> {
|
) -> Vec<(MultiBufferRow, String, bool)> {
|
||||||
snapshot
|
snapshot
|
||||||
.excerpt_boundaries_in_range(range)
|
.excerpt_boundaries_in_range(range)
|
||||||
.map(|boundary| {
|
.filter_map(|boundary| {
|
||||||
(
|
let starts_new_buffer = boundary.starts_new_buffer();
|
||||||
boundary.row,
|
boundary.next.map(|next| {
|
||||||
boundary
|
(
|
||||||
.buffer
|
boundary.row,
|
||||||
.text_for_range(boundary.range.context)
|
next.buffer
|
||||||
.collect::<String>(),
|
.text_for_range(next.range.context)
|
||||||
boundary.starts_new_buffer,
|
.collect::<String>(),
|
||||||
)
|
starts_new_buffer,
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
@ -5006,8 +5090,33 @@ mod tests {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let snapshot = multibuffer.read(cx).snapshot(cx);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
snapshot.text(),
|
||||||
|
concat!(
|
||||||
|
"ccc\n", //
|
||||||
|
"ddd\n", //
|
||||||
|
"eee", //
|
||||||
|
"\n", // End of excerpt
|
||||||
|
"ggg\n", //
|
||||||
|
"hhh\n", //
|
||||||
|
"iii", //
|
||||||
|
"\n", // End of excerpt
|
||||||
|
"ooo\n", //
|
||||||
|
"ppp\n", //
|
||||||
|
"qqq", // End of excerpt
|
||||||
|
)
|
||||||
|
);
|
||||||
|
drop(snapshot);
|
||||||
|
|
||||||
multibuffer.update(cx, |multibuffer, cx| {
|
multibuffer.update(cx, |multibuffer, cx| {
|
||||||
multibuffer.expand_excerpts(multibuffer.excerpt_ids(), 1, cx)
|
multibuffer.expand_excerpts(
|
||||||
|
multibuffer.excerpt_ids(),
|
||||||
|
1,
|
||||||
|
ExpandExcerptDirection::UpAndDown,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let snapshot = multibuffer.read(cx).snapshot(cx);
|
let snapshot = multibuffer.read(cx).snapshot(cx);
|
||||||
|
@ -5018,23 +5127,21 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
snapshot.text(),
|
snapshot.text(),
|
||||||
concat!(
|
concat!(
|
||||||
"bbb\n", // Preserve newlines
|
"bbb\n", //
|
||||||
"ccc\n", //
|
"ccc\n", //
|
||||||
"ddd\n", //
|
"ddd\n", //
|
||||||
"eee\n", //
|
"eee\n", //
|
||||||
"fff\n", // <- Same as below
|
"fff\n", // End of excerpt
|
||||||
"\n", // Excerpt boundary
|
"fff\n", //
|
||||||
"fff\n", // <- Same as above
|
|
||||||
"ggg\n", //
|
"ggg\n", //
|
||||||
"hhh\n", //
|
"hhh\n", //
|
||||||
"iii\n", //
|
"iii\n", //
|
||||||
"jjj\n", //
|
"jjj\n", // End of excerpt
|
||||||
"\n", //
|
|
||||||
"nnn\n", //
|
"nnn\n", //
|
||||||
"ooo\n", //
|
"ooo\n", //
|
||||||
"ppp\n", //
|
"ppp\n", //
|
||||||
"qqq\n", //
|
"qqq\n", //
|
||||||
"rrr\n", //
|
"rrr", // End of excerpt
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5071,12 +5178,11 @@ mod tests {
|
||||||
"hhh\n", //
|
"hhh\n", //
|
||||||
"iii\n", //
|
"iii\n", //
|
||||||
"jjj\n", //
|
"jjj\n", //
|
||||||
"\n", //
|
|
||||||
"nnn\n", //
|
"nnn\n", //
|
||||||
"ooo\n", //
|
"ooo\n", //
|
||||||
"ppp\n", //
|
"ppp\n", //
|
||||||
"qqq\n", //
|
"qqq\n", //
|
||||||
"rrr\n", //
|
"rrr", //
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -5088,7 +5194,7 @@ mod tests {
|
||||||
vec![
|
vec![
|
||||||
Point::new(2, 2)..Point::new(3, 2),
|
Point::new(2, 2)..Point::new(3, 2),
|
||||||
Point::new(6, 1)..Point::new(6, 3),
|
Point::new(6, 1)..Point::new(6, 3),
|
||||||
Point::new(12, 0)..Point::new(12, 0)
|
Point::new(11, 0)..Point::new(11, 0)
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5123,12 +5229,11 @@ mod tests {
|
||||||
"hhh\n", //
|
"hhh\n", //
|
||||||
"iii\n", //
|
"iii\n", //
|
||||||
"jjj\n", //
|
"jjj\n", //
|
||||||
"\n", //
|
|
||||||
"nnn\n", //
|
"nnn\n", //
|
||||||
"ooo\n", //
|
"ooo\n", //
|
||||||
"ppp\n", //
|
"ppp\n", //
|
||||||
"qqq\n", //
|
"qqq\n", //
|
||||||
"rrr\n", //
|
"rrr", //
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -5140,7 +5245,7 @@ mod tests {
|
||||||
vec![
|
vec![
|
||||||
Point::new(2, 2)..Point::new(3, 2),
|
Point::new(2, 2)..Point::new(3, 2),
|
||||||
Point::new(6, 1)..Point::new(6, 3),
|
Point::new(6, 1)..Point::new(6, 3),
|
||||||
Point::new(12, 0)..Point::new(12, 0)
|
Point::new(11, 0)..Point::new(11, 0)
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5404,7 +5509,12 @@ mod tests {
|
||||||
.map(|id| excerpt_ids.iter().position(|i| i == id).unwrap())
|
.map(|id| excerpt_ids.iter().position(|i| i == id).unwrap())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
log::info!("Expanding excerpts {excerpt_ixs:?} by {line_count} lines");
|
log::info!("Expanding excerpts {excerpt_ixs:?} by {line_count} lines");
|
||||||
multibuffer.expand_excerpts(excerpts.iter().cloned(), line_count, cx);
|
multibuffer.expand_excerpts(
|
||||||
|
excerpts.iter().cloned(),
|
||||||
|
line_count,
|
||||||
|
ExpandExcerptDirection::UpAndDown,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
if line_count > 0 {
|
if line_count > 0 {
|
||||||
for id in excerpts {
|
for id in excerpts {
|
||||||
|
@ -5418,6 +5528,7 @@ mod tests {
|
||||||
Point::new(point_range.end.row + line_count, 0),
|
Point::new(point_range.end.row + line_count, 0),
|
||||||
Bias::Left,
|
Bias::Left,
|
||||||
);
|
);
|
||||||
|
point_range.end.column = snapshot.line_len(point_range.end.row);
|
||||||
*range = snapshot.anchor_before(point_range.start)
|
*range = snapshot.anchor_before(point_range.start)
|
||||||
..snapshot.anchor_after(point_range.end);
|
..snapshot.anchor_after(point_range.end);
|
||||||
}
|
}
|
||||||
|
|
|
@ -653,7 +653,7 @@ impl ProjectSearchView {
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
let results_editor = cx.new_view(|cx| {
|
let results_editor = cx.new_view(|cx| {
|
||||||
let mut editor = Editor::for_multibuffer(excerpts, Some(project.clone()), cx);
|
let mut editor = Editor::for_multibuffer(excerpts, Some(project.clone()), true, cx);
|
||||||
editor.set_searchable(false);
|
editor.set_searchable(false);
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
|
@ -1722,7 +1722,7 @@ pub mod tests {
|
||||||
search_view
|
search_view
|
||||||
.results_editor
|
.results_editor
|
||||||
.update(cx, |editor, cx| editor.display_text(cx)),
|
.update(cx, |editor, cx| editor.display_text(cx)),
|
||||||
"\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;"
|
"\n\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\n\n\nconst TWO: usize = one::ONE + one::ONE;\n"
|
||||||
);
|
);
|
||||||
let match_background_color = cx.theme().colors().search_match_background;
|
let match_background_color = cx.theme().colors().search_match_background;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1731,15 +1731,15 @@ pub mod tests {
|
||||||
.update(cx, |editor, cx| editor.all_text_background_highlights(cx)),
|
.update(cx, |editor, cx| editor.all_text_background_highlights(cx)),
|
||||||
&[
|
&[
|
||||||
(
|
(
|
||||||
DisplayPoint::new(DisplayRow(2), 32)..DisplayPoint::new(DisplayRow(2), 35),
|
DisplayPoint::new(DisplayRow(3), 32)..DisplayPoint::new(DisplayRow(3), 35),
|
||||||
match_background_color
|
match_background_color
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
DisplayPoint::new(DisplayRow(2), 37)..DisplayPoint::new(DisplayRow(2), 40),
|
DisplayPoint::new(DisplayRow(3), 37)..DisplayPoint::new(DisplayRow(3), 40),
|
||||||
match_background_color
|
match_background_color
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 9),
|
DisplayPoint::new(DisplayRow(8), 6)..DisplayPoint::new(DisplayRow(8), 9),
|
||||||
match_background_color
|
match_background_color
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
@ -1749,7 +1749,7 @@ pub mod tests {
|
||||||
search_view
|
search_view
|
||||||
.results_editor
|
.results_editor
|
||||||
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
|
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
|
||||||
[DisplayPoint::new(DisplayRow(2), 32)..DisplayPoint::new(DisplayRow(2), 35)]
|
[DisplayPoint::new(DisplayRow(3), 32)..DisplayPoint::new(DisplayRow(3), 35)]
|
||||||
);
|
);
|
||||||
|
|
||||||
search_view.select_match(Direction::Next, cx);
|
search_view.select_match(Direction::Next, cx);
|
||||||
|
@ -1762,7 +1762,7 @@ pub mod tests {
|
||||||
search_view
|
search_view
|
||||||
.results_editor
|
.results_editor
|
||||||
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
|
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
|
||||||
[DisplayPoint::new(DisplayRow(2), 37)..DisplayPoint::new(DisplayRow(2), 40)]
|
[DisplayPoint::new(DisplayRow(3), 37)..DisplayPoint::new(DisplayRow(3), 40)]
|
||||||
);
|
);
|
||||||
search_view.select_match(Direction::Next, cx);
|
search_view.select_match(Direction::Next, cx);
|
||||||
})
|
})
|
||||||
|
@ -1775,7 +1775,7 @@ pub mod tests {
|
||||||
search_view
|
search_view
|
||||||
.results_editor
|
.results_editor
|
||||||
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
|
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
|
||||||
[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 9)]
|
[DisplayPoint::new(DisplayRow(8), 6)..DisplayPoint::new(DisplayRow(8), 9)]
|
||||||
);
|
);
|
||||||
search_view.select_match(Direction::Next, cx);
|
search_view.select_match(Direction::Next, cx);
|
||||||
})
|
})
|
||||||
|
@ -1788,7 +1788,7 @@ pub mod tests {
|
||||||
search_view
|
search_view
|
||||||
.results_editor
|
.results_editor
|
||||||
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
|
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
|
||||||
[DisplayPoint::new(DisplayRow(2), 32)..DisplayPoint::new(DisplayRow(2), 35)]
|
[DisplayPoint::new(DisplayRow(3), 32)..DisplayPoint::new(DisplayRow(3), 35)]
|
||||||
);
|
);
|
||||||
search_view.select_match(Direction::Prev, cx);
|
search_view.select_match(Direction::Prev, cx);
|
||||||
})
|
})
|
||||||
|
@ -1801,7 +1801,7 @@ pub mod tests {
|
||||||
search_view
|
search_view
|
||||||
.results_editor
|
.results_editor
|
||||||
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
|
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
|
||||||
[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 9)]
|
[DisplayPoint::new(DisplayRow(8), 6)..DisplayPoint::new(DisplayRow(8), 9)]
|
||||||
);
|
);
|
||||||
search_view.select_match(Direction::Prev, cx);
|
search_view.select_match(Direction::Prev, cx);
|
||||||
})
|
})
|
||||||
|
@ -1814,7 +1814,7 @@ pub mod tests {
|
||||||
search_view
|
search_view
|
||||||
.results_editor
|
.results_editor
|
||||||
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
|
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
|
||||||
[DisplayPoint::new(DisplayRow(2), 37)..DisplayPoint::new(DisplayRow(2), 40)]
|
[DisplayPoint::new(DisplayRow(3), 37)..DisplayPoint::new(DisplayRow(3), 40)]
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -1982,7 +1982,7 @@ pub mod tests {
|
||||||
search_view
|
search_view
|
||||||
.results_editor
|
.results_editor
|
||||||
.update(cx, |editor, cx| editor.display_text(cx)),
|
.update(cx, |editor, cx| editor.display_text(cx)),
|
||||||
"\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;",
|
"\n\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\n\n\nconst TWO: usize = one::ONE + one::ONE;\n",
|
||||||
"Search view results should match the query"
|
"Search view results should match the query"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -2021,7 +2021,7 @@ pub mod tests {
|
||||||
search_view
|
search_view
|
||||||
.results_editor
|
.results_editor
|
||||||
.update(cx, |editor, cx| editor.display_text(cx)),
|
.update(cx, |editor, cx| editor.display_text(cx)),
|
||||||
"\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;",
|
"\n\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\n\n\nconst TWO: usize = one::ONE + one::ONE;\n",
|
||||||
"Results should be unchanged after search view 2nd open in a row"
|
"Results should be unchanged after search view 2nd open in a row"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -2213,7 +2213,7 @@ pub mod tests {
|
||||||
search_view
|
search_view
|
||||||
.results_editor
|
.results_editor
|
||||||
.update(cx, |editor, cx| editor.display_text(cx)),
|
.update(cx, |editor, cx| editor.display_text(cx)),
|
||||||
"\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;",
|
"\n\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\n\n\nconst TWO: usize = one::ONE + one::ONE;\n",
|
||||||
"Search view results should match the query"
|
"Search view results should match the query"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -2268,7 +2268,7 @@ pub mod tests {
|
||||||
search_view
|
search_view
|
||||||
.results_editor
|
.results_editor
|
||||||
.update(cx, |editor, cx| editor.display_text(cx)),
|
.update(cx, |editor, cx| editor.display_text(cx)),
|
||||||
"\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;",
|
"\n\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\n\n\nconst TWO: usize = one::ONE + one::ONE;\n",
|
||||||
"Results of the first search view should not update too"
|
"Results of the first search view should not update too"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -2317,7 +2317,7 @@ pub mod tests {
|
||||||
search_view_2
|
search_view_2
|
||||||
.results_editor
|
.results_editor
|
||||||
.update(cx, |editor, cx| editor.display_text(cx)),
|
.update(cx, |editor, cx| editor.display_text(cx)),
|
||||||
"\n\nconst FOUR: usize = one::ONE + three::THREE;",
|
"\n\n\nconst FOUR: usize = one::ONE + three::THREE;\n",
|
||||||
"New search view with the updated query should have new search results"
|
"New search view with the updated query should have new search results"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -2462,7 +2462,7 @@ pub mod tests {
|
||||||
search_view
|
search_view
|
||||||
.results_editor
|
.results_editor
|
||||||
.update(cx, |editor, cx| editor.display_text(cx)),
|
.update(cx, |editor, cx| editor.display_text(cx)),
|
||||||
"\n\nconst ONE: usize = 1;\n\n\nconst TWO: usize = one::ONE + one::ONE;",
|
"\n\n\nconst ONE: usize = 1;\n\n\n\n\nconst TWO: usize = one::ONE + one::ONE;\n",
|
||||||
"New search in directory should have a filter that matches a certain directory"
|
"New search in directory should have a filter that matches a certain directory"
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
|
@ -77,9 +77,11 @@ pub enum IconName {
|
||||||
Ai,
|
Ai,
|
||||||
ArrowCircle,
|
ArrowCircle,
|
||||||
ArrowDown,
|
ArrowDown,
|
||||||
|
ArrowDownFromLine,
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
ArrowUp,
|
ArrowUp,
|
||||||
|
ArrowUpFromLine,
|
||||||
ArrowUpRight,
|
ArrowUpRight,
|
||||||
AtSign,
|
AtSign,
|
||||||
AudioOff,
|
AudioOff,
|
||||||
|
@ -193,6 +195,7 @@ impl IconName {
|
||||||
IconName::Ai => "icons/ai.svg",
|
IconName::Ai => "icons/ai.svg",
|
||||||
IconName::ArrowCircle => "icons/arrow_circle.svg",
|
IconName::ArrowCircle => "icons/arrow_circle.svg",
|
||||||
IconName::ArrowDown => "icons/arrow_down.svg",
|
IconName::ArrowDown => "icons/arrow_down.svg",
|
||||||
|
IconName::ArrowDownFromLine => "icons/arrow_down_from_line.svg",
|
||||||
IconName::ArrowLeft => "icons/arrow_left.svg",
|
IconName::ArrowLeft => "icons/arrow_left.svg",
|
||||||
IconName::ArrowRight => "icons/arrow_right.svg",
|
IconName::ArrowRight => "icons/arrow_right.svg",
|
||||||
IconName::ArrowUp => "icons/arrow_up.svg",
|
IconName::ArrowUp => "icons/arrow_up.svg",
|
||||||
|
@ -301,6 +304,7 @@ impl IconName {
|
||||||
IconName::XCircle => "icons/error.svg",
|
IconName::XCircle => "icons/error.svg",
|
||||||
IconName::ZedAssistant => "icons/zed_assistant.svg",
|
IconName::ZedAssistant => "icons/zed_assistant.svg",
|
||||||
IconName::ZedXCopilot => "icons/zed_x_copilot.svg",
|
IconName::ZedXCopilot => "icons/zed_x_copilot.svg",
|
||||||
|
IconName::ArrowUpFromLine => "icons/arrow_up_from_line.svg",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -601,8 +601,9 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
|
||||||
let buffer = cx.new_model(|cx| {
|
let buffer = cx.new_model(|cx| {
|
||||||
MultiBuffer::singleton(buffer, cx).with_title("Log".into())
|
MultiBuffer::singleton(buffer, cx).with_title("Log".into())
|
||||||
});
|
});
|
||||||
let editor =
|
let editor = cx.new_view(|cx| {
|
||||||
cx.new_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx));
|
Editor::for_multibuffer(buffer, Some(project), true, cx)
|
||||||
|
});
|
||||||
|
|
||||||
editor.update(cx, |editor, cx| {
|
editor.update(cx, |editor, cx| {
|
||||||
let last_multi_buffer_offset = editor.buffer().read(cx).len(cx);
|
let last_multi_buffer_offset = editor.buffer().read(cx).len(cx);
|
||||||
|
@ -831,7 +832,7 @@ fn open_telemetry_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Works
|
||||||
MultiBuffer::singleton(buffer, cx).with_title("Telemetry Log".into())
|
MultiBuffer::singleton(buffer, cx).with_title("Telemetry Log".into())
|
||||||
});
|
});
|
||||||
workspace.add_item_to_active_pane(
|
workspace.add_item_to_active_pane(
|
||||||
Box::new(cx.new_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx))),
|
Box::new(cx.new_view(|cx| Editor::for_multibuffer(buffer, Some(project), true, cx))),
|
||||||
None,cx,
|
None,cx,
|
||||||
);
|
);
|
||||||
}).log_err()?;
|
}).log_err()?;
|
||||||
|
@ -864,7 +865,7 @@ fn open_bundled_file(
|
||||||
});
|
});
|
||||||
workspace.add_item_to_active_pane(
|
workspace.add_item_to_active_pane(
|
||||||
Box::new(cx.new_view(|cx| {
|
Box::new(cx.new_view(|cx| {
|
||||||
Editor::for_multibuffer(buffer, Some(project.clone()), cx)
|
Editor::for_multibuffer(buffer, Some(project.clone()), true, cx)
|
||||||
})),
|
})),
|
||||||
None,
|
None,
|
||||||
cx,
|
cx,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue