Use replace blocks for patches (#20605)
Release Notes: - N/A --------- Co-authored-by: Max <max@zed.dev> Co-authored-by: Richard <richard@zed.dev>
This commit is contained in:
parent
96deabfb78
commit
92613a8904
13 changed files with 925 additions and 918 deletions
654
Cargo.lock
generated
654
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1480,7 +1480,6 @@ struct ScrollPosition {
|
|||
}
|
||||
|
||||
struct PatchViewState {
|
||||
footer_block_id: CustomBlockId,
|
||||
crease_id: CreaseId,
|
||||
editor: Option<PatchEditorState>,
|
||||
update_task: Option<Task<()>>,
|
||||
|
@ -1934,7 +1933,7 @@ impl ContextEditor {
|
|||
);
|
||||
});
|
||||
|
||||
Crease::new(
|
||||
Crease::inline(
|
||||
start..end,
|
||||
placeholder,
|
||||
fold_toggle("tool-use"),
|
||||
|
@ -2032,7 +2031,7 @@ impl ContextEditor {
|
|||
let end = buffer
|
||||
.anchor_in_excerpt(excerpt_id, command.source_range.end)
|
||||
.unwrap();
|
||||
Crease::new(start..end, placeholder, render_toggle, render_trailer)
|
||||
Crease::inline(start..end, placeholder, render_toggle, render_trailer)
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
|
@ -2124,7 +2123,7 @@ impl ContextEditor {
|
|||
|
||||
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
|
||||
|
||||
let crease = Crease::new(
|
||||
let crease = Crease::inline(
|
||||
start..end,
|
||||
placeholder,
|
||||
fold_toggle("tool-use"),
|
||||
|
@ -2192,18 +2191,14 @@ impl ContextEditor {
|
|||
let crease_end = buffer
|
||||
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.end)
|
||||
.unwrap();
|
||||
let fold_placeholder =
|
||||
invoked_slash_command_fold_placeholder(command_id, context);
|
||||
let crease_ids = editor.insert_creases(
|
||||
[Crease::new(
|
||||
crease_start..crease_end,
|
||||
fold_placeholder.clone(),
|
||||
fold_toggle("invoked-slash-command"),
|
||||
|_row, _folded, _cx| Empty.into_any(),
|
||||
)],
|
||||
cx,
|
||||
let crease = Crease::inline(
|
||||
crease_start..crease_end,
|
||||
invoked_slash_command_fold_placeholder(command_id, context),
|
||||
fold_toggle("invoked-slash-command"),
|
||||
|_row, _folded, _cx| Empty.into_any(),
|
||||
);
|
||||
editor.fold_ranges([(crease_start..crease_end, fold_placeholder)], false, cx);
|
||||
let crease_ids = editor.insert_creases([crease.clone()], cx);
|
||||
editor.fold_creases(vec![crease], false, cx);
|
||||
entry.insert(crease_ids[0]);
|
||||
} else {
|
||||
cx.notify()
|
||||
|
@ -2225,23 +2220,32 @@ impl ContextEditor {
|
|||
cx: &mut ViewContext<ContextEditor>,
|
||||
) {
|
||||
let this = cx.view().downgrade();
|
||||
let mut removed_crease_ids = Vec::new();
|
||||
let mut removed_block_ids = HashSet::default();
|
||||
let mut editors_to_close = Vec::new();
|
||||
for range in removed {
|
||||
if let Some(state) = self.patches.remove(range) {
|
||||
editors_to_close.extend(state.editor.and_then(|state| state.editor.upgrade()));
|
||||
removed_block_ids.insert(state.footer_block_id);
|
||||
removed_crease_ids.push(state.crease_id);
|
||||
}
|
||||
}
|
||||
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
let multibuffer = &snapshot.buffer_snapshot;
|
||||
let (&excerpt_id, _, _) = multibuffer.as_singleton().unwrap();
|
||||
|
||||
let mut replaced_blocks = HashMap::default();
|
||||
let mut removed_crease_ids = Vec::new();
|
||||
let mut ranges_to_unfold: Vec<Range<Anchor>> = Vec::new();
|
||||
for range in removed {
|
||||
if let Some(state) = self.patches.remove(range) {
|
||||
let patch_start = multibuffer
|
||||
.anchor_in_excerpt(excerpt_id, range.start)
|
||||
.unwrap();
|
||||
let patch_end = multibuffer
|
||||
.anchor_in_excerpt(excerpt_id, range.end)
|
||||
.unwrap();
|
||||
|
||||
editors_to_close.extend(state.editor.and_then(|state| state.editor.upgrade()));
|
||||
ranges_to_unfold.push(patch_start..patch_end);
|
||||
removed_crease_ids.push(state.crease_id);
|
||||
}
|
||||
}
|
||||
editor.unfold_ranges(&ranges_to_unfold, true, false, cx);
|
||||
editor.remove_creases(removed_crease_ids, cx);
|
||||
|
||||
for range in updated {
|
||||
let Some(patch) = self.context.read(cx).patch_for_range(&range, cx).cloned() else {
|
||||
continue;
|
||||
|
@ -2254,19 +2258,21 @@ impl ContextEditor {
|
|||
let patch_end = multibuffer
|
||||
.anchor_in_excerpt(excerpt_id, patch.range.end)
|
||||
.unwrap();
|
||||
let render_block: RenderBlock = Box::new({
|
||||
let render_block: RenderBlock = Arc::new({
|
||||
let this = this.clone();
|
||||
let patch_range = range.clone();
|
||||
move |cx: &mut BlockContext<'_, '_>| {
|
||||
let max_width = cx.max_width;
|
||||
let gutter_width = cx.gutter_dimensions.full_width();
|
||||
let block_id = cx.block_id;
|
||||
let selected = cx.selected;
|
||||
this.update(&mut **cx, |this, cx| {
|
||||
this.render_patch_footer(
|
||||
this.render_patch_block(
|
||||
patch_range.clone(),
|
||||
max_width,
|
||||
gutter_width,
|
||||
block_id,
|
||||
selected,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
|
@ -2276,25 +2282,16 @@ impl ContextEditor {
|
|||
}
|
||||
});
|
||||
|
||||
let header_placeholder = FoldPlaceholder {
|
||||
render: {
|
||||
let this = this.clone();
|
||||
let patch_range = range.clone();
|
||||
Arc::new(move |fold_id, _range, cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.render_patch_header(patch_range.clone(), fold_id, cx)
|
||||
})
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_else(|| Empty.into_any())
|
||||
})
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
let height = path_count as u32 + 1;
|
||||
let crease = Crease::block(
|
||||
patch_start..patch_end,
|
||||
height,
|
||||
BlockStyle::Flex,
|
||||
render_block.clone(),
|
||||
);
|
||||
|
||||
let should_refold;
|
||||
if let Some(state) = self.patches.get_mut(&range) {
|
||||
replaced_blocks.insert(state.footer_block_id, render_block);
|
||||
if let Some(editor_state) = &state.editor {
|
||||
if editor_state.opened_patch != patch {
|
||||
state.update_task = Some({
|
||||
|
@ -2311,33 +2308,11 @@ impl ContextEditor {
|
|||
should_refold =
|
||||
snapshot.intersects_fold(patch_start.to_offset(&snapshot.buffer_snapshot));
|
||||
} else {
|
||||
let block_ids = editor.insert_blocks(
|
||||
[BlockProperties {
|
||||
height: path_count as u32 + 1,
|
||||
style: BlockStyle::Flex,
|
||||
render: render_block,
|
||||
placement: BlockPlacement::Below(patch_start),
|
||||
priority: 0,
|
||||
}],
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
let new_crease_ids = editor.insert_creases(
|
||||
[Crease::new(
|
||||
patch_start..patch_end,
|
||||
header_placeholder.clone(),
|
||||
fold_toggle("patch-header"),
|
||||
|_, _, _| Empty.into_any_element(),
|
||||
)],
|
||||
cx,
|
||||
);
|
||||
|
||||
let crease_id = editor.insert_creases([crease.clone()], cx)[0];
|
||||
self.patches.insert(
|
||||
range.clone(),
|
||||
PatchViewState {
|
||||
footer_block_id: block_ids[0],
|
||||
crease_id: new_crease_ids[0],
|
||||
crease_id,
|
||||
editor: None,
|
||||
update_task: None,
|
||||
},
|
||||
|
@ -2348,13 +2323,9 @@ impl ContextEditor {
|
|||
|
||||
if should_refold {
|
||||
editor.unfold_ranges(&[patch_start..patch_end], true, false, cx);
|
||||
editor.fold_ranges([(patch_start..patch_end, header_placeholder)], false, cx);
|
||||
editor.fold_creases(vec![crease], false, cx);
|
||||
}
|
||||
}
|
||||
|
||||
editor.remove_creases(removed_crease_ids, cx);
|
||||
editor.remove_blocks(removed_block_ids, None, cx);
|
||||
editor.replace_blocks(replaced_blocks, None, cx);
|
||||
});
|
||||
|
||||
for editor in editors_to_close {
|
||||
|
@ -2385,7 +2356,7 @@ impl ContextEditor {
|
|||
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
|
||||
buffer_rows_to_fold.insert(buffer_row);
|
||||
creases.push(
|
||||
Crease::new(
|
||||
Crease::inline(
|
||||
start..end,
|
||||
FoldPlaceholder {
|
||||
render: render_fold_icon_button(
|
||||
|
@ -2674,7 +2645,7 @@ impl ContextEditor {
|
|||
let mut blocks_to_replace: HashMap<_, RenderBlock> = Default::default();
|
||||
|
||||
let render_block = |message: MessageMetadata| -> RenderBlock {
|
||||
Box::new({
|
||||
Arc::new({
|
||||
let context = self.context.clone();
|
||||
|
||||
move |cx| {
|
||||
|
@ -3127,7 +3098,7 @@ impl ContextEditor {
|
|||
crease_title,
|
||||
cx.view().downgrade(),
|
||||
);
|
||||
let crease = Crease::new(
|
||||
let crease = Crease::inline(
|
||||
anchor_before..anchor_after,
|
||||
fold_placeholder,
|
||||
render_quote_selection_output_toggle,
|
||||
|
@ -3217,31 +3188,29 @@ impl ContextEditor {
|
|||
&snapshot,
|
||||
)
|
||||
.filter_map(|crease| {
|
||||
if let Some(metadata) = &crease.metadata {
|
||||
let start = crease
|
||||
.range
|
||||
if let Crease::Inline {
|
||||
range, metadata, ..
|
||||
} = &crease
|
||||
{
|
||||
let metadata = metadata.as_ref()?;
|
||||
let start = range
|
||||
.start
|
||||
.to_offset(&snapshot)
|
||||
.saturating_sub(selection_start);
|
||||
let end = crease
|
||||
.range
|
||||
let end = range
|
||||
.end
|
||||
.to_offset(&snapshot)
|
||||
.saturating_sub(selection_start);
|
||||
|
||||
let range_relative_to_selection = start..end;
|
||||
|
||||
if range_relative_to_selection.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(SelectedCreaseMetadata {
|
||||
if !range_relative_to_selection.is_empty() {
|
||||
return Some(SelectedCreaseMetadata {
|
||||
range_relative_to_selection,
|
||||
crease: metadata.clone(),
|
||||
})
|
||||
});
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}),
|
||||
|
@ -3322,7 +3291,7 @@ impl ContextEditor {
|
|||
|
||||
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
|
||||
buffer_rows_to_fold.insert(buffer_row);
|
||||
Crease::new(
|
||||
Crease::inline(
|
||||
start..end,
|
||||
FoldPlaceholder {
|
||||
render: render_fold_icon_button(
|
||||
|
@ -3415,7 +3384,7 @@ impl ContextEditor {
|
|||
placement: BlockPlacement::Above(anchor),
|
||||
height: MAX_HEIGHT_IN_LINES,
|
||||
style: BlockStyle::Sticky,
|
||||
render: Box::new(move |cx| {
|
||||
render: Arc::new(move |cx| {
|
||||
let image_size = size_for_image(
|
||||
&image,
|
||||
size(
|
||||
|
@ -3472,33 +3441,13 @@ impl ContextEditor {
|
|||
.unwrap_or_else(|| Cow::Borrowed(DEFAULT_TAB_TITLE))
|
||||
}
|
||||
|
||||
fn render_patch_header(
|
||||
&self,
|
||||
range: Range<text::Anchor>,
|
||||
_id: FoldId,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<AnyElement> {
|
||||
let patch = self.context.read(cx).patch_for_range(&range, cx)?;
|
||||
let theme = cx.theme().clone();
|
||||
Some(
|
||||
h_flex()
|
||||
.px_1()
|
||||
.py_0p5()
|
||||
.border_b_1()
|
||||
.border_color(theme.status().info_border)
|
||||
.gap_1()
|
||||
.child(Icon::new(IconName::Diff).size(IconSize::Small))
|
||||
.child(Label::new(patch.title.clone()).size(LabelSize::Small))
|
||||
.into_any(),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_patch_footer(
|
||||
fn render_patch_block(
|
||||
&mut self,
|
||||
range: Range<text::Anchor>,
|
||||
max_width: Pixels,
|
||||
gutter_width: Pixels,
|
||||
id: BlockId,
|
||||
selected: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<AnyElement> {
|
||||
let snapshot = self.editor.update(cx, |editor, cx| editor.snapshot(cx));
|
||||
|
@ -3509,10 +3458,7 @@ impl ContextEditor {
|
|||
.anchor_in_excerpt(excerpt_id, range.start)
|
||||
.unwrap();
|
||||
|
||||
if !snapshot.intersects_fold(anchor) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let theme = cx.theme().clone();
|
||||
let patch = self.context.read(cx).patch_for_range(&range, cx)?;
|
||||
let paths = patch
|
||||
.paths()
|
||||
|
@ -3522,9 +3468,17 @@ impl ContextEditor {
|
|||
Some(
|
||||
v_flex()
|
||||
.id(id)
|
||||
.pl(gutter_width)
|
||||
.w(max_width)
|
||||
.py_2()
|
||||
.bg(theme.colors().editor_background)
|
||||
.ml(gutter_width)
|
||||
.w(max_width - gutter_width)
|
||||
.rounded_md()
|
||||
.border_1()
|
||||
.border_color(theme.colors().border)
|
||||
.hover(|style| style.border_color(theme.colors().text_accent))
|
||||
.when(selected, |this| {
|
||||
this.border_color(theme.colors().text_accent)
|
||||
})
|
||||
.pb_1()
|
||||
.cursor(CursorStyle::PointingHand)
|
||||
.on_click(cx.listener(move |this, _, cx| {
|
||||
this.editor.update(cx, |editor, cx| {
|
||||
|
@ -3534,24 +3488,55 @@ impl ContextEditor {
|
|||
});
|
||||
this.focus_active_patch(cx);
|
||||
}))
|
||||
.child(
|
||||
h_flex()
|
||||
.rounded_t_md()
|
||||
.bg(theme.colors().element_background)
|
||||
.px_2()
|
||||
.py_1()
|
||||
.child(Label::new(patch.title.clone()).size(LabelSize::Small))
|
||||
.border_b_1()
|
||||
.border_color(theme.colors().border),
|
||||
)
|
||||
.children(paths.into_iter().map(|path| {
|
||||
h_flex()
|
||||
.pl_1()
|
||||
.px_2()
|
||||
.pt_1()
|
||||
.gap_1()
|
||||
.child(Icon::new(IconName::File).size(IconSize::Small))
|
||||
.child(Label::new(path).size(LabelSize::Small))
|
||||
}))
|
||||
.when(patch.status == AssistantPatchStatus::Pending, |div| {
|
||||
div.child(
|
||||
Label::new("Generating")
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small)
|
||||
.with_animation(
|
||||
"pulsating-label",
|
||||
Animation::new(Duration::from_secs(2))
|
||||
.repeat()
|
||||
.with_easing(pulsating_between(0.4, 1.)),
|
||||
|label, delta| label.alpha(delta),
|
||||
h_flex()
|
||||
.pt_1()
|
||||
.px_2()
|
||||
.gap_1()
|
||||
.child(
|
||||
Icon::new(IconName::ArrowCircle)
|
||||
.size(IconSize::XSmall)
|
||||
.color(Color::Info)
|
||||
.with_animation(
|
||||
"arrow-circle",
|
||||
Animation::new(Duration::from_secs(2)).repeat(),
|
||||
|icon, delta| {
|
||||
icon.transform(Transformation::rotate(percentage(
|
||||
delta,
|
||||
)))
|
||||
},
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Label::new("Generating")
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small)
|
||||
.with_animation(
|
||||
"pulsating-label",
|
||||
Animation::new(Duration::from_secs(2))
|
||||
.repeat()
|
||||
.with_easing(pulsating_between(0.4, 1.)),
|
||||
|label, delta| label.alpha(delta),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
|
|
@ -460,7 +460,7 @@ impl InlineAssistant {
|
|||
style: BlockStyle::Sticky,
|
||||
placement: BlockPlacement::Below(range.end),
|
||||
height: 0,
|
||||
render: Box::new(|cx| {
|
||||
render: Arc::new(|cx| {
|
||||
v_flex()
|
||||
.h_full()
|
||||
.w_full()
|
||||
|
@ -1197,8 +1197,9 @@ impl InlineAssistant {
|
|||
placement: BlockPlacement::Above(new_row),
|
||||
height,
|
||||
style: BlockStyle::Flex,
|
||||
render: Box::new(move |cx| {
|
||||
render: Arc::new(move |cx| {
|
||||
div()
|
||||
.occlude()
|
||||
.bg(cx.theme().status().deleted_background)
|
||||
.size_full()
|
||||
.h(height as f32 * cx.line_height())
|
||||
|
@ -1317,7 +1318,7 @@ impl InlineAssistGroup {
|
|||
|
||||
fn build_assist_editor_renderer(editor: &View<PromptEditor>) -> RenderBlock {
|
||||
let editor = editor.clone();
|
||||
Box::new(move |cx: &mut BlockContext| {
|
||||
Arc::new(move |cx: &mut BlockContext| {
|
||||
*editor.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
|
||||
editor.clone().into_any_element()
|
||||
})
|
||||
|
@ -1480,6 +1481,7 @@ impl Render for PromptEditor {
|
|||
h_flex()
|
||||
.key_context("PromptEditor")
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.occlude()
|
||||
.border_y_1()
|
||||
.border_color(cx.theme().status().info_border)
|
||||
.size_full()
|
||||
|
|
|
@ -32,6 +32,7 @@ use std::{
|
|||
cmp::Ordering,
|
||||
mem,
|
||||
ops::Range,
|
||||
sync::Arc,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
pub use toolbar_controls::ToolbarControls;
|
||||
|
@ -790,10 +791,11 @@ const DIAGNOSTIC_HEADER: &str = "diagnostic header";
|
|||
fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
|
||||
let (message, code_ranges) = highlight_diagnostic_message(&diagnostic, None);
|
||||
let message: SharedString = message;
|
||||
Box::new(move |cx| {
|
||||
Arc::new(move |cx| {
|
||||
let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into();
|
||||
h_flex()
|
||||
.id(DIAGNOSTIC_HEADER)
|
||||
.occlude()
|
||||
.h(2. * cx.line_height())
|
||||
.pl_10()
|
||||
.pr_5()
|
||||
|
|
|
@ -36,7 +36,7 @@ use block_map::{BlockRow, BlockSnapshot};
|
|||
use collections::{HashMap, HashSet};
|
||||
pub use crease_map::*;
|
||||
pub use fold_map::{Fold, FoldId, FoldPlaceholder, FoldPoint};
|
||||
use fold_map::{FoldMap, FoldMapWriter, FoldOffset, FoldSnapshot};
|
||||
use fold_map::{FoldMap, FoldSnapshot};
|
||||
use gpui::{
|
||||
AnyElement, Font, HighlightStyle, LineLayout, Model, ModelContext, Pixels, UnderlineStyle,
|
||||
};
|
||||
|
@ -65,7 +65,7 @@ use std::{
|
|||
};
|
||||
use sum_tree::{Bias, TreeMap};
|
||||
use tab_map::{TabMap, TabSnapshot};
|
||||
use text::{Edit, LineIndent};
|
||||
use text::LineIndent;
|
||||
use ui::{div, px, IntoElement, ParentElement, SharedString, Styled, WindowContext};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use wrap_map::{WrapMap, WrapSnapshot};
|
||||
|
@ -197,22 +197,86 @@ impl DisplayMap {
|
|||
other
|
||||
.folds_in_range(0..other.buffer_snapshot.len())
|
||||
.map(|fold| {
|
||||
(
|
||||
Crease::simple(
|
||||
fold.range.to_offset(&other.buffer_snapshot),
|
||||
fold.placeholder.clone(),
|
||||
)
|
||||
}),
|
||||
})
|
||||
.collect(),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates folds for the given ranges.
|
||||
pub fn fold<T: ToOffset>(
|
||||
/// Creates folds for the given creases.
|
||||
pub fn fold<T: Clone + ToOffset>(
|
||||
&mut self,
|
||||
ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
|
||||
creases: Vec<Crease<T>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
self.update_fold_map(cx, |fold_map| fold_map.fold(ranges))
|
||||
let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
let edits = self.buffer_subscription.consume().into_inner();
|
||||
let tab_size = Self::tab_size(&self.buffer, cx);
|
||||
let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot.clone(), edits);
|
||||
let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
|
||||
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
|
||||
let (snapshot, edits) = self
|
||||
.wrap_map
|
||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||
self.block_map.read(snapshot, edits);
|
||||
|
||||
let inline = creases.iter().filter_map(|crease| {
|
||||
if let Crease::Inline {
|
||||
range, placeholder, ..
|
||||
} = crease
|
||||
{
|
||||
Some((range.clone(), placeholder.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let (snapshot, edits) = fold_map.fold(inline);
|
||||
|
||||
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
|
||||
let (snapshot, edits) = self
|
||||
.wrap_map
|
||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||
let mut block_map = self.block_map.write(snapshot, edits);
|
||||
let blocks = creases.into_iter().filter_map(|crease| {
|
||||
if let Crease::Block {
|
||||
range,
|
||||
block_height,
|
||||
render_block,
|
||||
block_style,
|
||||
block_priority,
|
||||
..
|
||||
} = crease
|
||||
{
|
||||
Some((
|
||||
range,
|
||||
render_block,
|
||||
block_height,
|
||||
block_style,
|
||||
block_priority,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
block_map.insert(
|
||||
blocks
|
||||
.into_iter()
|
||||
.map(|(range, render, height, style, priority)| {
|
||||
let start = buffer_snapshot.anchor_before(range.start);
|
||||
let end = buffer_snapshot.anchor_after(range.end);
|
||||
BlockProperties {
|
||||
placement: BlockPlacement::Replace(start..end),
|
||||
render,
|
||||
height,
|
||||
style,
|
||||
priority,
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/// Removes any folds with the given ranges.
|
||||
|
@ -221,26 +285,6 @@ impl DisplayMap {
|
|||
ranges: impl IntoIterator<Item = Range<T>>,
|
||||
type_id: TypeId,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
self.update_fold_map(cx, |fold_map| fold_map.remove_folds(ranges, type_id))
|
||||
}
|
||||
|
||||
/// Removes any folds whose ranges intersect any of the given ranges.
|
||||
pub fn unfold_intersecting<T: ToOffset>(
|
||||
&mut self,
|
||||
ranges: impl IntoIterator<Item = Range<T>>,
|
||||
inclusive: bool,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
self.update_fold_map(cx, |fold_map| {
|
||||
fold_map.unfold_intersecting(ranges, inclusive)
|
||||
})
|
||||
}
|
||||
|
||||
fn update_fold_map(
|
||||
&mut self,
|
||||
cx: &mut ModelContext<Self>,
|
||||
callback: impl FnOnce(&mut FoldMapWriter) -> (FoldSnapshot, Vec<Edit<FoldOffset>>),
|
||||
) {
|
||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
let edits = self.buffer_subscription.consume().into_inner();
|
||||
|
@ -252,17 +296,49 @@ impl DisplayMap {
|
|||
.wrap_map
|
||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||
self.block_map.read(snapshot, edits);
|
||||
let (snapshot, edits) = callback(&mut fold_map);
|
||||
let (snapshot, edits) = fold_map.remove_folds(ranges, type_id);
|
||||
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
|
||||
let (snapshot, edits) = self
|
||||
.wrap_map
|
||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||
self.block_map.write(snapshot, edits);
|
||||
}
|
||||
|
||||
/// Removes any folds whose ranges intersect any of the given ranges.
|
||||
pub fn unfold_intersecting<T: ToOffset>(
|
||||
&mut self,
|
||||
ranges: impl IntoIterator<Item = Range<T>>,
|
||||
inclusive: bool,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
let offset_ranges = ranges
|
||||
.into_iter()
|
||||
.map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot))
|
||||
.collect::<Vec<_>>();
|
||||
let edits = self.buffer_subscription.consume().into_inner();
|
||||
let tab_size = Self::tab_size(&self.buffer, cx);
|
||||
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
|
||||
let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
|
||||
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
|
||||
let (snapshot, edits) = self
|
||||
.wrap_map
|
||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||
self.block_map.read(snapshot, edits);
|
||||
|
||||
let (snapshot, edits) =
|
||||
fold_map.unfold_intersecting(offset_ranges.iter().cloned(), inclusive);
|
||||
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
|
||||
let (snapshot, edits) = self
|
||||
.wrap_map
|
||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||
let mut block_map = self.block_map.write(snapshot, edits);
|
||||
block_map.remove_intersecting_replace_blocks(offset_ranges, inclusive);
|
||||
}
|
||||
|
||||
pub fn insert_creases(
|
||||
&mut self,
|
||||
creases: impl IntoIterator<Item = Crease>,
|
||||
creases: impl IntoIterator<Item = Crease<Anchor>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Vec<CreaseId> {
|
||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
|
@ -596,7 +672,7 @@ impl DisplaySnapshot {
|
|||
) -> impl Iterator<Item = Option<MultiBufferRow>> + '_ {
|
||||
self.block_snapshot
|
||||
.buffer_rows(BlockRow(start_row.0))
|
||||
.map(|row| row.map(|row| MultiBufferRow(row.0)))
|
||||
.map(|row| row.map(MultiBufferRow))
|
||||
}
|
||||
|
||||
pub fn max_buffer_row(&self) -> MultiBufferRow {
|
||||
|
@ -987,7 +1063,12 @@ impl DisplaySnapshot {
|
|||
}
|
||||
|
||||
pub fn is_line_folded(&self, buffer_row: MultiBufferRow) -> bool {
|
||||
self.fold_snapshot.is_line_folded(buffer_row)
|
||||
self.block_snapshot.is_line_replaced(buffer_row)
|
||||
|| self.fold_snapshot.is_line_folded(buffer_row)
|
||||
}
|
||||
|
||||
pub fn is_line_replaced(&self, buffer_row: MultiBufferRow) -> bool {
|
||||
self.block_snapshot.is_line_replaced(buffer_row)
|
||||
}
|
||||
|
||||
pub fn is_block_line(&self, display_row: DisplayRow) -> bool {
|
||||
|
@ -1061,19 +1142,42 @@ impl DisplaySnapshot {
|
|||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn foldable_range(
|
||||
&self,
|
||||
buffer_row: MultiBufferRow,
|
||||
) -> Option<(Range<Point>, FoldPlaceholder)> {
|
||||
pub fn crease_for_buffer_row(&self, buffer_row: MultiBufferRow) -> Option<Crease<Point>> {
|
||||
let start = MultiBufferPoint::new(buffer_row.0, self.buffer_snapshot.line_len(buffer_row));
|
||||
if let Some(crease) = self
|
||||
.crease_snapshot
|
||||
.query_row(buffer_row, &self.buffer_snapshot)
|
||||
{
|
||||
Some((
|
||||
crease.range.to_point(&self.buffer_snapshot),
|
||||
crease.placeholder.clone(),
|
||||
))
|
||||
match crease {
|
||||
Crease::Inline {
|
||||
range,
|
||||
placeholder,
|
||||
render_toggle,
|
||||
render_trailer,
|
||||
metadata,
|
||||
} => Some(Crease::Inline {
|
||||
range: range.to_point(&self.buffer_snapshot),
|
||||
placeholder: placeholder.clone(),
|
||||
render_toggle: render_toggle.clone(),
|
||||
render_trailer: render_trailer.clone(),
|
||||
metadata: metadata.clone(),
|
||||
}),
|
||||
Crease::Block {
|
||||
range,
|
||||
block_height,
|
||||
block_style,
|
||||
render_block,
|
||||
block_priority,
|
||||
render_toggle,
|
||||
} => Some(Crease::Block {
|
||||
range: range.to_point(&self.buffer_snapshot),
|
||||
block_height: *block_height,
|
||||
block_style: *block_style,
|
||||
render_block: render_block.clone(),
|
||||
block_priority: *block_priority,
|
||||
render_toggle: render_toggle.clone(),
|
||||
}),
|
||||
}
|
||||
} else if self.starts_indent(MultiBufferRow(start.row))
|
||||
&& !self.is_line_folded(MultiBufferRow(start.row))
|
||||
{
|
||||
|
@ -1110,7 +1214,13 @@ impl DisplaySnapshot {
|
|||
.line_len(MultiBufferRow(row_before_line_breaks.row)),
|
||||
);
|
||||
|
||||
Some((start..row_before_line_breaks, self.fold_placeholder.clone()))
|
||||
Some(Crease::Inline {
|
||||
range: start..row_before_line_breaks,
|
||||
placeholder: self.fold_placeholder.clone(),
|
||||
render_toggle: None,
|
||||
render_trailer: None,
|
||||
metadata: None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -1418,7 +1528,7 @@ pub mod tests {
|
|||
placement,
|
||||
style: BlockStyle::Fixed,
|
||||
height,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority,
|
||||
}
|
||||
})
|
||||
|
@ -1457,7 +1567,8 @@ pub mod tests {
|
|||
map.fold(
|
||||
ranges
|
||||
.into_iter()
|
||||
.map(|range| (range, FoldPlaceholder::test())),
|
||||
.map(|range| Crease::simple(range, FoldPlaceholder::test()))
|
||||
.collect(),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
@ -1832,7 +1943,7 @@ pub mod tests {
|
|||
|
||||
map.update(cx, |map, cx| {
|
||||
map.fold(
|
||||
vec![(
|
||||
vec![Crease::simple(
|
||||
MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2),
|
||||
FoldPlaceholder::test(),
|
||||
)],
|
||||
|
@ -1922,7 +2033,7 @@ pub mod tests {
|
|||
),
|
||||
height: 1,
|
||||
style: BlockStyle::Sticky,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
}],
|
||||
cx,
|
||||
|
@ -2028,7 +2139,7 @@ pub mod tests {
|
|||
),
|
||||
height: 1,
|
||||
style: BlockStyle::Sticky,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
}],
|
||||
cx,
|
||||
|
@ -2104,7 +2215,7 @@ pub mod tests {
|
|||
),
|
||||
height: 4,
|
||||
style: BlockStyle::Fixed,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
}],
|
||||
cx,
|
||||
|
@ -2253,7 +2364,7 @@ pub mod tests {
|
|||
|
||||
map.update(cx, |map, cx| {
|
||||
map.fold(
|
||||
vec![(
|
||||
vec![Crease::simple(
|
||||
MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2),
|
||||
FoldPlaceholder::test(),
|
||||
)],
|
||||
|
@ -2452,7 +2563,7 @@ pub mod tests {
|
|||
snapshot.anchor_before(Point::new(2, 0))..snapshot.anchor_after(Point::new(3, 3));
|
||||
|
||||
map.crease_map.insert(
|
||||
[Crease::new(
|
||||
[Crease::inline(
|
||||
range,
|
||||
FoldPlaceholder::test(),
|
||||
|_row, _status, _toggle, _cx| div(),
|
||||
|
|
|
@ -7,7 +7,7 @@ use collections::{Bound, HashMap, HashSet};
|
|||
use gpui::{AnyElement, EntityId, Pixels, WindowContext};
|
||||
use language::{Chunk, Patch, Point};
|
||||
use multi_buffer::{
|
||||
Anchor, ExcerptId, ExcerptInfo, MultiBufferRow, MultiBufferSnapshot, ToPoint as _,
|
||||
Anchor, ExcerptId, ExcerptInfo, MultiBufferRow, MultiBufferSnapshot, ToOffset, ToPoint as _,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use std::{
|
||||
|
@ -77,7 +77,7 @@ pub struct BlockRow(pub(super) u32);
|
|||
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
|
||||
struct WrapRow(u32);
|
||||
|
||||
pub type RenderBlock = Box<dyn Send + FnMut(&mut BlockContext) -> AnyElement>;
|
||||
pub type RenderBlock = Arc<dyn Send + Sync + Fn(&mut BlockContext) -> AnyElement>;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum BlockPlacement<T> {
|
||||
|
@ -352,6 +352,13 @@ impl Block {
|
|||
Block::ExcerptBoundary { next_excerpt, .. } => next_excerpt.is_none(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_replacement(&self) -> bool {
|
||||
match self {
|
||||
Block::Custom(block) => matches!(block.placement, BlockPlacement::Replace(_)),
|
||||
Block::ExcerptBoundary { .. } => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Block {
|
||||
|
@ -1119,6 +1126,64 @@ impl<'a> BlockMapWriter<'a> {
|
|||
.retain(|id, _| !block_ids.contains(id));
|
||||
self.0.sync(wrap_snapshot, edits);
|
||||
}
|
||||
|
||||
pub fn remove_intersecting_replace_blocks<T>(
|
||||
&mut self,
|
||||
ranges: impl IntoIterator<Item = Range<T>>,
|
||||
inclusive: bool,
|
||||
) where
|
||||
T: ToOffset,
|
||||
{
|
||||
let wrap_snapshot = self.0.wrap_snapshot.borrow();
|
||||
let mut blocks_to_remove = HashSet::default();
|
||||
for range in ranges {
|
||||
let range = range.start.to_offset(wrap_snapshot.buffer_snapshot())
|
||||
..range.end.to_offset(wrap_snapshot.buffer_snapshot());
|
||||
for block in self.blocks_intersecting_buffer_range(range, inclusive) {
|
||||
if matches!(block.placement, BlockPlacement::Replace(_)) {
|
||||
blocks_to_remove.insert(block.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
drop(wrap_snapshot);
|
||||
self.remove(blocks_to_remove);
|
||||
}
|
||||
|
||||
fn blocks_intersecting_buffer_range(
|
||||
&self,
|
||||
range: Range<usize>,
|
||||
inclusive: bool,
|
||||
) -> &[Arc<CustomBlock>] {
|
||||
let wrap_snapshot = self.0.wrap_snapshot.borrow();
|
||||
let buffer = wrap_snapshot.buffer_snapshot();
|
||||
let start_block_ix = match self.0.custom_blocks.binary_search_by(|probe| {
|
||||
probe
|
||||
.end()
|
||||
.to_offset(buffer)
|
||||
.cmp(&range.start)
|
||||
.then(if inclusive {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Less
|
||||
})
|
||||
}) {
|
||||
Ok(ix) | Err(ix) => ix,
|
||||
};
|
||||
let end_block_ix = match self.0.custom_blocks.binary_search_by(|probe| {
|
||||
probe
|
||||
.start()
|
||||
.to_offset(buffer)
|
||||
.cmp(&range.end)
|
||||
.then(if inclusive {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Greater
|
||||
})
|
||||
}) {
|
||||
Ok(ix) | Err(ix) => ix,
|
||||
};
|
||||
&self.0.custom_blocks[start_block_ix..end_block_ix]
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockSnapshot {
|
||||
|
@ -1298,6 +1363,21 @@ impl BlockSnapshot {
|
|||
cursor.item().map_or(false, |t| t.block.is_some())
|
||||
}
|
||||
|
||||
pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool {
|
||||
let wrap_point = self
|
||||
.wrap_snapshot
|
||||
.make_wrap_point(Point::new(row.0, 0), Bias::Left);
|
||||
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
|
||||
cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
|
||||
cursor.item().map_or(false, |transform| {
|
||||
if let Some(Block::Custom(block)) = transform.block.as_ref() {
|
||||
matches!(block.placement, BlockPlacement::Replace(_))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
||||
cursor.seek(&BlockRow(point.row), Bias::Right, &());
|
||||
|
@ -1515,7 +1595,7 @@ impl<'a> Iterator for BlockChunks<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Iterator for BlockBufferRows<'a> {
|
||||
type Item = Option<BlockRow>;
|
||||
type Item = Option<u32>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.started {
|
||||
|
@ -1538,16 +1618,25 @@ impl<'a> Iterator for BlockBufferRows<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
if self.transforms.item()?.block.is_none() {
|
||||
let transform = self.transforms.item()?;
|
||||
if transform
|
||||
.block
|
||||
.as_ref()
|
||||
.map_or(true, |block| block.is_replacement())
|
||||
{
|
||||
self.input_buffer_rows.seek(self.transforms.start().1 .0);
|
||||
}
|
||||
}
|
||||
|
||||
let transform = self.transforms.item()?;
|
||||
if transform.block.is_some() {
|
||||
Some(None)
|
||||
if let Some(block) = transform.block.as_ref() {
|
||||
if block.is_replacement() && self.transforms.start().0 == self.output_row {
|
||||
Some(self.input_buffer_rows.next().unwrap())
|
||||
} else {
|
||||
Some(None)
|
||||
}
|
||||
} else {
|
||||
Some(self.input_buffer_rows.next().unwrap().map(BlockRow))
|
||||
Some(self.input_buffer_rows.next().unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1709,21 +1798,21 @@ mod tests {
|
|||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
|
||||
height: 1,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
},
|
||||
BlockProperties {
|
||||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
|
||||
height: 2,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
},
|
||||
BlockProperties {
|
||||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
|
||||
height: 3,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
},
|
||||
]);
|
||||
|
@ -1821,10 +1910,7 @@ mod tests {
|
|||
);
|
||||
|
||||
assert_eq!(
|
||||
snapshot
|
||||
.buffer_rows(BlockRow(0))
|
||||
.map(|row| row.map(|r| r.0))
|
||||
.collect::<Vec<_>>(),
|
||||
snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
|
||||
&[
|
||||
Some(0),
|
||||
None,
|
||||
|
@ -1960,21 +2046,21 @@ mod tests {
|
|||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
|
||||
height: 1,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
},
|
||||
BlockProperties {
|
||||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
|
||||
height: 2,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
},
|
||||
BlockProperties {
|
||||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
|
||||
height: 3,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
},
|
||||
]);
|
||||
|
@ -2062,14 +2148,14 @@ mod tests {
|
|||
BlockProperties {
|
||||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
height: 1,
|
||||
priority: 0,
|
||||
},
|
||||
BlockProperties {
|
||||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
height: 1,
|
||||
priority: 0,
|
||||
},
|
||||
|
@ -2109,7 +2195,7 @@ mod tests {
|
|||
..buffer_snapshot.anchor_before(Point::new(3, 1)),
|
||||
),
|
||||
height: 4,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
}]);
|
||||
|
||||
|
@ -2162,14 +2248,14 @@ mod tests {
|
|||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
|
||||
height: 1,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
},
|
||||
BlockProperties {
|
||||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
|
||||
height: 1,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
},
|
||||
]);
|
||||
|
@ -2183,21 +2269,21 @@ mod tests {
|
|||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
|
||||
height: 1,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
},
|
||||
BlockProperties {
|
||||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
|
||||
height: 1,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
},
|
||||
BlockProperties {
|
||||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
|
||||
height: 1,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
},
|
||||
]);
|
||||
|
@ -2302,7 +2388,7 @@ mod tests {
|
|||
style: BlockStyle::Fixed,
|
||||
placement,
|
||||
height,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
}
|
||||
})
|
||||
|
@ -2321,7 +2407,7 @@ mod tests {
|
|||
placement: props.placement.clone(),
|
||||
height: props.height,
|
||||
style: props.style,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
}));
|
||||
}
|
||||
|
@ -2409,6 +2495,7 @@ mod tests {
|
|||
let mut expected_buffer_rows = Vec::new();
|
||||
let mut expected_text = String::new();
|
||||
let mut expected_block_positions = Vec::new();
|
||||
let mut expected_replaced_buffer_rows = HashSet::default();
|
||||
let input_text = wraps_snapshot.text();
|
||||
|
||||
// Loop over the input lines, creating (N - 1) empty lines for
|
||||
|
@ -2422,6 +2509,9 @@ mod tests {
|
|||
let mut block_row = 0;
|
||||
while let Some((wrap_row, input_line)) = input_text_lines.next() {
|
||||
let wrap_row = wrap_row as u32;
|
||||
let multibuffer_row = wraps_snapshot
|
||||
.to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
|
||||
.row;
|
||||
|
||||
// Create empty lines for the above block
|
||||
while let Some((placement, block)) = sorted_blocks_iter.peek() {
|
||||
|
@ -2451,30 +2541,33 @@ mod tests {
|
|||
{
|
||||
if wrap_row >= replace_range.start.0 {
|
||||
is_in_replace_block = true;
|
||||
|
||||
if wrap_row == replace_range.start.0 {
|
||||
expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
|
||||
}
|
||||
|
||||
if wrap_row == replace_range.end.0 {
|
||||
expected_block_positions.push((block_row, block.id()));
|
||||
if block.height() > 0 {
|
||||
let text = "\n".repeat((block.height() - 1) as usize);
|
||||
if block_row > 0 {
|
||||
expected_text.push('\n');
|
||||
}
|
||||
expected_text.push_str(&text);
|
||||
for _ in 0..block.height() {
|
||||
expected_buffer_rows.push(None);
|
||||
}
|
||||
block_row += block.height();
|
||||
let text = "\n".repeat((block.height() - 1) as usize);
|
||||
if block_row > 0 {
|
||||
expected_text.push('\n');
|
||||
}
|
||||
expected_text.push_str(&text);
|
||||
|
||||
for _ in 1..block.height() {
|
||||
expected_buffer_rows.push(None);
|
||||
}
|
||||
block_row += block.height();
|
||||
|
||||
sorted_blocks_iter.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !is_in_replace_block {
|
||||
let buffer_row = input_buffer_rows[wraps_snapshot
|
||||
.to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
|
||||
.row as usize];
|
||||
|
||||
if is_in_replace_block {
|
||||
expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
|
||||
} else {
|
||||
let buffer_row = input_buffer_rows[multibuffer_row as usize];
|
||||
let soft_wrapped = wraps_snapshot
|
||||
.to_tab_point(WrapPoint::new(wrap_row, 0))
|
||||
.column()
|
||||
|
@ -2543,9 +2636,10 @@ mod tests {
|
|||
assert_eq!(
|
||||
blocks_snapshot
|
||||
.buffer_rows(BlockRow(start_row as u32))
|
||||
.map(|row| row.map(|r| r.0))
|
||||
.collect::<Vec<_>>(),
|
||||
&expected_buffer_rows[start_row..]
|
||||
&expected_buffer_rows[start_row..],
|
||||
"incorrect buffer_rows starting at row {:?}",
|
||||
start_row
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2666,6 +2760,16 @@ mod tests {
|
|||
block_point.column += c.len_utf8() as u32;
|
||||
}
|
||||
}
|
||||
|
||||
for buffer_row in 0..=buffer_snapshot.max_point().row {
|
||||
let buffer_row = MultiBufferRow(buffer_row);
|
||||
assert_eq!(
|
||||
blocks_snapshot.is_line_replaced(buffer_row),
|
||||
expected_replaced_buffer_rows.contains(&buffer_row),
|
||||
"incorrect is_line_replaced({:?})",
|
||||
buffer_row
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,12 +2,12 @@ use collections::HashMap;
|
|||
use gpui::{AnyElement, IntoElement};
|
||||
use multi_buffer::{Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, ToPoint};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{cmp::Ordering, ops::Range, sync::Arc};
|
||||
use std::{cmp::Ordering, fmt::Debug, ops::Range, sync::Arc};
|
||||
use sum_tree::{Bias, SeekTarget, SumTree};
|
||||
use text::Point;
|
||||
use ui::{IconName, SharedString, WindowContext};
|
||||
|
||||
use crate::FoldPlaceholder;
|
||||
use crate::{BlockStyle, FoldPlaceholder, RenderBlock};
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||
pub struct CreaseId(usize);
|
||||
|
@ -45,15 +45,15 @@ impl CreaseSnapshot {
|
|||
&'a self,
|
||||
row: MultiBufferRow,
|
||||
snapshot: &'a MultiBufferSnapshot,
|
||||
) -> Option<&'a Crease> {
|
||||
) -> Option<&'a Crease<Anchor>> {
|
||||
let start = snapshot.anchor_before(Point::new(row.0, 0));
|
||||
let mut cursor = self.creases.cursor::<ItemSummary>(snapshot);
|
||||
cursor.seek(&start, Bias::Left, snapshot);
|
||||
while let Some(item) = cursor.item() {
|
||||
match Ord::cmp(&item.crease.range.start.to_point(snapshot).row, &row.0) {
|
||||
match Ord::cmp(&item.crease.range().start.to_point(snapshot).row, &row.0) {
|
||||
Ordering::Less => cursor.next(snapshot),
|
||||
Ordering::Equal => {
|
||||
if item.crease.range.start.is_valid(snapshot) {
|
||||
if item.crease.range().start.is_valid(snapshot) {
|
||||
return Some(&item.crease);
|
||||
} else {
|
||||
cursor.next(snapshot);
|
||||
|
@ -69,7 +69,7 @@ impl CreaseSnapshot {
|
|||
&'a self,
|
||||
range: Range<MultiBufferRow>,
|
||||
snapshot: &'a MultiBufferSnapshot,
|
||||
) -> impl 'a + Iterator<Item = &'a Crease> {
|
||||
) -> impl 'a + Iterator<Item = &'a Crease<Anchor>> {
|
||||
let start = snapshot.anchor_before(Point::new(range.start.0, 0));
|
||||
let mut cursor = self.creases.cursor::<ItemSummary>(snapshot);
|
||||
cursor.seek(&start, Bias::Left, snapshot);
|
||||
|
@ -77,8 +77,9 @@ impl CreaseSnapshot {
|
|||
std::iter::from_fn(move || {
|
||||
while let Some(item) = cursor.item() {
|
||||
cursor.next(snapshot);
|
||||
let crease_start = item.crease.range.start.to_point(snapshot);
|
||||
let crease_end = item.crease.range.end.to_point(snapshot);
|
||||
let crease_range = item.crease.range();
|
||||
let crease_start = crease_range.start.to_point(snapshot);
|
||||
let crease_end = crease_range.end.to_point(snapshot);
|
||||
if crease_end.row > range.end.0 {
|
||||
continue;
|
||||
}
|
||||
|
@ -99,8 +100,9 @@ impl CreaseSnapshot {
|
|||
|
||||
cursor.next(snapshot);
|
||||
while let Some(item) = cursor.item() {
|
||||
let start_point = item.crease.range.start.to_point(snapshot);
|
||||
let end_point = item.crease.range.end.to_point(snapshot);
|
||||
let crease_range = item.crease.range();
|
||||
let start_point = crease_range.start.to_point(snapshot);
|
||||
let end_point = crease_range.end.to_point(snapshot);
|
||||
results.push((item.id, start_point..end_point));
|
||||
cursor.next(snapshot);
|
||||
}
|
||||
|
@ -123,12 +125,22 @@ type RenderTrailerFn =
|
|||
Arc<dyn Send + Sync + Fn(MultiBufferRow, bool, &mut WindowContext) -> AnyElement>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Crease {
|
||||
pub range: Range<Anchor>,
|
||||
pub placeholder: FoldPlaceholder,
|
||||
pub render_toggle: RenderToggleFn,
|
||||
pub render_trailer: RenderTrailerFn,
|
||||
pub metadata: Option<CreaseMetadata>,
|
||||
pub enum Crease<T> {
|
||||
Inline {
|
||||
range: Range<T>,
|
||||
placeholder: FoldPlaceholder,
|
||||
render_toggle: Option<RenderToggleFn>,
|
||||
render_trailer: Option<RenderTrailerFn>,
|
||||
metadata: Option<CreaseMetadata>,
|
||||
},
|
||||
Block {
|
||||
range: Range<T>,
|
||||
block_height: u32,
|
||||
block_style: BlockStyle,
|
||||
render_block: RenderBlock,
|
||||
block_priority: usize,
|
||||
render_toggle: Option<RenderToggleFn>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Metadata about a [`Crease`], that is used for serialization.
|
||||
|
@ -138,9 +150,30 @@ pub struct CreaseMetadata {
|
|||
pub label: SharedString,
|
||||
}
|
||||
|
||||
impl Crease {
|
||||
pub fn new<RenderToggle, ToggleElement, RenderTrailer, TrailerElement>(
|
||||
range: Range<Anchor>,
|
||||
impl<T> Crease<T> {
|
||||
pub fn simple(range: Range<T>, placeholder: FoldPlaceholder) -> Self {
|
||||
Crease::Inline {
|
||||
range,
|
||||
placeholder,
|
||||
render_toggle: None,
|
||||
render_trailer: None,
|
||||
metadata: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn block(range: Range<T>, height: u32, style: BlockStyle, render: RenderBlock) -> Self {
|
||||
Self::Block {
|
||||
range,
|
||||
block_height: height,
|
||||
block_style: style,
|
||||
render_block: render,
|
||||
block_priority: 0,
|
||||
render_toggle: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inline<RenderToggle, ToggleElement, RenderTrailer, TrailerElement>(
|
||||
range: Range<T>,
|
||||
placeholder: FoldPlaceholder,
|
||||
render_toggle: RenderToggle,
|
||||
render_trailer: RenderTrailer,
|
||||
|
@ -164,37 +197,76 @@ impl Crease {
|
|||
+ 'static,
|
||||
TrailerElement: IntoElement,
|
||||
{
|
||||
Crease {
|
||||
Crease::Inline {
|
||||
range,
|
||||
placeholder,
|
||||
render_toggle: Arc::new(move |row, folded, toggle, cx| {
|
||||
render_toggle: Some(Arc::new(move |row, folded, toggle, cx| {
|
||||
render_toggle(row, folded, toggle, cx).into_any_element()
|
||||
}),
|
||||
render_trailer: Arc::new(move |row, folded, cx| {
|
||||
})),
|
||||
render_trailer: Some(Arc::new(move |row, folded, cx| {
|
||||
render_trailer(row, folded, cx).into_any_element()
|
||||
}),
|
||||
})),
|
||||
metadata: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_metadata(mut self, metadata: CreaseMetadata) -> Self {
|
||||
self.metadata = Some(metadata);
|
||||
self
|
||||
pub fn with_metadata(self, metadata: CreaseMetadata) -> Self {
|
||||
match self {
|
||||
Crease::Inline {
|
||||
range,
|
||||
placeholder,
|
||||
render_toggle,
|
||||
render_trailer,
|
||||
..
|
||||
} => Crease::Inline {
|
||||
range,
|
||||
placeholder,
|
||||
render_toggle,
|
||||
render_trailer,
|
||||
metadata: Some(metadata),
|
||||
},
|
||||
Crease::Block { .. } => self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn range(&self) -> &Range<T> {
|
||||
match self {
|
||||
Crease::Inline { range, .. } => range,
|
||||
Crease::Block { range, .. } => range,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Crease {
|
||||
impl<T> std::fmt::Debug for Crease<T>
|
||||
where
|
||||
T: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Crease")
|
||||
.field("range", &self.range)
|
||||
.finish()
|
||||
match self {
|
||||
Crease::Inline {
|
||||
range, metadata, ..
|
||||
} => f
|
||||
.debug_struct("Crease::Inline")
|
||||
.field("range", range)
|
||||
.field("metadata", metadata)
|
||||
.finish_non_exhaustive(),
|
||||
Crease::Block {
|
||||
range,
|
||||
block_height,
|
||||
..
|
||||
} => f
|
||||
.debug_struct("Crease::Block")
|
||||
.field("range", range)
|
||||
.field("height", block_height)
|
||||
.finish_non_exhaustive(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct CreaseItem {
|
||||
id: CreaseId,
|
||||
crease: Crease,
|
||||
crease: Crease<Anchor>,
|
||||
}
|
||||
|
||||
impl CreaseMap {
|
||||
|
@ -204,7 +276,7 @@ impl CreaseMap {
|
|||
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
creases: impl IntoIterator<Item = Crease>,
|
||||
creases: impl IntoIterator<Item = Crease<Anchor>>,
|
||||
snapshot: &MultiBufferSnapshot,
|
||||
) -> Vec<CreaseId> {
|
||||
let mut new_ids = Vec::new();
|
||||
|
@ -212,11 +284,12 @@ impl CreaseMap {
|
|||
let mut new_creases = SumTree::new(snapshot);
|
||||
let mut cursor = self.snapshot.creases.cursor::<ItemSummary>(snapshot);
|
||||
for crease in creases {
|
||||
new_creases.append(cursor.slice(&crease.range, Bias::Left, snapshot), snapshot);
|
||||
let crease_range = crease.range().clone();
|
||||
new_creases.append(cursor.slice(&crease_range, Bias::Left, snapshot), snapshot);
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id.0 += 1;
|
||||
self.id_to_range.insert(id, crease.range.clone());
|
||||
self.id_to_range.insert(id, crease_range);
|
||||
new_creases.push(CreaseItem { crease, id }, snapshot);
|
||||
new_ids.push(id);
|
||||
}
|
||||
|
@ -293,7 +366,7 @@ impl sum_tree::Item for CreaseItem {
|
|||
|
||||
fn summary(&self, _cx: &MultiBufferSnapshot) -> Self::Summary {
|
||||
ItemSummary {
|
||||
range: self.crease.range.clone(),
|
||||
range: self.crease.range().clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -326,13 +399,13 @@ mod test {
|
|||
|
||||
// Insert creases
|
||||
let creases = [
|
||||
Crease::new(
|
||||
Crease::inline(
|
||||
snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 5)),
|
||||
FoldPlaceholder::test(),
|
||||
|_row, _folded, _toggle, _cx| div(),
|
||||
|_row, _folded, _cx| div(),
|
||||
),
|
||||
Crease::new(
|
||||
Crease::inline(
|
||||
snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_after(Point::new(3, 5)),
|
||||
FoldPlaceholder::test(),
|
||||
|_row, _folded, _toggle, _cx| div(),
|
||||
|
@ -372,19 +445,19 @@ mod test {
|
|||
let mut crease_map = CreaseMap::new(&snapshot);
|
||||
|
||||
let creases = [
|
||||
Crease::new(
|
||||
Crease::inline(
|
||||
snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 5)),
|
||||
FoldPlaceholder::test(),
|
||||
|_row, _folded, _toggle, _cx| div(),
|
||||
|_row, _folded, _cx| div(),
|
||||
),
|
||||
Crease::new(
|
||||
Crease::inline(
|
||||
snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_after(Point::new(3, 5)),
|
||||
FoldPlaceholder::test(),
|
||||
|_row, _folded, _toggle, _cx| div(),
|
||||
|_row, _folded, _cx| div(),
|
||||
),
|
||||
Crease::new(
|
||||
Crease::inline(
|
||||
snapshot.anchor_before(Point::new(5, 0))..snapshot.anchor_after(Point::new(5, 5)),
|
||||
FoldPlaceholder::test(),
|
||||
|_row, _folded, _toggle, _cx| div(),
|
||||
|
@ -402,12 +475,12 @@ mod test {
|
|||
let range = MultiBufferRow(2)..MultiBufferRow(5);
|
||||
let creases: Vec<_> = crease_snapshot.creases_in_range(range, &snapshot).collect();
|
||||
assert_eq!(creases.len(), 1);
|
||||
assert_eq!(creases[0].range.start.to_point(&snapshot).row, 3);
|
||||
assert_eq!(creases[0].range().start.to_point(&snapshot).row, 3);
|
||||
|
||||
let range = MultiBufferRow(0)..MultiBufferRow(2);
|
||||
let creases: Vec<_> = crease_snapshot.creases_in_range(range, &snapshot).collect();
|
||||
assert_eq!(creases.len(), 1);
|
||||
assert_eq!(creases[0].range.start.to_point(&snapshot).row, 1);
|
||||
assert_eq!(creases[0].range().start.to_point(&snapshot).row, 1);
|
||||
|
||||
let range = MultiBufferRow(6)..MultiBufferRow(7);
|
||||
let creases: Vec<_> = crease_snapshot.creases_in_range(range, &snapshot).collect();
|
||||
|
|
|
@ -6779,7 +6779,7 @@ impl Editor {
|
|||
|
||||
let mut edits = Vec::new();
|
||||
let mut unfold_ranges = Vec::new();
|
||||
let mut refold_ranges = Vec::new();
|
||||
let mut refold_creases = Vec::new();
|
||||
|
||||
let selections = self.selections.all::<Point>(cx);
|
||||
let mut selections = selections.iter().peekable();
|
||||
|
@ -6854,7 +6854,7 @@ impl Editor {
|
|||
let mut end = fold.range.end.to_point(&buffer);
|
||||
start.row -= row_delta;
|
||||
end.row -= row_delta;
|
||||
refold_ranges.push((start..end, fold.placeholder.clone()));
|
||||
refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6870,7 +6870,7 @@ impl Editor {
|
|||
buffer.edit([(range, text)], None, cx);
|
||||
}
|
||||
});
|
||||
this.fold_ranges(refold_ranges, true, cx);
|
||||
this.fold_creases(refold_creases, true, cx);
|
||||
this.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.select(new_selections);
|
||||
})
|
||||
|
@ -6883,7 +6883,7 @@ impl Editor {
|
|||
|
||||
let mut edits = Vec::new();
|
||||
let mut unfold_ranges = Vec::new();
|
||||
let mut refold_ranges = Vec::new();
|
||||
let mut refold_creases = Vec::new();
|
||||
|
||||
let selections = self.selections.all::<Point>(cx);
|
||||
let mut selections = selections.iter().peekable();
|
||||
|
@ -6948,7 +6948,7 @@ impl Editor {
|
|||
let mut end = fold.range.end.to_point(&buffer);
|
||||
start.row += row_delta;
|
||||
end.row += row_delta;
|
||||
refold_ranges.push((start..end, fold.placeholder.clone()));
|
||||
refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6964,7 +6964,7 @@ impl Editor {
|
|||
buffer.edit([(range, text)], None, cx);
|
||||
}
|
||||
});
|
||||
this.fold_ranges(refold_ranges, true, cx);
|
||||
this.fold_creases(refold_creases, true, cx);
|
||||
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
|
||||
});
|
||||
}
|
||||
|
@ -10421,7 +10421,7 @@ impl Editor {
|
|||
style: BlockStyle::Flex,
|
||||
placement: BlockPlacement::Below(range.start),
|
||||
height: 1,
|
||||
render: Box::new({
|
||||
render: Arc::new({
|
||||
let rename_editor = rename_editor.clone();
|
||||
move |cx: &mut BlockContext| {
|
||||
let mut text_style = cx.editor_style.text.clone();
|
||||
|
@ -10431,6 +10431,7 @@ impl Editor {
|
|||
text_style = text_style.highlight(highlight_style);
|
||||
}
|
||||
div()
|
||||
.occlude()
|
||||
.pl(cx.anchor_x)
|
||||
.child(EditorElement::new(
|
||||
&rename_editor,
|
||||
|
@ -10894,7 +10895,7 @@ impl Editor {
|
|||
}
|
||||
|
||||
pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
|
||||
let mut fold_ranges = Vec::new();
|
||||
let mut to_fold = Vec::new();
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let selections = self.selections.all_adjusted(cx);
|
||||
|
||||
|
@ -10906,12 +10907,10 @@ impl Editor {
|
|||
let mut found = false;
|
||||
let mut row = range.start.row;
|
||||
while row <= range.end.row {
|
||||
if let Some((foldable_range, fold_text)) =
|
||||
{ display_map.foldable_range(MultiBufferRow(row)) }
|
||||
{
|
||||
if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
|
||||
found = true;
|
||||
row = foldable_range.end.row + 1;
|
||||
fold_ranges.push((foldable_range, fold_text));
|
||||
row = crease.range().end.row + 1;
|
||||
to_fold.push(crease);
|
||||
} else {
|
||||
row += 1
|
||||
}
|
||||
|
@ -10922,11 +10921,9 @@ impl Editor {
|
|||
}
|
||||
|
||||
for row in (0..=range.start.row).rev() {
|
||||
if let Some((foldable_range, fold_text)) =
|
||||
display_map.foldable_range(MultiBufferRow(row))
|
||||
{
|
||||
if foldable_range.end.row >= buffer_start_row {
|
||||
fold_ranges.push((foldable_range, fold_text));
|
||||
if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
|
||||
if crease.range().end.row >= buffer_start_row {
|
||||
to_fold.push(crease);
|
||||
if row <= range.start.row {
|
||||
break;
|
||||
}
|
||||
|
@ -10935,26 +10932,29 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
self.fold_ranges(fold_ranges, true, cx);
|
||||
self.fold_creases(to_fold, true, cx);
|
||||
}
|
||||
|
||||
fn fold_at_level(&mut self, fold_at: &FoldAtLevel, cx: &mut ViewContext<Self>) {
|
||||
let fold_at_level = fold_at.level;
|
||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
let mut fold_ranges = Vec::new();
|
||||
let mut to_fold = Vec::new();
|
||||
let mut stack = vec![(0, snapshot.max_buffer_row().0, 1)];
|
||||
|
||||
while let Some((mut start_row, end_row, current_level)) = stack.pop() {
|
||||
while start_row < end_row {
|
||||
match self.snapshot(cx).foldable_range(MultiBufferRow(start_row)) {
|
||||
Some(foldable_range) => {
|
||||
let nested_start_row = foldable_range.0.start.row + 1;
|
||||
let nested_end_row = foldable_range.0.end.row;
|
||||
match self
|
||||
.snapshot(cx)
|
||||
.crease_for_buffer_row(MultiBufferRow(start_row))
|
||||
{
|
||||
Some(crease) => {
|
||||
let nested_start_row = crease.range().start.row + 1;
|
||||
let nested_end_row = crease.range().end.row;
|
||||
|
||||
if current_level < fold_at_level {
|
||||
stack.push((nested_start_row, nested_end_row, current_level + 1));
|
||||
} else if current_level == fold_at_level {
|
||||
fold_ranges.push(foldable_range);
|
||||
to_fold.push(crease);
|
||||
}
|
||||
|
||||
start_row = nested_end_row + 1;
|
||||
|
@ -10964,7 +10964,7 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
self.fold_ranges(fold_ranges, true, cx);
|
||||
self.fold_creases(to_fold, true, cx);
|
||||
}
|
||||
|
||||
pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext<Self>) {
|
||||
|
@ -10972,16 +10972,18 @@ impl Editor {
|
|||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
|
||||
for row in 0..snapshot.max_buffer_row().0 {
|
||||
if let Some(foldable_range) = self.snapshot(cx).foldable_range(MultiBufferRow(row)) {
|
||||
if let Some(foldable_range) =
|
||||
self.snapshot(cx).crease_for_buffer_row(MultiBufferRow(row))
|
||||
{
|
||||
fold_ranges.push(foldable_range);
|
||||
}
|
||||
}
|
||||
|
||||
self.fold_ranges(fold_ranges, true, cx);
|
||||
self.fold_creases(fold_ranges, true, cx);
|
||||
}
|
||||
|
||||
pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
|
||||
let mut fold_ranges = Vec::new();
|
||||
let mut to_fold = Vec::new();
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let selections = self.selections.all_adjusted(cx);
|
||||
|
||||
|
@ -10992,11 +10994,9 @@ impl Editor {
|
|||
if range.start.row != range.end.row {
|
||||
let mut found = false;
|
||||
for row in range.start.row..=range.end.row {
|
||||
if let Some((foldable_range, fold_text)) =
|
||||
{ display_map.foldable_range(MultiBufferRow(row)) }
|
||||
{
|
||||
if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
|
||||
found = true;
|
||||
fold_ranges.push((foldable_range, fold_text));
|
||||
to_fold.push(crease);
|
||||
}
|
||||
}
|
||||
if found {
|
||||
|
@ -11005,11 +11005,9 @@ impl Editor {
|
|||
}
|
||||
|
||||
for row in (0..=range.start.row).rev() {
|
||||
if let Some((foldable_range, fold_text)) =
|
||||
display_map.foldable_range(MultiBufferRow(row))
|
||||
{
|
||||
if foldable_range.end.row >= buffer_start_row {
|
||||
fold_ranges.push((foldable_range, fold_text));
|
||||
if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
|
||||
if crease.range().end.row >= buffer_start_row {
|
||||
to_fold.push(crease);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -11017,21 +11015,21 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
self.fold_ranges(fold_ranges, true, cx);
|
||||
self.fold_creases(to_fold, true, cx);
|
||||
}
|
||||
|
||||
pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
|
||||
let buffer_row = fold_at.buffer_row;
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
|
||||
if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
|
||||
if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
|
||||
let autoscroll = self
|
||||
.selections
|
||||
.all::<Point>(cx)
|
||||
.iter()
|
||||
.any(|selection| fold_range.overlaps(&selection.range()));
|
||||
.any(|selection| crease.range().overlaps(&selection.range()));
|
||||
|
||||
self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
|
||||
self.fold_creases(vec![crease], autoscroll, cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11092,81 +11090,78 @@ impl Editor {
|
|||
|
||||
pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext<Self>) {
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
self.unfold_ranges(
|
||||
&[Point::zero()..display_map.max_point().to_point(&display_map)],
|
||||
true,
|
||||
true,
|
||||
cx,
|
||||
);
|
||||
self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
|
||||
}
|
||||
|
||||
pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
|
||||
let selections = self.selections.all::<Point>(cx);
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let line_mode = self.selections.line_mode;
|
||||
let ranges = selections.into_iter().map(|s| {
|
||||
if line_mode {
|
||||
let start = Point::new(s.start.row, 0);
|
||||
let end = Point::new(
|
||||
s.end.row,
|
||||
display_map
|
||||
.buffer_snapshot
|
||||
.line_len(MultiBufferRow(s.end.row)),
|
||||
);
|
||||
(start..end, display_map.fold_placeholder.clone())
|
||||
} else {
|
||||
(s.start..s.end, display_map.fold_placeholder.clone())
|
||||
}
|
||||
});
|
||||
self.fold_ranges(ranges, true, cx);
|
||||
let ranges = selections
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
if line_mode {
|
||||
let start = Point::new(s.start.row, 0);
|
||||
let end = Point::new(
|
||||
s.end.row,
|
||||
display_map
|
||||
.buffer_snapshot
|
||||
.line_len(MultiBufferRow(s.end.row)),
|
||||
);
|
||||
Crease::simple(start..end, display_map.fold_placeholder.clone())
|
||||
} else {
|
||||
Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
self.fold_creases(ranges, true, cx);
|
||||
}
|
||||
|
||||
pub fn fold_ranges<T: ToOffset + Clone>(
|
||||
pub fn fold_creases<T: ToOffset + Clone>(
|
||||
&mut self,
|
||||
ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
|
||||
creases: Vec<Crease<T>>,
|
||||
auto_scroll: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let mut fold_ranges = Vec::new();
|
||||
if creases.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut buffers_affected = HashMap::default();
|
||||
let multi_buffer = self.buffer().read(cx);
|
||||
for (fold_range, fold_text) in ranges {
|
||||
for crease in &creases {
|
||||
if let Some((_, buffer, _)) =
|
||||
multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
|
||||
multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
|
||||
{
|
||||
buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
|
||||
};
|
||||
fold_ranges.push((fold_range, fold_text));
|
||||
}
|
||||
|
||||
let mut ranges = fold_ranges.into_iter().peekable();
|
||||
if ranges.peek().is_some() {
|
||||
self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
|
||||
self.display_map.update(cx, |map, cx| map.fold(creases, cx));
|
||||
|
||||
if auto_scroll {
|
||||
self.request_autoscroll(Autoscroll::fit(), cx);
|
||||
}
|
||||
|
||||
for buffer in buffers_affected.into_values() {
|
||||
self.sync_expanded_diff_hunks(buffer, cx);
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
|
||||
if let Some(active_diagnostics) = self.active_diagnostics.take() {
|
||||
// Clear diagnostics block when folding a range that contains it.
|
||||
let snapshot = self.snapshot(cx);
|
||||
if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
|
||||
drop(snapshot);
|
||||
self.active_diagnostics = Some(active_diagnostics);
|
||||
self.dismiss_diagnostics(cx);
|
||||
} else {
|
||||
self.active_diagnostics = Some(active_diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
self.scrollbar_marker_state.dirty = true;
|
||||
if auto_scroll {
|
||||
self.request_autoscroll(Autoscroll::fit(), cx);
|
||||
}
|
||||
|
||||
for buffer in buffers_affected.into_values() {
|
||||
self.sync_expanded_diff_hunks(buffer, cx);
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
|
||||
if let Some(active_diagnostics) = self.active_diagnostics.take() {
|
||||
// Clear diagnostics block when folding a range that contains it.
|
||||
let snapshot = self.snapshot(cx);
|
||||
if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
|
||||
drop(snapshot);
|
||||
self.active_diagnostics = Some(active_diagnostics);
|
||||
self.dismiss_diagnostics(cx);
|
||||
} else {
|
||||
self.active_diagnostics = Some(active_diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
self.scrollbar_marker_state.dirty = true;
|
||||
}
|
||||
|
||||
/// Removes any folds whose ranges intersect any of the given ranges.
|
||||
|
@ -11215,6 +11210,7 @@ impl Editor {
|
|||
}
|
||||
|
||||
self.display_map.update(cx, update);
|
||||
|
||||
if auto_scroll {
|
||||
self.request_autoscroll(Autoscroll::fit(), cx);
|
||||
}
|
||||
|
@ -11317,7 +11313,7 @@ impl Editor {
|
|||
|
||||
pub fn insert_creases(
|
||||
&mut self,
|
||||
creases: impl IntoIterator<Item = Crease>,
|
||||
creases: impl IntoIterator<Item = Crease<Anchor>>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Vec<CreaseId> {
|
||||
self.display_map
|
||||
|
@ -14056,7 +14052,7 @@ impl EditorSnapshot {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn render_fold_toggle(
|
||||
pub fn render_crease_toggle(
|
||||
&self,
|
||||
buffer_row: MultiBufferRow,
|
||||
row_contains_cursor: bool,
|
||||
|
@ -14064,34 +14060,38 @@ impl EditorSnapshot {
|
|||
cx: &mut WindowContext,
|
||||
) -> Option<AnyElement> {
|
||||
let folded = self.is_line_folded(buffer_row);
|
||||
let mut is_foldable = false;
|
||||
|
||||
if let Some(crease) = self
|
||||
.crease_snapshot
|
||||
.query_row(buffer_row, &self.buffer_snapshot)
|
||||
{
|
||||
let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
|
||||
if folded {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.fold_at(&crate::FoldAt { buffer_row }, cx)
|
||||
});
|
||||
} else {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
|
||||
});
|
||||
is_foldable = true;
|
||||
match crease {
|
||||
Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
|
||||
if let Some(render_toggle) = render_toggle {
|
||||
let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
|
||||
if folded {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.fold_at(&crate::FoldAt { buffer_row }, cx)
|
||||
});
|
||||
} else {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
|
||||
});
|
||||
}
|
||||
});
|
||||
return Some((render_toggle)(buffer_row, folded, toggle_callback, cx));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Some((crease.render_toggle)(
|
||||
buffer_row,
|
||||
folded,
|
||||
toggle_callback,
|
||||
cx,
|
||||
))
|
||||
} else if folded
|
||||
|| (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
|
||||
{
|
||||
is_foldable |= self.starts_indent(buffer_row);
|
||||
|
||||
if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
|
||||
Some(
|
||||
Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
|
||||
Disclosure::new(("gutter_crease", buffer_row.0), !folded)
|
||||
.selected(folded)
|
||||
.on_click(cx.listener_for(&editor, move |this, _e, cx| {
|
||||
if folded {
|
||||
|
@ -14113,10 +14113,15 @@ impl EditorSnapshot {
|
|||
cx: &mut WindowContext,
|
||||
) -> Option<AnyElement> {
|
||||
let folded = self.is_line_folded(buffer_row);
|
||||
let crease = self
|
||||
if let Crease::Inline { render_trailer, .. } = self
|
||||
.crease_snapshot
|
||||
.query_row(buffer_row, &self.buffer_snapshot)?;
|
||||
Some((crease.render_trailer)(buffer_row, folded, cx))
|
||||
.query_row(buffer_row, &self.buffer_snapshot)?
|
||||
{
|
||||
let render_trailer = render_trailer.as_ref()?;
|
||||
Some(render_trailer(buffer_row, folded, cx))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14621,7 +14626,7 @@ pub fn diagnostic_block_renderer(
|
|||
let (text_without_backticks, code_ranges) =
|
||||
highlight_diagnostic_message(&diagnostic, max_message_rows);
|
||||
|
||||
Box::new(move |cx: &mut BlockContext| {
|
||||
Arc::new(move |cx: &mut BlockContext| {
|
||||
let group_id: SharedString = cx.block_id.to_string().into();
|
||||
|
||||
let mut text_style = cx.text_style().clone();
|
||||
|
@ -14676,6 +14681,7 @@ pub fn diagnostic_block_renderer(
|
|||
.group(group_id.clone())
|
||||
.relative()
|
||||
.size_full()
|
||||
.occlude()
|
||||
.pl(cx.gutter_dimensions.width)
|
||||
.w(cx.max_width - cx.gutter_dimensions.full_width())
|
||||
.child(
|
||||
|
|
|
@ -596,10 +596,10 @@ fn test_clone(cx: &mut TestAppContext) {
|
|||
|
||||
_ = editor.update(cx, |editor, cx| {
|
||||
editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
|
||||
editor.fold_ranges(
|
||||
[
|
||||
(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
|
||||
(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
|
||||
editor.fold_creases(
|
||||
vec![
|
||||
Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
|
||||
Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
|
||||
],
|
||||
true,
|
||||
cx,
|
||||
|
@ -1283,11 +1283,11 @@ fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
|
|||
assert_eq!('α'.len_utf8(), 2);
|
||||
|
||||
_ = view.update(cx, |view, cx| {
|
||||
view.fold_ranges(
|
||||
view.fold_creases(
|
||||
vec![
|
||||
(Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
|
||||
(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
|
||||
(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
|
||||
Crease::simple(Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
|
||||
Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
|
||||
Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
|
||||
],
|
||||
true,
|
||||
cx,
|
||||
|
@ -3875,11 +3875,11 @@ fn test_move_line_up_down(cx: &mut TestAppContext) {
|
|||
build_editor(buffer, cx)
|
||||
});
|
||||
_ = view.update(cx, |view, cx| {
|
||||
view.fold_ranges(
|
||||
view.fold_creases(
|
||||
vec![
|
||||
(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
|
||||
(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
|
||||
(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
|
||||
Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
|
||||
Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
|
||||
Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
|
||||
],
|
||||
true,
|
||||
cx,
|
||||
|
@ -3980,7 +3980,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
|
|||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
|
||||
height: 1,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
}],
|
||||
Some(Autoscroll::fit()),
|
||||
|
@ -4022,7 +4022,7 @@ async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
|
|||
placement,
|
||||
height: 4,
|
||||
style: BlockStyle::Sticky,
|
||||
render: Box::new(|_| gpui::div().into_any_element()),
|
||||
render: Arc::new(|_| gpui::div().into_any_element()),
|
||||
priority: 0,
|
||||
}],
|
||||
None,
|
||||
|
@ -4717,11 +4717,11 @@ fn test_split_selection_into_lines(cx: &mut TestAppContext) {
|
|||
build_editor(buffer, cx)
|
||||
});
|
||||
_ = view.update(cx, |view, cx| {
|
||||
view.fold_ranges(
|
||||
view.fold_creases(
|
||||
vec![
|
||||
(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
|
||||
(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
|
||||
(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
|
||||
Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
|
||||
Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
|
||||
Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
|
||||
],
|
||||
true,
|
||||
cx,
|
||||
|
@ -5398,13 +5398,13 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
|
|||
// Ensure that we keep expanding the selection if the larger selection starts or ends within
|
||||
// a fold.
|
||||
editor.update(cx, |view, cx| {
|
||||
view.fold_ranges(
|
||||
view.fold_creases(
|
||||
vec![
|
||||
(
|
||||
Crease::simple(
|
||||
Point::new(0, 21)..Point::new(0, 24),
|
||||
FoldPlaceholder::test(),
|
||||
),
|
||||
(
|
||||
Crease::simple(
|
||||
Point::new(3, 20)..Point::new(3, 22),
|
||||
FoldPlaceholder::test(),
|
||||
),
|
||||
|
@ -13139,7 +13139,7 @@ fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
|
|||
callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
|
||||
}
|
||||
|
||||
let crease = Crease::new(
|
||||
let crease = Crease::inline(
|
||||
range,
|
||||
FoldPlaceholder::test(),
|
||||
{
|
||||
|
@ -13158,7 +13158,8 @@ fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
|
|||
|
||||
editor.insert_creases(Some(crease), cx);
|
||||
let snapshot = editor.snapshot(cx);
|
||||
let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
|
||||
let _div =
|
||||
snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
|
||||
snapshot
|
||||
})
|
||||
.unwrap();
|
||||
|
|
|
@ -1227,9 +1227,9 @@ impl EditorElement {
|
|||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn prepaint_gutter_fold_toggles(
|
||||
fn prepaint_crease_toggles(
|
||||
&self,
|
||||
toggles: &mut [Option<AnyElement>],
|
||||
crease_toggles: &mut [Option<AnyElement>],
|
||||
line_height: Pixels,
|
||||
gutter_dimensions: &GutterDimensions,
|
||||
gutter_settings: crate::editor_settings::Gutter,
|
||||
|
@ -1237,25 +1237,25 @@ impl EditorElement {
|
|||
gutter_hitbox: &Hitbox,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
for (ix, fold_indicator) in toggles.iter_mut().enumerate() {
|
||||
if let Some(fold_indicator) = fold_indicator {
|
||||
for (ix, crease_toggle) in crease_toggles.iter_mut().enumerate() {
|
||||
if let Some(crease_toggle) = crease_toggle {
|
||||
debug_assert!(gutter_settings.folds);
|
||||
let available_space = size(
|
||||
AvailableSpace::MinContent,
|
||||
AvailableSpace::Definite(line_height * 0.55),
|
||||
);
|
||||
let fold_indicator_size = fold_indicator.layout_as_root(available_space, cx);
|
||||
let crease_toggle_size = crease_toggle.layout_as_root(available_space, cx);
|
||||
|
||||
let position = point(
|
||||
gutter_dimensions.width - gutter_dimensions.right_padding,
|
||||
ix as f32 * line_height - (scroll_pixel_position.y % line_height),
|
||||
);
|
||||
let centering_offset = point(
|
||||
(gutter_dimensions.fold_area_width() - fold_indicator_size.width) / 2.,
|
||||
(line_height - fold_indicator_size.height) / 2.,
|
||||
(gutter_dimensions.fold_area_width() - crease_toggle_size.width) / 2.,
|
||||
(line_height - crease_toggle_size.height) / 2.,
|
||||
);
|
||||
let origin = gutter_hitbox.origin + position + centering_offset;
|
||||
fold_indicator.prepaint_as_root(origin, available_space, cx);
|
||||
crease_toggle.prepaint_as_root(origin, available_space, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1915,7 +1915,7 @@ impl EditorElement {
|
|||
.collect()
|
||||
}
|
||||
|
||||
fn layout_gutter_fold_toggles(
|
||||
fn layout_crease_toggles(
|
||||
&self,
|
||||
rows: Range<DisplayRow>,
|
||||
buffer_rows: impl IntoIterator<Item = Option<MultiBufferRow>>,
|
||||
|
@ -1934,7 +1934,7 @@ impl EditorElement {
|
|||
if let Some(multibuffer_row) = row {
|
||||
let display_row = DisplayRow(rows.start.0 + ix as u32);
|
||||
let active = active_rows.contains_key(&display_row);
|
||||
snapshot.render_fold_toggle(
|
||||
snapshot.render_crease_toggle(
|
||||
multibuffer_row,
|
||||
active,
|
||||
self.editor.clone(),
|
||||
|
@ -2122,9 +2122,7 @@ impl EditorElement {
|
|||
max_width: text_hitbox.size.width.max(*scroll_width),
|
||||
editor_style: &self.style,
|
||||
}))
|
||||
.cursor(CursorStyle::Arrow)
|
||||
.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
|
||||
.into_any_element()
|
||||
.into_any()
|
||||
}
|
||||
|
||||
Block::ExcerptBoundary {
|
||||
|
@ -3354,9 +3352,9 @@ impl EditorElement {
|
|||
|
||||
fn paint_gutter_indicators(&self, layout: &mut EditorLayout, cx: &mut WindowContext) {
|
||||
cx.paint_layer(layout.gutter_hitbox.bounds, |cx| {
|
||||
cx.with_element_namespace("gutter_fold_toggles", |cx| {
|
||||
for fold_indicator in layout.gutter_fold_toggles.iter_mut().flatten() {
|
||||
fold_indicator.paint(cx);
|
||||
cx.with_element_namespace("crease_toggles", |cx| {
|
||||
for crease_toggle in layout.crease_toggles.iter_mut().flatten() {
|
||||
crease_toggle.paint(cx);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -5167,16 +5165,15 @@ impl Element for EditorElement {
|
|||
cx,
|
||||
);
|
||||
|
||||
let mut gutter_fold_toggles =
|
||||
cx.with_element_namespace("gutter_fold_toggles", |cx| {
|
||||
self.layout_gutter_fold_toggles(
|
||||
start_row..end_row,
|
||||
buffer_rows.iter().copied(),
|
||||
&active_rows,
|
||||
&snapshot,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let mut crease_toggles = cx.with_element_namespace("crease_toggles", |cx| {
|
||||
self.layout_crease_toggles(
|
||||
start_row..end_row,
|
||||
buffer_rows.iter().copied(),
|
||||
&active_rows,
|
||||
&snapshot,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let crease_trailers = cx.with_element_namespace("crease_trailers", |cx| {
|
||||
self.layout_crease_trailers(buffer_rows.iter().copied(), &snapshot, cx)
|
||||
});
|
||||
|
@ -5556,9 +5553,9 @@ impl Element for EditorElement {
|
|||
let mouse_context_menu =
|
||||
self.layout_mouse_context_menu(&snapshot, start_row..end_row, cx);
|
||||
|
||||
cx.with_element_namespace("gutter_fold_toggles", |cx| {
|
||||
self.prepaint_gutter_fold_toggles(
|
||||
&mut gutter_fold_toggles,
|
||||
cx.with_element_namespace("crease_toggles", |cx| {
|
||||
self.prepaint_crease_toggles(
|
||||
&mut crease_toggles,
|
||||
line_height,
|
||||
&gutter_dimensions,
|
||||
gutter_settings,
|
||||
|
@ -5638,7 +5635,7 @@ impl Element for EditorElement {
|
|||
mouse_context_menu,
|
||||
test_indicators,
|
||||
code_actions_indicator,
|
||||
gutter_fold_toggles,
|
||||
crease_toggles,
|
||||
crease_trailers,
|
||||
tab_invisible,
|
||||
space_invisible,
|
||||
|
@ -5671,7 +5668,6 @@ impl Element for EditorElement {
|
|||
line_height: Some(self.style.text.line_height),
|
||||
..Default::default()
|
||||
};
|
||||
let mouse_position = cx.mouse_position();
|
||||
let hovered_hunk = layout
|
||||
.display_hunks
|
||||
.iter()
|
||||
|
@ -5685,7 +5681,7 @@ impl Element for EditorElement {
|
|||
} => {
|
||||
if hunk_hitbox
|
||||
.as_ref()
|
||||
.map(|hitbox| hitbox.contains(&mouse_position))
|
||||
.map(|hitbox| hitbox.is_hovered(cx))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
Some(HoveredHunk {
|
||||
|
@ -5778,7 +5774,7 @@ pub struct EditorLayout {
|
|||
selections: Vec<(PlayerColor, Vec<SelectionLayout>)>,
|
||||
code_actions_indicator: Option<AnyElement>,
|
||||
test_indicators: Vec<AnyElement>,
|
||||
gutter_fold_toggles: Vec<Option<AnyElement>>,
|
||||
crease_toggles: Vec<Option<AnyElement>>,
|
||||
crease_trailers: Vec<Option<CreaseTrailerLayout>>,
|
||||
mouse_context_menu: Option<AnyElement>,
|
||||
tab_invisible: ShapedLine,
|
||||
|
@ -6623,7 +6619,7 @@ mod tests {
|
|||
style: BlockStyle::Fixed,
|
||||
placement: BlockPlacement::Above(Anchor::min()),
|
||||
height: 3,
|
||||
render: Box::new(|cx| div().h(3. * cx.line_height()).into_any()),
|
||||
render: Arc::new(|cx| div().h(3. * cx.line_height()).into_any()),
|
||||
priority: 0,
|
||||
}],
|
||||
None,
|
||||
|
|
|
@ -425,7 +425,7 @@ impl Editor {
|
|||
height: 1,
|
||||
style: BlockStyle::Sticky,
|
||||
priority: 0,
|
||||
render: Box::new({
|
||||
render: Arc::new({
|
||||
let editor = cx.view().clone();
|
||||
let hunk = hunk.clone();
|
||||
|
||||
|
@ -435,6 +435,7 @@ impl Editor {
|
|||
|
||||
h_flex()
|
||||
.id(cx.block_id)
|
||||
.occlude()
|
||||
.h(cx.line_height())
|
||||
.w_full()
|
||||
.border_t_1()
|
||||
|
@ -707,12 +708,13 @@ impl Editor {
|
|||
height,
|
||||
style: BlockStyle::Flex,
|
||||
priority: 0,
|
||||
render: Box::new(move |cx| {
|
||||
render: Arc::new(move |cx| {
|
||||
let width = EditorElement::diff_hunk_strip_width(cx.line_height());
|
||||
let gutter_dimensions = editor.read(cx.context).gutter_dimensions;
|
||||
|
||||
h_flex()
|
||||
.id(cx.block_id)
|
||||
.occlude()
|
||||
.bg(deleted_hunk_color)
|
||||
.h(height as f32 * cx.line_height())
|
||||
.w_full()
|
||||
|
|
|
@ -125,7 +125,7 @@ pub struct MultiBufferDiffHunk {
|
|||
|
||||
pub type MultiBufferPoint = Point;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, serde::Deserialize)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, Hash, serde::Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct MultiBufferRow(pub u32);
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ impl EditorBlock {
|
|||
execution_view: View<ExecutionView>,
|
||||
on_close: CloseBlockFn,
|
||||
) -> RenderBlock {
|
||||
let render = move |cx: &mut BlockContext| {
|
||||
Arc::new(move |cx: &mut BlockContext| {
|
||||
let execution_view = execution_view.clone();
|
||||
let text_style = crate::outputs::plain::text_style(cx);
|
||||
|
||||
|
@ -163,6 +163,7 @@ impl EditorBlock {
|
|||
|
||||
div()
|
||||
.id(cx.block_id)
|
||||
.occlude()
|
||||
.flex()
|
||||
.items_start()
|
||||
.min_h(text_line_height)
|
||||
|
@ -186,9 +187,7 @@ impl EditorBlock {
|
|||
.child(execution_view),
|
||||
)
|
||||
.into_any_element()
|
||||
};
|
||||
|
||||
Box::new(render)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue