Detailed changes
@@ -225,6 +225,12 @@ dependencies = [
"util",
]
+[[package]]
+name = "any_vec"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e78f17bacc1bc7b91fef7b1885c10772eb2b9e4e989356f6f0f6a972240f97cd"
+
[[package]]
name = "anyhow"
version = "1.0.75"
@@ -8296,6 +8302,7 @@ dependencies = [
name = "search"
version = "0.1.0"
dependencies = [
+ "any_vec",
"anyhow",
"bitflags 2.4.2",
"client",
@@ -12112,6 +12119,7 @@ dependencies = [
name = "workspace"
version = "0.1.0"
dependencies = [
+ "any_vec",
"anyhow",
"async-recursion 1.0.5",
"bincode",
@@ -220,6 +220,7 @@ zed = { path = "crates/zed" }
zed_actions = { path = "crates/zed_actions" }
anyhow = "1.0.57"
+any_vec = "0.13"
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
async-fs = "1.6"
async-recursion = "1.0.0"
@@ -345,7 +345,7 @@ impl AssistantPanel {
style: BlockStyle::Flex,
position: snapshot.anchor_before(point_selection.head()),
height: 2,
- render: Arc::new({
+ render: Box::new({
let inline_assistant = inline_assistant.clone();
move |cx: &mut BlockContext| {
*measurements.lock() = BlockMeasurements {
@@ -695,7 +695,7 @@ impl AssistantPanel {
editor.clear_background_highlights::<PendingInlineAssist>(cx);
} else {
editor.highlight_background::<PendingInlineAssist>(
- background_ranges,
+ &background_ranges,
|theme| theme.editor_active_line_background, // todo!("use the appropriate color")
cx,
);
@@ -2266,7 +2266,7 @@ impl ConversationEditor {
.unwrap(),
height: 2,
style: BlockStyle::Sticky,
- render: Arc::new({
+ render: Box::new({
let conversation = self.conversation.clone();
move |_cx| {
let message_id = message.id;
@@ -32,7 +32,6 @@ use std::{
mem,
ops::Range,
path::PathBuf,
- sync::Arc,
};
use theme::ActiveTheme;
pub use toolbar_controls::ToolbarControls;
@@ -805,7 +804,7 @@ impl Item for ProjectDiagnosticsEditor {
fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
let (message, code_ranges) = highlight_diagnostic_message(&diagnostic);
let message: SharedString = message;
- Arc::new(move |cx| {
+ Box::new(move |cx| {
let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into();
h_flex()
.id("diagnostic header")
@@ -26,7 +26,7 @@ mod wrap_map;
use crate::EditorStyle;
use crate::{hover_links::InlayHighlight, movement::TextLayoutDetails, InlayId};
pub use block_map::{BlockMap, BlockPoint};
-use collections::{BTreeMap, HashMap, HashSet};
+use collections::{HashMap, HashSet};
use fold_map::FoldMap;
use gpui::{Font, HighlightStyle, Hsla, LineLayout, Model, ModelContext, Pixels, UnderlineStyle};
use inlay_map::InlayMap;
@@ -63,7 +63,7 @@ pub trait ToDisplayPoint {
}
type TextHighlights = TreeMap<Option<TypeId>, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>;
-type InlayHighlights = BTreeMap<TypeId, HashMap<InlayId, (HighlightStyle, InlayHighlight)>>;
+type InlayHighlights = TreeMap<TypeId, TreeMap<InlayId, (HighlightStyle, InlayHighlight)>>;
/// Decides how text in a [`MultiBuffer`] should be displayed in a buffer, handling inlay hints,
/// folding, hard tabs, soft wrapping, custom blocks (like diagnostics), and highlighting.
@@ -257,10 +257,15 @@ impl DisplayMap {
style: HighlightStyle,
) {
for highlight in highlights {
- self.inlay_highlights
- .entry(type_id)
- .or_default()
- .insert(highlight.inlay, (style, highlight));
+ let update = self.inlay_highlights.update(&type_id, |highlights| {
+ highlights.insert(highlight.inlay, (style, highlight.clone()))
+ });
+ if update.is_none() {
+ self.inlay_highlights.insert(
+ type_id,
+ TreeMap::from_ordered_entries([(highlight.inlay, (style, highlight))]),
+ );
+ }
}
}
@@ -354,6 +359,7 @@ pub struct HighlightedChunk<'a> {
pub is_tab: bool,
}
+#[derive(Clone)]
pub struct DisplaySnapshot {
pub buffer_snapshot: MultiBufferSnapshot,
pub fold_snapshot: fold_map::FoldSnapshot,
@@ -872,7 +878,7 @@ impl DisplaySnapshot {
#[cfg(any(test, feature = "test-support"))]
pub(crate) fn inlay_highlights<Tag: ?Sized + 'static>(
&self,
- ) -> Option<&HashMap<InlayId, (HighlightStyle, InlayHighlight)>> {
+ ) -> Option<&TreeMap<InlayId, (HighlightStyle, InlayHighlight)>> {
let type_id = TypeId::of::<Tag>();
self.inlay_highlights.get(&type_id)
}
@@ -1093,7 +1099,7 @@ pub mod tests {
position,
height,
disposition,
- render: Arc::new(|_| div().into_any()),
+ render: Box::new(|_| div().into_any()),
}
})
.collect::<Vec<_>>();
@@ -37,6 +37,7 @@ pub struct BlockMap {
pub struct BlockMapWriter<'a>(&'a mut BlockMap);
+#[derive(Clone)]
pub struct BlockSnapshot {
wrap_snapshot: WrapSnapshot,
transforms: SumTree<Transform>,
@@ -54,7 +55,7 @@ struct BlockRow(u32);
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
struct WrapRow(u32);
-pub type RenderBlock = Arc<dyn Fn(&mut BlockContext) -> AnyElement>;
+pub type RenderBlock = Box<dyn Send + Fn(&mut BlockContext) -> AnyElement>;
pub struct Block {
id: BlockId,
@@ -65,15 +66,11 @@ pub struct Block {
disposition: BlockDisposition,
}
-#[derive(Clone)]
-pub struct BlockProperties<P>
-where
- P: Clone,
-{
+pub struct BlockProperties<P> {
pub position: P,
pub height: u8,
pub style: BlockStyle,
- pub render: Arc<dyn Fn(&mut BlockContext) -> AnyElement>,
+ pub render: Box<dyn Send + Fn(&mut BlockContext) -> AnyElement>,
pub disposition: BlockDisposition,
}
@@ -1041,21 +1038,21 @@ mod tests {
position: buffer_snapshot.anchor_after(Point::new(1, 0)),
height: 1,
disposition: BlockDisposition::Above,
- render: Arc::new(|_| div().into_any()),
+ render: Box::new(|_| div().into_any()),
},
BlockProperties {
style: BlockStyle::Fixed,
position: buffer_snapshot.anchor_after(Point::new(1, 2)),
height: 2,
disposition: BlockDisposition::Above,
- render: Arc::new(|_| div().into_any()),
+ render: Box::new(|_| div().into_any()),
},
BlockProperties {
style: BlockStyle::Fixed,
position: buffer_snapshot.anchor_after(Point::new(3, 3)),
height: 3,
disposition: BlockDisposition::Below,
- render: Arc::new(|_| div().into_any()),
+ render: Box::new(|_| div().into_any()),
},
]);
@@ -1209,14 +1206,14 @@ mod tests {
style: BlockStyle::Fixed,
position: buffer_snapshot.anchor_after(Point::new(1, 12)),
disposition: BlockDisposition::Above,
- render: Arc::new(|_| div().into_any()),
+ render: Box::new(|_| div().into_any()),
height: 1,
},
BlockProperties {
style: BlockStyle::Fixed,
position: buffer_snapshot.anchor_after(Point::new(1, 1)),
disposition: BlockDisposition::Below,
- render: Arc::new(|_| div().into_any()),
+ render: Box::new(|_| div().into_any()),
height: 1,
},
]);
@@ -1311,7 +1308,7 @@ mod tests {
position,
height,
disposition,
- render: Arc::new(|_| div().into_any()),
+ render: Box::new(|_| div().into_any()),
}
})
.collect::<Vec<_>>();
@@ -1325,7 +1322,14 @@ mod tests {
wrap_map.sync(tab_snapshot, tab_edits, cx)
});
let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
- let block_ids = block_map.insert(block_properties.clone());
+ let block_ids =
+ block_map.insert(block_properties.iter().map(|props| BlockProperties {
+ position: props.position,
+ height: props.height,
+ style: props.style,
+ render: Box::new(|_| div().into_any()),
+ disposition: props.disposition,
+ }));
for (block_id, props) in block_ids.into_iter().zip(block_properties) {
custom_blocks.push((block_id, props));
}
@@ -1695,38 +1695,39 @@ mod tests {
while inlay_indices.len() < inlay_highlight_count {
inlay_indices.insert(rng.gen_range(0..inlays.len()));
}
- let new_highlights = inlay_indices
- .into_iter()
- .filter_map(|i| {
- let (_, inlay) = &inlays[i];
- let inlay_text_len = inlay.text.len();
- match inlay_text_len {
- 0 => None,
- 1 => Some(InlayHighlight {
- inlay: inlay.id,
- inlay_position: inlay.position,
- range: 0..1,
- }),
- n => {
- let inlay_text = inlay.text.to_string();
- let mut highlight_end = rng.gen_range(1..n);
- let mut highlight_start = rng.gen_range(0..highlight_end);
- while !inlay_text.is_char_boundary(highlight_end) {
- highlight_end += 1;
- }
- while !inlay_text.is_char_boundary(highlight_start) {
- highlight_start -= 1;
- }
- Some(InlayHighlight {
+ let new_highlights = TreeMap::from_ordered_entries(
+ inlay_indices
+ .into_iter()
+ .filter_map(|i| {
+ let (_, inlay) = &inlays[i];
+ let inlay_text_len = inlay.text.len();
+ match inlay_text_len {
+ 0 => None,
+ 1 => Some(InlayHighlight {
inlay: inlay.id,
inlay_position: inlay.position,
- range: highlight_start..highlight_end,
- })
+ range: 0..1,
+ }),
+ n => {
+ let inlay_text = inlay.text.to_string();
+ let mut highlight_end = rng.gen_range(1..n);
+ let mut highlight_start = rng.gen_range(0..highlight_end);
+ while !inlay_text.is_char_boundary(highlight_end) {
+ highlight_end += 1;
+ }
+ while !inlay_text.is_char_boundary(highlight_start) {
+ highlight_start -= 1;
+ }
+ Some(InlayHighlight {
+ inlay: inlay.id,
+ inlay_position: inlay.position,
+ range: highlight_start..highlight_end,
+ })
+ }
}
- }
- })
- .map(|highlight| (highlight.inlay, (HighlightStyle::default(), highlight)))
- .collect();
+ })
+ .map(|highlight| (highlight.inlay, (HighlightStyle::default(), highlight))),
+ );
log::info!("highlighting inlay ranges {new_highlights:?}");
inlay_highlights.insert(TypeId::of::<()>(), new_highlights);
}
@@ -64,9 +64,9 @@ use gpui::{
AnyElement, AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds,
ClipboardItem, Context, DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusableView,
FontId, FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext, Model,
- MouseButton, ParentElement, Pixels, Render, SharedString, StrikethroughStyle, Styled,
- StyledText, Subscription, Task, TextStyle, UnderlineStyle, UniformListScrollHandle, View,
- ViewContext, ViewInputHandler, VisualContext, WeakView, WhiteSpace, WindowContext,
+ MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString, Size, StrikethroughStyle,
+ Styled, StyledText, Subscription, Task, TextStyle, UnderlineStyle, UniformListScrollHandle,
+ View, ViewContext, ViewInputHandler, VisualContext, WeakView, WhiteSpace, WindowContext,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HoverState};
@@ -116,6 +116,7 @@ use std::{
time::{Duration, Instant},
};
pub use sum_tree::Bias;
+use sum_tree::TreeMap;
use text::{BufferId, OffsetUtf16, Rope};
use theme::{
observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
@@ -355,7 +356,31 @@ type CompletionId = usize;
// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
-type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<Range<Anchor>>);
+type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
+
+struct ScrollbarMarkerState {
+ scrollbar_size: Size<Pixels>,
+ dirty: bool,
+ markers: Arc<[PaintQuad]>,
+ pending_refresh: Option<Task<Result<()>>>,
+}
+
+impl ScrollbarMarkerState {
+ fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
+ self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
+ }
+}
+
+impl Default for ScrollbarMarkerState {
+ fn default() -> Self {
+ Self {
+ scrollbar_size: Size::default(),
+ dirty: false,
+ markers: Arc::from([]),
+ pending_refresh: None,
+ }
+ }
+}
/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
///
@@ -394,7 +419,8 @@ pub struct Editor {
placeholder_text: Option<Arc<str>>,
highlight_order: usize,
highlighted_rows: HashMap<TypeId, Vec<(usize, Range<Anchor>, Hsla)>>,
- background_highlights: BTreeMap<TypeId, BackgroundHighlight>,
+ background_highlights: TreeMap<TypeId, BackgroundHighlight>,
+ scrollbar_marker_state: ScrollbarMarkerState,
nav_history: Option<ItemNavHistory>,
context_menu: RwLock<Option<ContextMenu>>,
mouse_context_menu: Option<MouseContextMenu>,
@@ -444,6 +470,7 @@ pub struct Editor {
>,
}
+#[derive(Clone)]
pub struct EditorSnapshot {
pub mode: EditorMode,
show_gutter: bool,
@@ -1440,6 +1467,7 @@ impl Editor {
highlight_order: 0,
highlighted_rows: HashMap::default(),
background_highlights: Default::default(),
+ scrollbar_marker_state: ScrollbarMarkerState::default(),
nav_history: None,
context_menu: RwLock::new(None),
mouse_context_menu: None,
@@ -3730,7 +3758,7 @@ impl Editor {
workspace.add_item_to_active_pane(Box::new(editor.clone()), cx);
editor.update(cx, |editor, cx| {
editor.highlight_background::<Self>(
- ranges_to_highlight,
+ &ranges_to_highlight,
|theme| theme.editor_highlighted_line_background,
cx,
);
@@ -3860,12 +3888,12 @@ impl Editor {
}
this.highlight_background::<DocumentHighlightRead>(
- read_ranges,
+ &read_ranges,
|theme| theme.editor_document_highlight_read_background,
cx,
);
this.highlight_background::<DocumentHighlightWrite>(
- write_ranges,
+ &write_ranges,
|theme| theme.editor_document_highlight_write_background,
cx,
);
@@ -7967,7 +7995,7 @@ impl Editor {
});
editor.update(cx, |editor, cx| {
editor.highlight_background::<Self>(
- ranges_to_highlight,
+ &ranges_to_highlight,
|theme| theme.editor_highlighted_line_background,
cx,
);
@@ -8058,15 +8086,15 @@ impl Editor {
editor
});
- let ranges = this
- .clear_background_highlights::<DocumentHighlightWrite>(cx)
- .into_iter()
- .flat_map(|(_, ranges)| ranges.into_iter())
- .chain(
- this.clear_background_highlights::<DocumentHighlightRead>(cx)
- .into_iter()
- .flat_map(|(_, ranges)| ranges.into_iter()),
- )
+ let write_highlights =
+ this.clear_background_highlights::<DocumentHighlightWrite>(cx);
+ let read_highlights =
+ this.clear_background_highlights::<DocumentHighlightRead>(cx);
+ let ranges = write_highlights
+ .iter()
+ .flat_map(|(_, ranges)| ranges.iter())
+ .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
+ .cloned()
.collect();
this.highlight_text::<Rename>(
@@ -8084,7 +8112,7 @@ impl Editor {
style: BlockStyle::Flex,
position: range.start,
height: 1,
- render: Arc::new({
+ render: Box::new({
let rename_editor = rename_editor.clone();
move |cx: &mut BlockContext| {
let mut text_style = cx.editor_style.text.clone();
@@ -9016,13 +9044,13 @@ impl Editor {
pub fn highlight_background<T: 'static>(
&mut self,
- ranges: Vec<Range<Anchor>>,
+ ranges: &[Range<Anchor>],
color_fetcher: fn(&ThemeColors) -> Hsla,
cx: &mut ViewContext<Self>,
) {
let snapshot = self.snapshot(cx);
// this is to try and catch a panic sooner
- for range in &ranges {
+ for range in ranges {
snapshot
.buffer_snapshot
.summary_for_anchor::<usize>(&range.start);
@@ -9032,16 +9060,21 @@ impl Editor {
}
self.background_highlights
- .insert(TypeId::of::<T>(), (color_fetcher, ranges));
+ .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
+ self.scrollbar_marker_state.dirty = true;
cx.notify();
}
pub fn clear_background_highlights<T: 'static>(
&mut self,
- _cx: &mut ViewContext<Self>,
+ cx: &mut ViewContext<Self>,
) -> Option<BackgroundHighlight> {
- let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
- text_highlights
+ let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
+ if !text_highlights.1.is_empty() {
+ self.scrollbar_marker_state.dirty = true;
+ cx.notify();
+ }
+ Some(text_highlights)
}
#[cfg(feature = "test-support")]
@@ -9295,6 +9328,7 @@ impl Editor {
multi_buffer::Event::Edited {
singleton_buffer_edited,
} => {
+ self.scrollbar_marker_state.dirty = true;
self.refresh_active_diagnostics(cx);
self.refresh_code_actions(cx);
if self.has_active_inline_completion(cx) {
@@ -9362,10 +9396,16 @@ impl Editor {
multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
cx.emit(EditorEvent::TitleChanged)
}
- multi_buffer::Event::DiffBaseChanged => cx.emit(EditorEvent::DiffBaseChanged),
+ multi_buffer::Event::DiffBaseChanged => {
+ self.scrollbar_marker_state.dirty = true;
+ cx.emit(EditorEvent::DiffBaseChanged);
+ cx.notify();
+ }
multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
multi_buffer::Event::DiagnosticsUpdated => {
self.refresh_active_diagnostics(cx);
+ self.scrollbar_marker_state.dirty = true;
+ cx.notify();
}
_ => {}
};
@@ -10526,7 +10566,7 @@ impl InvalidationRegion for SnippetState {
pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> RenderBlock {
let (text_without_backticks, code_ranges) = highlight_diagnostic_message(&diagnostic);
- Arc::new(move |cx: &mut BlockContext| {
+ Box::new(move |cx: &mut BlockContext| {
let group_id: SharedString = cx.block_id.to_string().into();
let mut text_style = cx.text_style().clone();
@@ -3317,7 +3317,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
position: snapshot.anchor_after(Point::new(2, 0)),
disposition: BlockDisposition::Below,
height: 1,
- render: Arc::new(|_| div().into_any()),
+ render: Box::new(|_| div().into_any()),
}],
Some(Autoscroll::fit()),
cx,
@@ -7263,7 +7263,7 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) {
|range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
editor.highlight_background::<Type1>(
- vec![
+ &[
anchor_range(Point::new(2, 1)..Point::new(2, 3)),
anchor_range(Point::new(4, 2)..Point::new(4, 4)),
anchor_range(Point::new(6, 3)..Point::new(6, 5)),
@@ -7273,7 +7273,7 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) {
cx,
);
editor.highlight_background::<Type2>(
- vec![
+ &[
anchor_range(Point::new(3, 2)..Point::new(3, 5)),
anchor_range(Point::new(5, 3)..Point::new(5, 6)),
anchor_range(Point::new(7, 4)..Point::new(7, 7)),
@@ -24,7 +24,7 @@ use gpui::{
transparent_black, Action, AnchorCorner, AnyElement, AnyView, AvailableSpace, Bounds,
ClipboardItem, ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element,
ElementContext, ElementInputHandler, Entity, Hitbox, Hsla, InteractiveElement, IntoElement,
- ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
+ ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size, Stateful,
StatefulInteractiveElement, Style, Styled, TextRun, TextStyle, TextStyleRefinement, View,
ViewContext, WindowContext,
@@ -2370,150 +2370,15 @@ impl EditorElement {
},
cx.theme().colors().scrollbar_track_border,
));
- let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
- let is_singleton = self.editor.read(cx).is_singleton(cx);
- let left = scrollbar_layout.hitbox.left();
- let right = scrollbar_layout.hitbox.right();
- let column_width =
- px(((right - left - ScrollbarLayout::BORDER_WIDTH).0 / 3.0).floor());
- if is_singleton && scrollbar_settings.selections {
- let start_anchor = Anchor::min();
- let end_anchor = Anchor::max();
- let background_ranges = self
- .editor
- .read(cx)
- .background_highlight_row_ranges::<BufferSearchHighlights>(
- start_anchor..end_anchor,
- &layout.position_map.snapshot,
- 50000,
- );
- let left_x = left + ScrollbarLayout::BORDER_WIDTH + column_width;
- let right_x = left_x + column_width;
- for range in background_ranges {
- let (start_y, end_y) =
- scrollbar_layout.ys_for_marker(range.start().row(), range.end().row());
- let bounds =
- Bounds::from_corners(point(left_x, start_y), point(right_x, end_y));
- cx.paint_quad(quad(
- bounds,
- Corners::default(),
- cx.theme().status().info,
- Edges::default(),
- cx.theme().colors().scrollbar_thumb_border,
- ));
- }
- }
- if is_singleton && scrollbar_settings.symbols_selections {
- let selection_ranges = self.editor.read(cx).background_highlights_in_range(
- Anchor::min()..Anchor::max(),
- &layout.position_map.snapshot,
- cx.theme().colors(),
- );
- let left_x = left + ScrollbarLayout::BORDER_WIDTH + column_width;
- let right_x = left_x + column_width;
- for hunk in selection_ranges {
- let start_display = Point::new(hunk.0.start.row(), 0)
- .to_display_point(&layout.position_map.snapshot.display_snapshot);
- let end_display = Point::new(hunk.0.end.row(), 0)
- .to_display_point(&layout.position_map.snapshot.display_snapshot);
- let (start_y, end_y) =
- scrollbar_layout.ys_for_marker(start_display.row(), end_display.row());
- let bounds =
- Bounds::from_corners(point(left_x, start_y), point(right_x, end_y));
- cx.paint_quad(quad(
- bounds,
- Corners::default(),
- cx.theme().status().info,
- Edges::default(),
- cx.theme().colors().scrollbar_thumb_border,
- ));
- }
- }
+ // Refresh scrollbar markers in the background. Below, we paint whatever markers have already been computed.
+ self.refresh_scrollbar_markers(layout, scrollbar_layout, cx);
- if is_singleton && scrollbar_settings.git_diff {
- let left_x = left + ScrollbarLayout::BORDER_WIDTH;
- let right_x = left_x + column_width;
- for hunk in layout
- .position_map
- .snapshot
- .buffer_snapshot
- .git_diff_hunks_in_range(0..layout.max_row)
- {
- let start_display_row = Point::new(hunk.associated_range.start, 0)
- .to_display_point(&layout.position_map.snapshot.display_snapshot)
- .row();
- let mut end_display_row = Point::new(hunk.associated_range.end, 0)
- .to_display_point(&layout.position_map.snapshot.display_snapshot)
- .row();
- if end_display_row != start_display_row {
- end_display_row -= 1;
- }
- let (start_y, end_y) =
- scrollbar_layout.ys_for_marker(start_display_row, end_display_row);
- let bounds =
- Bounds::from_corners(point(left_x, start_y), point(right_x, end_y));
- let color = match hunk.status() {
- DiffHunkStatus::Added => cx.theme().status().created,
- DiffHunkStatus::Modified => cx.theme().status().modified,
- DiffHunkStatus::Removed => cx.theme().status().deleted,
- };
- cx.paint_quad(quad(
- bounds,
- Corners::default(),
- color,
- Edges::default(),
- cx.theme().colors().scrollbar_thumb_border,
- ));
- }
- }
-
- if is_singleton && scrollbar_settings.diagnostics {
- let max_point = layout
- .position_map
- .snapshot
- .display_snapshot
- .buffer_snapshot
- .max_point();
-
- let diagnostics = layout
- .position_map
- .snapshot
- .buffer_snapshot
- .diagnostics_in_range::<_, Point>(Point::zero()..max_point, false)
- // We want to sort by severity, in order to paint the most severe diagnostics last.
- .sorted_by_key(|diagnostic| {
- std::cmp::Reverse(diagnostic.diagnostic.severity)
- });
-
- let left_x = left + ScrollbarLayout::BORDER_WIDTH + 2.0 * column_width;
- for diagnostic in diagnostics {
- let start_display = diagnostic
- .range
- .start
- .to_display_point(&layout.position_map.snapshot.display_snapshot);
- let end_display = diagnostic
- .range
- .end
- .to_display_point(&layout.position_map.snapshot.display_snapshot);
- let (start_y, end_y) =
- scrollbar_layout.ys_for_marker(start_display.row(), end_display.row());
- let bounds =
- Bounds::from_corners(point(left_x, start_y), point(right, end_y));
- let color = match diagnostic.diagnostic.severity {
- DiagnosticSeverity::ERROR => cx.theme().status().error,
- DiagnosticSeverity::WARNING => cx.theme().status().warning,
- DiagnosticSeverity::INFORMATION => cx.theme().status().info,
- _ => cx.theme().status().hint,
- };
- cx.paint_quad(quad(
- bounds,
- Corners::default(),
- color,
- Edges::default(),
- cx.theme().colors().scrollbar_thumb_border,
- ));
- }
+ let markers = self.editor.read(cx).scrollbar_marker_state.markers.clone();
+ for marker in markers.iter() {
+ let mut marker = marker.clone();
+ marker.bounds.origin += scrollbar_layout.hitbox.origin;
+ cx.paint_quad(marker);
}
cx.paint_quad(quad(
@@ -2619,6 +2484,156 @@ impl EditorElement {
}
}
+ fn refresh_scrollbar_markers(
+ &self,
+ layout: &EditorLayout,
+ scrollbar_layout: &ScrollbarLayout,
+ cx: &mut ElementContext,
+ ) {
+ self.editor.update(cx, |editor, cx| {
+ if !editor.is_singleton(cx)
+ || !editor
+ .scrollbar_marker_state
+ .should_refresh(scrollbar_layout.hitbox.size)
+ {
+ return;
+ }
+
+ let scrollbar_layout = scrollbar_layout.clone();
+ let background_highlights = editor.background_highlights.clone();
+ let snapshot = layout.position_map.snapshot.clone();
+ let theme = cx.theme().clone();
+ let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
+ let max_row = layout.max_row;
+
+ editor.scrollbar_marker_state.dirty = false;
+ editor.scrollbar_marker_state.pending_refresh =
+ Some(cx.spawn(|editor, mut cx| async move {
+ let scrollbar_size = scrollbar_layout.hitbox.size;
+ let scrollbar_markers = cx
+ .background_executor()
+ .spawn(async move {
+ let mut marker_quads = Vec::new();
+
+ if scrollbar_settings.git_diff {
+ let marker_row_ranges = snapshot
+ .buffer_snapshot
+ .git_diff_hunks_in_range(0..max_row)
+ .map(|hunk| {
+ let start_display_row =
+ Point::new(hunk.associated_range.start, 0)
+ .to_display_point(&snapshot.display_snapshot)
+ .row();
+ let mut end_display_row =
+ Point::new(hunk.associated_range.end, 0)
+ .to_display_point(&snapshot.display_snapshot)
+ .row();
+ if end_display_row != start_display_row {
+ end_display_row -= 1;
+ }
+ let color = match hunk.status() {
+ DiffHunkStatus::Added => theme.status().created,
+ DiffHunkStatus::Modified => theme.status().modified,
+ DiffHunkStatus::Removed => theme.status().deleted,
+ };
+ ColoredRange {
+ start: start_display_row,
+ end: end_display_row,
+ color,
+ }
+ });
+
+ marker_quads.extend(
+ scrollbar_layout.marker_quads_for_ranges(marker_row_ranges, 0),
+ );
+ }
+
+ for (background_highlight_id, (_, background_ranges)) in
+ background_highlights.iter()
+ {
+ if (*background_highlight_id
+ == TypeId::of::<BufferSearchHighlights>()
+ && scrollbar_settings.selections)
+ || scrollbar_settings.symbols_selections
+ {
+ let marker_row_ranges =
+ background_ranges.into_iter().map(|range| {
+ let display_start = range
+ .start
+ .to_display_point(&snapshot.display_snapshot);
+ let display_end = range
+ .end
+ .to_display_point(&snapshot.display_snapshot);
+ ColoredRange {
+ start: display_start.row(),
+ end: display_end.row(),
+ color: theme.status().info,
+ }
+ });
+ marker_quads.extend(
+ scrollbar_layout
+ .marker_quads_for_ranges(marker_row_ranges, 1),
+ );
+ }
+ }
+
+ if scrollbar_settings.diagnostics {
+ let max_point =
+ snapshot.display_snapshot.buffer_snapshot.max_point();
+
+ let diagnostics = snapshot
+ .buffer_snapshot
+ .diagnostics_in_range::<_, Point>(
+ Point::zero()..max_point,
+ false,
+ )
+ // We want to sort by severity, in order to paint the most severe diagnostics last.
+ .sorted_by_key(|diagnostic| {
+ std::cmp::Reverse(diagnostic.diagnostic.severity)
+ });
+
+ let marker_row_ranges = diagnostics.into_iter().map(|diagnostic| {
+ let start_display = diagnostic
+ .range
+ .start
+ .to_display_point(&snapshot.display_snapshot);
+ let end_display = diagnostic
+ .range
+ .end
+ .to_display_point(&snapshot.display_snapshot);
+ let color = match diagnostic.diagnostic.severity {
+ DiagnosticSeverity::ERROR => theme.status().error,
+ DiagnosticSeverity::WARNING => theme.status().warning,
+ DiagnosticSeverity::INFORMATION => theme.status().info,
+ _ => theme.status().hint,
+ };
+ ColoredRange {
+ start: start_display.row(),
+ end: end_display.row(),
+ color,
+ }
+ });
+ marker_quads.extend(
+ scrollbar_layout.marker_quads_for_ranges(marker_row_ranges, 2),
+ );
+ }
+
+ Arc::from(marker_quads)
+ })
+ .await;
+
+ editor.update(&mut cx, |editor, cx| {
+ editor.scrollbar_marker_state.markers = scrollbar_markers;
+ editor.scrollbar_marker_state.scrollbar_size = scrollbar_size;
+ editor.scrollbar_marker_state.pending_refresh = None;
+ cx.notify();
+ })?;
+
+ Ok(())
+ }));
+ });
+ }
+
#[allow(clippy::too_many_arguments)]
fn paint_highlighted_range(
&self,
@@ -3811,6 +3826,13 @@ impl EditorLayout {
}
}
+struct ColoredRange<T> {
+ start: T,
+ end: T,
+ color: Hsla,
+}
+
+#[derive(Clone)]
struct ScrollbarLayout {
hitbox: Hitbox,
visible_row_range: Range<f32>,
@@ -3838,13 +3860,60 @@ impl ScrollbarLayout {
self.hitbox.top() + self.first_row_y_offset + row * self.row_height
}
- fn ys_for_marker(&self, start_row: u32, end_row: u32) -> (Pixels, Pixels) {
- let start_y = self.y_for_row(start_row as f32);
- let mut end_y = self.y_for_row((end_row + 1) as f32);
- if end_y - start_y < Self::MIN_MARKER_HEIGHT {
- end_y = start_y + Self::MIN_MARKER_HEIGHT;
+ fn marker_quads_for_ranges(
+ &self,
+ row_ranges: impl IntoIterator<Item = ColoredRange<u32>>,
+ column: usize,
+ ) -> Vec<PaintQuad> {
+ let column_width =
+ px(((self.hitbox.size.width - ScrollbarLayout::BORDER_WIDTH).0 / 3.0).floor());
+
+ let left_x = ScrollbarLayout::BORDER_WIDTH + (column as f32 * column_width);
+ let right_x = left_x + column_width;
+
+ let mut background_pixel_ranges = row_ranges
+ .into_iter()
+ .map(|range| {
+ let start_y = self.first_row_y_offset + range.start as f32 * self.row_height;
+ let mut end_y = self.first_row_y_offset + (range.end + 1) as f32 * self.row_height;
+ if end_y - start_y < Self::MIN_MARKER_HEIGHT {
+ end_y = start_y + Self::MIN_MARKER_HEIGHT;
+ }
+ ColoredRange {
+ start: start_y,
+ end: end_y,
+ color: range.color,
+ }
+ })
+ .peekable();
+
+ let mut quads = Vec::new();
+ while let Some(mut pixel_range) = background_pixel_ranges.next() {
+ while let Some(next_pixel_range) = background_pixel_ranges.peek() {
+ if pixel_range.end >= next_pixel_range.start
+ && pixel_range.color == next_pixel_range.color
+ {
+ pixel_range.end = next_pixel_range.end;
+ background_pixel_ranges.next();
+ } else {
+ break;
+ }
+ }
+
+ let bounds = Bounds::from_corners(
+ point(left_x, pixel_range.start),
+ point(right_x, pixel_range.end),
+ );
+ quads.push(quad(
+ bounds,
+ Corners::default(),
+ pixel_range.color,
+ Edges::default(),
+ Hsla::transparent_black(),
+ ));
}
- (start_y, end_y)
+
+ quads
}
}
@@ -4241,7 +4310,7 @@ mod tests {
use gpui::TestAppContext;
use language::language_settings;
use log::info;
- use std::{num::NonZeroU32, sync::Arc};
+ use std::num::NonZeroU32;
use util::test::sample_text;
#[gpui::test]
@@ -4473,7 +4542,7 @@ mod tests {
disposition: BlockDisposition::Above,
height: 3,
position: Anchor::min(),
- render: Arc::new(|_| div().into_any()),
+ render: Box::new(|_| div().into_any()),
}],
None,
cx,
@@ -20,7 +20,7 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
.innermost_enclosing_bracket_ranges(head..head, None)
{
editor.highlight_background::<MatchingBracketHighlight>(
- vec![
+ &[
opening_range.to_anchors(&snapshot.buffer_snapshot),
closing_range.to_anchors(&snapshot.buffer_snapshot),
],
@@ -342,7 +342,7 @@ fn show_hover(
} else {
// Highlight the selected symbol using a background highlight
editor.highlight_background::<HoverState>(
- hover_highlights,
+ &hover_highlights,
|theme| theme.element_hover, // todo update theme
cx,
);
@@ -976,7 +976,7 @@ impl SearchableItem for Editor {
self.clear_background_highlights::<BufferSearchHighlights>(cx);
}
- fn update_matches(&mut self, matches: Vec<Range<Anchor>>, cx: &mut ViewContext<Self>) {
+ fn update_matches(&mut self, matches: &[Range<Anchor>], cx: &mut ViewContext<Self>) {
self.highlight_background::<BufferSearchHighlights>(
matches,
|theme| theme.search_match_background,
@@ -1013,7 +1013,7 @@ impl SearchableItem for Editor {
fn activate_match(
&mut self,
index: usize,
- matches: Vec<Range<Anchor>>,
+ matches: &[Range<Anchor>],
cx: &mut ViewContext<Self>,
) {
self.unfold_ranges([matches[index].clone()], false, true, cx);
@@ -1023,10 +1023,10 @@ impl SearchableItem for Editor {
})
}
- fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
- self.unfold_ranges(matches.clone(), false, false, cx);
+ fn select_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>) {
+ self.unfold_ranges(matches.to_vec(), false, false, cx);
let mut ranges = Vec::new();
- for m in &matches {
+ for m in matches {
ranges.push(self.range_for_match(&m))
}
self.change_selections(None, cx, |s| s.select_ranges(ranges));
@@ -1055,7 +1055,7 @@ impl SearchableItem for Editor {
}
fn match_index_for_direction(
&mut self,
- matches: &Vec<Range<Anchor>>,
+ matches: &[Range<Anchor>],
current_index: usize,
direction: Direction,
count: usize,
@@ -1147,11 +1147,11 @@ impl SearchableItem for Editor {
fn active_match_index(
&mut self,
- matches: Vec<Range<Anchor>>,
+ matches: &[Range<Anchor>],
cx: &mut ViewContext<Self>,
) -> Option<usize> {
active_match_index(
- &matches,
+ matches,
&self.selections.newest_anchor().head(),
&self.buffer().read(cx).snapshot(cx),
)
@@ -345,7 +345,7 @@ impl EditorTestContext {
.background_highlights
.get(&TypeId::of::<Tag>())
.map(|h| h.1.clone())
- .unwrap_or_default()
+ .unwrap_or_else(|| Arc::from([]))
.into_iter()
.map(|range| range.to_offset(&snapshot.buffer_snapshot))
.collect()
@@ -2853,11 +2853,16 @@ impl From<(&'static str, u64)> for ElementId {
/// Passed as an argument [`ElementContext::paint_quad`].
#[derive(Clone)]
pub struct PaintQuad {
- bounds: Bounds<Pixels>,
- corner_radii: Corners<Pixels>,
- background: Hsla,
- border_widths: Edges<Pixels>,
- border_color: Hsla,
+ /// The bounds of the quad within the window.
+ pub bounds: Bounds<Pixels>,
+ /// The radii of the quad's corners.
+ pub corner_radii: Corners<Pixels>,
+ /// The background color of the quad.
+ pub background: Hsla,
+ /// The widths of the quad's borders.
+ pub border_widths: Edges<Pixels>,
+ /// The color of the quad's borders.
+ pub border_color: Hsla,
}
impl PaintQuad {
@@ -654,7 +654,7 @@ impl SearchableItem for LspLogView {
self.editor.update(cx, |e, cx| e.clear_matches(cx))
}
- fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
+ fn update_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>) {
self.editor
.update(cx, |e, cx| e.update_matches(matches, cx))
}
@@ -666,14 +666,14 @@ impl SearchableItem for LspLogView {
fn activate_match(
&mut self,
index: usize,
- matches: Vec<Self::Match>,
+ matches: &[Self::Match],
cx: &mut ViewContext<Self>,
) {
self.editor
.update(cx, |e, cx| e.activate_match(index, matches, cx))
}
- fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
+ fn select_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>) {
self.editor
.update(cx, |e, cx| e.select_matches(matches, cx))
}
@@ -700,7 +700,7 @@ impl SearchableItem for LspLogView {
}
fn active_match_index(
&mut self,
- matches: Vec<Self::Match>,
+ matches: &[Self::Match],
cx: &mut ViewContext<Self>,
) -> Option<usize> {
self.editor
@@ -337,7 +337,7 @@ impl Render for SyntaxTreeView {
tree_view.update_editor_with_range_for_descendant_ix(descendant_ix, cx, |editor, range, cx| {
editor.clear_background_highlights::<Self>(cx);
editor.highlight_background::<Self>(
- vec![range],
+ &[range],
|theme| theme.editor_document_highlight_write_background,
cx,
);
@@ -14,6 +14,7 @@ doctest = false
[dependencies]
anyhow.workspace = true
+any_vec.workspace = true
bitflags.workspace = true
collections.workspace = true
editor.workspace = true
@@ -7,6 +7,7 @@ use crate::{
ReplaceAll, ReplaceNext, SearchOptions, SelectAllMatches, SelectNextMatch, SelectPrevMatch,
ToggleCaseSensitive, ToggleReplace, ToggleWholeWord,
};
+use any_vec::AnyVec;
use collections::HashMap;
use editor::{
actions::{Tab, TabPrev},
@@ -25,7 +26,7 @@ use project::{
};
use serde::Deserialize;
use settings::Settings;
-use std::{any::Any, sync::Arc};
+use std::sync::Arc;
use theme::ThemeSettings;
use ui::{h_flex, prelude::*, IconButton, IconName, ToggleButton, Tooltip};
@@ -70,8 +71,7 @@ pub struct BufferSearchBar {
active_match_index: Option<usize>,
active_searchable_item_subscription: Option<Subscription>,
active_search: Option<Arc<SearchQuery>>,
- searchable_items_with_matches:
- HashMap<Box<dyn WeakSearchableItemHandle>, Vec<Box<dyn Any + Send>>>,
+ searchable_items_with_matches: HashMap<Box<dyn WeakSearchableItemHandle>, AnyVec<dyn Send>>,
pending_search: Option<Task<()>>,
search_options: SearchOptions,
default_options: SearchOptions,
@@ -191,7 +191,7 @@ impl Render for BufferSearchBar {
let matches_count = self
.searchable_items_with_matches
.get(&searchable_item.downgrade())
- .map(Vec::len)
+ .map(AnyVec::len)
.unwrap_or(0);
if let Some(match_ix) = self.active_match_index {
Some(format!("{}/{}", match_ix + 1, matches_count))
@@ -1067,7 +1067,7 @@ impl BufferSearchBar {
.as_ref()
.clone()
.with_replacement(self.replacement(cx));
- searchable_item.replace(&matches[active_index], &query, cx);
+ searchable_item.replace(matches.at(active_index), &query, cx);
self.select_next_match(&SelectNextMatch, cx);
}
should_propagate = false;
@@ -585,43 +585,54 @@ impl ProjectSearchView {
cx.notify();
}
fn replace_next(&mut self, _: &ReplaceNext, cx: &mut ViewContext<Self>) {
- let model = self.model.read(cx);
- if let Some(query) = model.active_query.as_ref() {
- if model.match_ranges.is_empty() {
- return;
- }
- if let Some(active_index) = self.active_match_index {
- let query = query.clone().with_replacement(self.replacement(cx));
- self.results_editor.replace(
- &(Box::new(model.match_ranges[active_index].clone()) as _),
- &query,
- cx,
- );
- self.select_match(Direction::Next, cx)
- }
+ if self.model.read(cx).match_ranges.is_empty() {
+ return;
+ }
+ let Some(active_index) = self.active_match_index else {
+ return;
+ };
+
+ let query = self.model.read(cx).active_query.clone();
+ if let Some(query) = query {
+ let query = query.with_replacement(self.replacement(cx));
+
+ // TODO: Do we need the clone here?
+ let mat = self.model.read(cx).match_ranges[active_index].clone();
+ self.results_editor.update(cx, |editor, cx| {
+ editor.replace(&mat, &query, cx);
+ });
+ self.select_match(Direction::Next, cx)
}
}
pub fn replacement(&self, cx: &AppContext) -> String {
self.replacement_editor.read(cx).text(cx)
}
fn replace_all(&mut self, _: &ReplaceAll, cx: &mut ViewContext<Self>) {
- let model = self.model.read(cx);
- if let Some(query) = model.active_query.as_ref() {
- if model.match_ranges.is_empty() {
- return;
- }
- if self.active_match_index.is_some() {
- let query = query.clone().with_replacement(self.replacement(cx));
- let matches = model
- .match_ranges
- .iter()
- .map(|item| Box::new(item.clone()) as _)
- .collect::<Vec<_>>();
- for item in matches {
- self.results_editor.replace(&item, &query, cx);
- }
- }
+ if self.active_match_index.is_none() {
+ return;
+ }
+
+ let Some(query) = self.model.read(cx).active_query.as_ref() else {
+ return;
+ };
+ let query = query.clone().with_replacement(self.replacement(cx));
+
+ let match_ranges = self
+ .model
+ .update(cx, |model, _| mem::take(&mut model.match_ranges));
+ if match_ranges.is_empty() {
+ return;
}
+
+ self.results_editor.update(cx, |editor, cx| {
+ for item in &match_ranges {
+ editor.replace(item, &query, cx);
+ }
+ });
+
+ self.model.update(cx, |model, _cx| {
+ model.match_ranges = match_ranges;
+ });
}
fn new(
@@ -1060,7 +1071,7 @@ impl ProjectSearchView {
editor.scroll(Point::default(), Some(Axis::Vertical), cx);
}
editor.highlight_background::<Self>(
- match_ranges,
+ &match_ranges,
|theme| theme.search_match_background,
cx,
);
@@ -5,7 +5,7 @@ use crate::{Bias, Dimension, Edit, Item, KeyedItem, SeekTarget, SumTree, Summary
#[derive(Clone, PartialEq, Eq)]
pub struct TreeMap<K, V>(SumTree<MapEntry<K, V>>)
where
- K: Clone + Debug + Default + Ord,
+ K: Clone + Debug + Ord,
V: Clone + Debug;
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -14,18 +14,30 @@ pub struct MapEntry<K, V> {
value: V,
}
-#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
-pub struct MapKey<K>(K);
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct MapKey<K>(Option<K>);
-#[derive(Clone, Debug, Default)]
+impl<K> Default for MapKey<K> {
+ fn default() -> Self {
+ Self(None)
+ }
+}
+
+#[derive(Clone, Debug)]
pub struct MapKeyRef<'a, K>(Option<&'a K>);
+impl<'a, K> Default for MapKeyRef<'a, K> {
+ fn default() -> Self {
+ Self(None)
+ }
+}
+
#[derive(Clone)]
pub struct TreeSet<K>(TreeMap<K, ()>)
where
- K: Clone + Debug + Default + Ord;
+ K: Clone + Debug + Ord;
-impl<K: Clone + Debug + Default + Ord, V: Clone + Debug> TreeMap<K, V> {
+impl<K: Clone + Debug + Ord, V: Clone + Debug> TreeMap<K, V> {
pub fn from_ordered_entries(entries: impl IntoIterator<Item = (K, V)>) -> Self {
let tree = SumTree::from_iter(
entries
@@ -44,7 +56,7 @@ impl<K: Clone + Debug + Default + Ord, V: Clone + Debug> TreeMap<K, V> {
let mut cursor = self.0.cursor::<MapKeyRef<'_, K>>();
cursor.seek(&MapKeyRef(Some(key)), Bias::Left, &());
if let Some(item) = cursor.item() {
- if *key == item.key().0 {
+ if Some(key) == item.key().0.as_ref() {
Some(&item.value)
} else {
None
@@ -162,7 +174,7 @@ impl<K: Clone + Debug + Default + Ord, V: Clone + Debug> TreeMap<K, V> {
impl<K: Debug, V: Debug> Debug for TreeMap<K, V>
where
- K: Clone + Debug + Default + Ord,
+ K: Clone + Debug + Ord,
V: Clone + Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -173,8 +185,8 @@ where
#[derive(Debug)]
struct MapSeekTargetAdaptor<'a, T>(&'a T);
-impl<'a, K: Debug + Clone + Default + Ord, T: MapSeekTarget<K>>
- SeekTarget<'a, MapKey<K>, MapKeyRef<'a, K>> for MapSeekTargetAdaptor<'_, T>
+impl<'a, K: Debug + Clone + Ord, T: MapSeekTarget<K>> SeekTarget<'a, MapKey<K>, MapKeyRef<'a, K>>
+ for MapSeekTargetAdaptor<'_, T>
{
fn cmp(&self, cursor_location: &MapKeyRef<K>, _: &()) -> Ordering {
if let Some(key) = &cursor_location.0 {
@@ -197,7 +209,7 @@ impl<K: Debug + Ord> MapSeekTarget<K> for K {
impl<K, V> Default for TreeMap<K, V>
where
- K: Clone + Debug + Default + Ord,
+ K: Clone + Debug + Ord,
V: Clone + Debug,
{
fn default() -> Self {
@@ -207,7 +219,7 @@ where
impl<K, V> Item for MapEntry<K, V>
where
- K: Clone + Debug + Default + Ord,
+ K: Clone + Debug + Ord,
V: Clone,
{
type Summary = MapKey<K>;
@@ -219,19 +231,19 @@ where
impl<K, V> KeyedItem for MapEntry<K, V>
where
- K: Clone + Debug + Default + Ord,
+ K: Clone + Debug + Ord,
V: Clone,
{
type Key = MapKey<K>;
fn key(&self) -> Self::Key {
- MapKey(self.key.clone())
+ MapKey(Some(self.key.clone()))
}
}
impl<K> Summary for MapKey<K>
where
- K: Clone + Debug + Default,
+ K: Clone + Debug,
{
type Context = ();
@@ -242,16 +254,16 @@ where
impl<'a, K> Dimension<'a, MapKey<K>> for MapKeyRef<'a, K>
where
- K: Clone + Debug + Default + Ord,
+ K: Clone + Debug + Ord,
{
fn add_summary(&mut self, summary: &'a MapKey<K>, _: &()) {
- self.0 = Some(&summary.0)
+ self.0 = summary.0.as_ref();
}
}
impl<'a, K> SeekTarget<'a, MapKey<K>, MapKeyRef<'a, K>> for MapKeyRef<'_, K>
where
- K: Clone + Debug + Default + Ord,
+ K: Clone + Debug + Ord,
{
fn cmp(&self, cursor_location: &MapKeyRef<K>, _: &()) -> Ordering {
Ord::cmp(&self.0, &cursor_location.0)
@@ -260,7 +272,7 @@ where
impl<K> Default for TreeSet<K>
where
- K: Clone + Debug + Default + Ord,
+ K: Clone + Debug + Ord,
{
fn default() -> Self {
Self(Default::default())
@@ -269,7 +281,7 @@ where
impl<K> TreeSet<K>
where
- K: Clone + Debug + Default + Ord,
+ K: Clone + Debug + Ord,
{
pub fn from_ordered_entries(entries: impl IntoIterator<Item = K>) -> Self {
Self(TreeMap::from_ordered_entries(
@@ -952,7 +952,7 @@ impl Terminal {
}
}
- pub fn select_matches(&mut self, matches: Vec<RangeInclusive<AlacPoint>>) {
+ pub fn select_matches(&mut self, matches: &[RangeInclusive<AlacPoint>]) {
let matches_to_select = self
.matches
.iter()
@@ -943,8 +943,9 @@ impl SearchableItem for TerminalView {
}
/// Store matches returned from find_matches somewhere for rendering
- fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
- self.terminal().update(cx, |term, _| term.matches = matches)
+ fn update_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>) {
+ self.terminal()
+ .update(cx, |term, _| term.matches = matches.to_vec())
}
/// Returns the selection content to pre-load into this search
@@ -958,14 +959,14 @@ impl SearchableItem for TerminalView {
}
/// Focus match at given index into the Vec of matches
- fn activate_match(&mut self, index: usize, _: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
+ fn activate_match(&mut self, index: usize, _: &[Self::Match], cx: &mut ViewContext<Self>) {
self.terminal()
.update(cx, |term, _| term.activate_match(index));
cx.notify();
}
/// Add selections for all matches given.
- fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
+ fn select_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>) {
self.terminal()
.update(cx, |term, _| term.select_matches(matches));
cx.notify();
@@ -1003,7 +1004,7 @@ impl SearchableItem for TerminalView {
/// Reports back to the search toolbar what the active match should be (the selection)
fn active_match_index(
&mut self,
- matches: Vec<Self::Match>,
+ matches: &[Self::Match],
cx: &mut ViewContext<Self>,
) -> Option<usize> {
// Selection head might have a value if there's a selection that isn't
@@ -103,7 +103,7 @@ fn copy_selections_content_internal(
}
editor.highlight_background::<HighlightOnYank>(
- ranges_to_highlight,
+ &ranges_to_highlight,
|colors| colors.editor_document_highlight_read_background,
cx,
);
@@ -25,6 +25,7 @@ test-support = [
[dependencies]
anyhow.workspace = true
+any_vec.workspace = true
async-recursion.workspace = true
bincode = "1.2.1"
call.workspace = true
@@ -1,5 +1,6 @@
use std::{any::Any, sync::Arc};
+use any_vec::AnyVec;
use gpui::{
AnyView, AnyWeakView, AppContext, EventEmitter, Subscription, Task, View, ViewContext,
WeakView, WindowContext,
@@ -45,19 +46,14 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
}
fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
- fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
+ fn update_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>);
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
- fn activate_match(
- &mut self,
- index: usize,
- matches: Vec<Self::Match>,
- cx: &mut ViewContext<Self>,
- );
- fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
+ fn activate_match(&mut self, index: usize, matches: &[Self::Match], cx: &mut ViewContext<Self>);
+ fn select_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>);
fn replace(&mut self, _: &Self::Match, _: &SearchQuery, _: &mut ViewContext<Self>);
fn match_index_for_direction(
&mut self,
- matches: &Vec<Self::Match>,
+ matches: &[Self::Match],
current_index: usize,
direction: Direction,
count: usize,
@@ -82,7 +78,7 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
) -> Task<Vec<Self::Match>>;
fn active_match_index(
&mut self,
- matches: Vec<Self::Match>,
+ matches: &[Self::Match],
cx: &mut ViewContext<Self>,
) -> Option<usize>;
}
@@ -97,19 +93,19 @@ pub trait SearchableItemHandle: ItemHandle {
handler: Box<dyn Fn(&SearchEvent, &mut WindowContext) + Send>,
) -> Subscription;
fn clear_matches(&self, cx: &mut WindowContext);
- fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
+ fn update_matches(&self, matches: &AnyVec<dyn Send>, cx: &mut WindowContext);
fn query_suggestion(&self, cx: &mut WindowContext) -> String;
- fn activate_match(
+ fn activate_match(&self, index: usize, matches: &AnyVec<dyn Send>, cx: &mut WindowContext);
+ fn select_matches(&self, matches: &AnyVec<dyn Send>, cx: &mut WindowContext);
+ fn replace(
&self,
- index: usize,
- matches: &Vec<Box<dyn Any + Send>>,
- cx: &mut WindowContext,
+ _: any_vec::element::ElementRef<'_, dyn Send>,
+ _: &SearchQuery,
+ _: &mut WindowContext,
);
- fn select_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
- fn replace(&self, _: &Box<dyn Any + Send>, _: &SearchQuery, _: &mut WindowContext);
fn match_index_for_direction(
&self,
- matches: &Vec<Box<dyn Any + Send>>,
+ matches: &AnyVec<dyn Send>,
current_index: usize,
direction: Direction,
count: usize,
@@ -119,10 +115,10 @@ pub trait SearchableItemHandle: ItemHandle {
&self,
query: Arc<SearchQuery>,
cx: &mut WindowContext,
- ) -> Task<Vec<Box<dyn Any + Send>>>;
+ ) -> Task<AnyVec<dyn Send>>;
fn active_match_index(
&self,
- matches: &Vec<Box<dyn Any + Send>>,
+ matches: &AnyVec<dyn Send>,
cx: &mut WindowContext,
) -> Option<usize>;
}
@@ -151,80 +147,78 @@ impl<T: SearchableItem> SearchableItemHandle for View<T> {
fn clear_matches(&self, cx: &mut WindowContext) {
self.update(cx, |this, cx| this.clear_matches(cx));
}
- fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext) {
- let matches = downcast_matches(matches);
- self.update(cx, |this, cx| this.update_matches(matches, cx));
+ fn update_matches(&self, matches: &AnyVec<dyn Send>, cx: &mut WindowContext) {
+ let matches = matches.downcast_ref().unwrap();
+ self.update(cx, |this, cx| this.update_matches(matches.as_slice(), cx));
}
fn query_suggestion(&self, cx: &mut WindowContext) -> String {
self.update(cx, |this, cx| this.query_suggestion(cx))
}
- fn activate_match(
- &self,
- index: usize,
- matches: &Vec<Box<dyn Any + Send>>,
- cx: &mut WindowContext,
- ) {
- let matches = downcast_matches(matches);
- self.update(cx, |this, cx| this.activate_match(index, matches, cx));
+ fn activate_match(&self, index: usize, matches: &AnyVec<dyn Send>, cx: &mut WindowContext) {
+ let matches = matches.downcast_ref().unwrap();
+ self.update(cx, |this, cx| {
+ this.activate_match(index, matches.as_slice(), cx)
+ });
}
- fn select_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext) {
- let matches = downcast_matches(matches);
- self.update(cx, |this, cx| this.select_matches(matches, cx));
+ fn select_matches(&self, matches: &AnyVec<dyn Send>, cx: &mut WindowContext) {
+ let matches = matches.downcast_ref().unwrap();
+ self.update(cx, |this, cx| this.select_matches(matches.as_slice(), cx));
}
fn match_index_for_direction(
&self,
- matches: &Vec<Box<dyn Any + Send>>,
+ matches: &AnyVec<dyn Send>,
current_index: usize,
direction: Direction,
count: usize,
cx: &mut WindowContext,
) -> usize {
- let matches = downcast_matches(matches);
+ let matches = matches.downcast_ref().unwrap();
self.update(cx, |this, cx| {
- this.match_index_for_direction(&matches, current_index, direction, count, cx)
+ this.match_index_for_direction(matches.as_slice(), current_index, direction, count, cx)
})
}
fn find_matches(
&self,
query: Arc<SearchQuery>,
cx: &mut WindowContext,
- ) -> Task<Vec<Box<dyn Any + Send>>> {
+ ) -> Task<AnyVec<dyn Send>> {
let matches = self.update(cx, |this, cx| this.find_matches(query, cx));
cx.spawn(|_| async {
let matches = matches.await;
- matches
- .into_iter()
- .map::<Box<dyn Any + Send>, _>(|range| Box::new(range))
- .collect()
+ let mut any_matches = AnyVec::with_capacity::<T::Match>(matches.len());
+ {
+ let mut any_matches = any_matches.downcast_mut::<T::Match>().unwrap();
+ for mat in matches {
+ any_matches.push(mat);
+ }
+ }
+ any_matches
})
}
fn active_match_index(
&self,
- matches: &Vec<Box<dyn Any + Send>>,
+ matches: &AnyVec<dyn Send>,
cx: &mut WindowContext,
) -> Option<usize> {
- let matches = downcast_matches(matches);
- self.update(cx, |this, cx| this.active_match_index(matches, cx))
+ let matches = matches.downcast_ref()?;
+ self.update(cx, |this, cx| {
+ this.active_match_index(matches.as_slice(), cx)
+ })
}
- fn replace(&self, matches: &Box<dyn Any + Send>, query: &SearchQuery, cx: &mut WindowContext) {
- let matches = matches.downcast_ref().unwrap();
- self.update(cx, |this, cx| this.replace(matches, query, cx))
+ fn replace(
+ &self,
+ mat: any_vec::element::ElementRef<'_, dyn Send>,
+ query: &SearchQuery,
+ cx: &mut WindowContext,
+ ) {
+ let mat = mat.downcast_ref().unwrap();
+ self.update(cx, |this, cx| this.replace(mat, query, cx))
}
}
-fn downcast_matches<T: Any + Clone>(matches: &Vec<Box<dyn Any + Send>>) -> Vec<T> {
- matches
- .iter()
- .map(|range| range.downcast_ref::<T>().cloned())
- .collect::<Option<Vec<_>>>()
- .expect(
- "SearchableItemHandle function called with vec of matches of a different type than expected",
- )
-}
-
impl From<Box<dyn SearchableItemHandle>> for AnyView {
fn from(this: Box<dyn SearchableItemHandle>) -> Self {
this.to_any().clone()