{
+ div()
+ .occlude()
+ .id("diagnostic-popover-vertical-scroll")
+ .on_mouse_move(cx.listener(|_, _, _, cx| {
+ cx.notify();
+ cx.stop_propagation()
+ }))
+ .on_hover(|_, _, cx| {
+ cx.stop_propagation();
+ })
+ .on_any_mouse_down(|_, _, cx| {
+ cx.stop_propagation();
+ })
+ .on_mouse_up(
+ MouseButton::Left,
+ cx.listener(|_, _, _, cx| {
+ cx.stop_propagation();
+ }),
+ )
+ .on_scroll_wheel(cx.listener(|_, _, _, cx| {
+ cx.notify();
+ }))
+ .h_full()
+ .absolute()
+ .right_1()
+ .top_1()
+ .bottom_0()
+ .w(px(12.))
+ .cursor_default()
+ .children(Scrollbar::vertical(self.scrollbar_state.clone()))
+ }
}
#[cfg(test)]
diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs
index fa6bd93ab8..2e4631a62b 100644
--- a/crates/editor/src/items.rs
+++ b/crates/editor/src/items.rs
@@ -1607,24 +1607,10 @@ impl SearchableItem for Editor {
let text = self.buffer.read(cx);
let text = text.snapshot(cx);
let mut edits = vec![];
- let mut last_point: Option
= None;
for m in matches {
- let point = m.start.to_point(&text);
let text = text.text_for_range(m.clone()).collect::>();
- // Check if the row for the current match is different from the last
- // match. If that's not the case and we're still replacing matches
- // in the same row/line, skip this match if the `one_match_per_line`
- // option is enabled.
- if last_point.is_none() {
- last_point = Some(point);
- } else if last_point.is_some() && point.row != last_point.unwrap().row {
- last_point = Some(point);
- } else if query.one_match_per_line().is_some_and(|enabled| enabled) {
- continue;
- }
-
let text: Cow<_> = if text.len() == 1 {
text.first().cloned().unwrap().into()
} else {
diff --git a/crates/editor/src/scroll.rs b/crates/editor/src/scroll.rs
index 0642b2b20e..b3007d3091 100644
--- a/crates/editor/src/scroll.rs
+++ b/crates/editor/src/scroll.rs
@@ -13,6 +13,7 @@ use crate::{
pub use autoscroll::{Autoscroll, AutoscrollStrategy};
use core::fmt::Debug;
use gpui::{App, Axis, Context, Global, Pixels, Task, Window, point, px};
+use language::language_settings::{AllLanguageSettings, SoftWrap};
use language::{Bias, Point};
pub use scroll_amount::ScrollAmount;
use settings::Settings;
@@ -151,12 +152,16 @@ pub struct ScrollManager {
pub(crate) vertical_scroll_margin: f32,
anchor: ScrollAnchor,
ongoing: OngoingScroll,
+ /// The second element indicates whether the autoscroll request is local
+ /// (true) or remote (false). Local requests are initiated by user actions,
+ /// while remote requests come from external sources.
autoscroll_request: Option<(Autoscroll, bool)>,
last_autoscroll: Option<(gpui::Point, f32, f32, AutoscrollStrategy)>,
show_scrollbars: bool,
hide_scrollbar_task: Option>,
active_scrollbar: Option,
visible_line_count: Option,
+ visible_column_count: Option,
forbid_vertical_scroll: bool,
minimap_thumb_state: Option,
}
@@ -173,6 +178,7 @@ impl ScrollManager {
active_scrollbar: None,
last_autoscroll: None,
visible_line_count: None,
+ visible_column_count: None,
forbid_vertical_scroll: false,
minimap_thumb_state: None,
}
@@ -210,7 +216,7 @@ impl ScrollManager {
window: &mut Window,
cx: &mut Context,
) {
- let (new_anchor, top_row) = if scroll_position.y <= 0. {
+ let (new_anchor, top_row) = if scroll_position.y <= 0. && scroll_position.x <= 0. {
(
ScrollAnchor {
anchor: Anchor::min(),
@@ -218,6 +224,22 @@ impl ScrollManager {
},
0,
)
+ } else if scroll_position.y <= 0. {
+ let buffer_point = map
+ .clip_point(
+ DisplayPoint::new(DisplayRow(0), scroll_position.x as u32),
+ Bias::Left,
+ )
+ .to_point(map);
+ let anchor = map.buffer_snapshot.anchor_at(buffer_point, Bias::Right);
+
+ (
+ ScrollAnchor {
+ anchor: anchor,
+ offset: scroll_position.max(&gpui::Point::default()),
+ },
+ 0,
+ )
} else {
let scroll_top = scroll_position.y;
let scroll_top = match EditorSettings::get_global(cx).scroll_beyond_last_line {
@@ -242,8 +264,13 @@ impl ScrollManager {
}
};
- let scroll_top_buffer_point =
- DisplayPoint::new(DisplayRow(scroll_top as u32), 0).to_point(map);
+ let scroll_top_row = DisplayRow(scroll_top as u32);
+ let scroll_top_buffer_point = map
+ .clip_point(
+ DisplayPoint::new(scroll_top_row, scroll_position.x as u32),
+ Bias::Left,
+ )
+ .to_point(map);
let top_anchor = map
.buffer_snapshot
.anchor_at(scroll_top_buffer_point, Bias::Right);
@@ -476,6 +503,10 @@ impl Editor {
.map(|line_count| line_count as u32 - 1)
}
+ pub fn visible_column_count(&self) -> Option {
+ self.scroll_manager.visible_column_count
+ }
+
pub(crate) fn set_visible_line_count(
&mut self,
lines: f32,
@@ -497,6 +528,10 @@ impl Editor {
}
}
+ pub(crate) fn set_visible_column_count(&mut self, columns: f32) {
+ self.scroll_manager.visible_column_count = Some(columns);
+ }
+
pub fn apply_scroll_delta(
&mut self,
scroll_delta: gpui::Point,
@@ -675,25 +710,48 @@ impl Editor {
let Some(visible_line_count) = self.visible_line_count() else {
return;
};
+ let Some(mut visible_column_count) = self.visible_column_count() else {
+ return;
+ };
+
+ // If the user has a preferred line length, and has the editor
+ // configured to wrap at the preferred line length, or bounded to it,
+ // use that value over the visible column count. This was mostly done so
+ // that tests could actually be written for vim's `z l`, `z h`, `z
+ // shift-l` and `z shift-h` commands, as there wasn't a good way to
+ // configure the editor to only display a certain number of columns. If
+ // that ever happens, this could probably be removed.
+ let settings = AllLanguageSettings::get_global(cx);
+ if matches!(
+ settings.defaults.soft_wrap,
+ SoftWrap::PreferredLineLength | SoftWrap::Bounded
+ ) {
+ if (settings.defaults.preferred_line_length as f32) < visible_column_count {
+ visible_column_count = settings.defaults.preferred_line_length as f32;
+ }
+ }
// If the scroll position is currently at the left edge of the document
// (x == 0.0) and the intent is to scroll right, the gutter's margin
// should first be added to the current position, otherwise the cursor
// will end at the column position minus the margin, which looks off.
- if current_position.x == 0.0 && amount.columns() > 0. {
+ if current_position.x == 0.0 && amount.columns(visible_column_count) > 0. {
if let Some(last_position_map) = &self.last_position_map {
current_position.x += self.gutter_dimensions.margin / last_position_map.em_advance;
}
}
- let new_position =
- current_position + point(amount.columns(), amount.lines(visible_line_count));
+ let new_position = current_position
+ + point(
+ amount.columns(visible_column_count),
+ amount.lines(visible_line_count),
+ );
self.set_scroll_position(new_position, window, cx);
}
/// Returns an ordering. The newest selection is:
/// Ordering::Equal => on screen
- /// Ordering::Less => above the screen
- /// Ordering::Greater => below the screen
+ /// Ordering::Less => above or to the left of the screen
+ /// Ordering::Greater => below or to the right of the screen
pub fn newest_selection_on_screen(&self, cx: &mut App) -> Ordering {
let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let newest_head = self
@@ -711,8 +769,12 @@ impl Editor {
return Ordering::Less;
}
- if let Some(visible_lines) = self.visible_line_count() {
- if newest_head.row() <= DisplayRow(screen_top.row().0 + visible_lines as u32) {
+ if let (Some(visible_lines), Some(visible_columns)) =
+ (self.visible_line_count(), self.visible_column_count())
+ {
+ if newest_head.row() <= DisplayRow(screen_top.row().0 + visible_lines as u32)
+ && newest_head.column() <= screen_top.column() + visible_columns as u32
+ {
return Ordering::Equal;
}
}
diff --git a/crates/editor/src/scroll/autoscroll.rs b/crates/editor/src/scroll/autoscroll.rs
index 55998aa2fd..340277633a 100644
--- a/crates/editor/src/scroll/autoscroll.rs
+++ b/crates/editor/src/scroll/autoscroll.rs
@@ -274,12 +274,14 @@ impl Editor {
start_row: DisplayRow,
viewport_width: Pixels,
scroll_width: Pixels,
- max_glyph_width: Pixels,
+ em_advance: Pixels,
layouts: &[LineWithInvisibles],
+ window: &mut Window,
cx: &mut Context,
) -> bool {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selections = self.selections.all::(cx);
+ let mut scroll_position = self.scroll_manager.scroll_position(&display_map);
let mut target_left;
let mut target_right;
@@ -295,16 +297,17 @@ impl Editor {
if head.row() >= start_row
&& head.row() < DisplayRow(start_row.0 + layouts.len() as u32)
{
- let start_column = head.column().saturating_sub(3);
- let end_column = cmp::min(display_map.line_len(head.row()), head.column() + 3);
+ let start_column = head.column();
+ let end_column = cmp::min(display_map.line_len(head.row()), head.column());
target_left = target_left.min(
layouts[head.row().minus(start_row) as usize]
- .x_for_index(start_column as usize),
+ .x_for_index(start_column as usize)
+ + self.gutter_dimensions.margin,
);
target_right = target_right.max(
layouts[head.row().minus(start_row) as usize]
.x_for_index(end_column as usize)
- + max_glyph_width,
+ + em_advance,
);
}
}
@@ -319,14 +322,16 @@ impl Editor {
return false;
}
- let scroll_left = self.scroll_manager.anchor.offset.x * max_glyph_width;
+ let scroll_left = self.scroll_manager.anchor.offset.x * em_advance;
let scroll_right = scroll_left + viewport_width;
if target_left < scroll_left {
- self.scroll_manager.anchor.offset.x = target_left / max_glyph_width;
+ scroll_position.x = target_left / em_advance;
+ self.set_scroll_position_internal(scroll_position, true, true, window, cx);
true
} else if target_right > scroll_right {
- self.scroll_manager.anchor.offset.x = (target_right - viewport_width) / max_glyph_width;
+ scroll_position.x = (target_right - viewport_width) / em_advance;
+ self.set_scroll_position_internal(scroll_position, true, true, window, cx);
true
} else {
false
diff --git a/crates/editor/src/scroll/scroll_amount.rs b/crates/editor/src/scroll/scroll_amount.rs
index bc9d4757f1..b2af4f8e4f 100644
--- a/crates/editor/src/scroll/scroll_amount.rs
+++ b/crates/editor/src/scroll/scroll_amount.rs
@@ -23,6 +23,8 @@ pub enum ScrollAmount {
Page(f32),
// Scroll N columns (positive is towards the right of the document)
Column(f32),
+ // Scroll N page width (positive is towards the right of the document)
+ PageWidth(f32),
}
impl ScrollAmount {
@@ -37,14 +39,16 @@ impl ScrollAmount {
(visible_line_count * count).trunc()
}
Self::Column(_count) => 0.0,
+ Self::PageWidth(_count) => 0.0,
}
}
- pub fn columns(&self) -> f32 {
+ pub fn columns(&self, visible_column_count: f32) -> f32 {
match self {
Self::Line(_count) => 0.0,
Self::Page(_count) => 0.0,
Self::Column(count) => *count,
+ Self::PageWidth(count) => (visible_column_count * count).trunc(),
}
}
@@ -58,6 +62,7 @@ impl ScrollAmount {
// so I'm leaving this at 0.0 for now to try and make it clear that
// this should not have an impact on that?
ScrollAmount::Column(_) => px(0.0),
+ ScrollAmount::PageWidth(_) => px(0.0),
}
}
diff --git a/crates/eval/src/explorer.html b/crates/eval/src/explorer.html
index fec4597163..04c41090d3 100644
--- a/crates/eval/src/explorer.html
+++ b/crates/eval/src/explorer.html
@@ -324,20 +324,8 @@
Thread Explorer
-
-
+
+