Detailed changes
@@ -108,7 +108,7 @@ use gpui::{
DispatchPhase, Edges, Entity, EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent,
Focusable, FontId, FontWeight, Global, HighlightStyle, Hsla, KeyContext, Modifiers,
MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement, Pixels, Render,
- ScrollHandle, SharedString, Size, Stateful, Styled, Subscription, Task, TextStyle,
+ ScrollHandle, SharedString, Size, Stateful, Styled, Subscription, Task, TextRun, TextStyle,
TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle, WeakEntity,
WeakFocusHandle, Window, div, point, prelude::*, pulsating_between, px, relative, size,
};
@@ -575,7 +575,7 @@ impl Default for EditorStyle {
}
}
-pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
+pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
let show_background = language_settings::language_settings(None, None, cx)
.inlay_hints
.show_background;
@@ -598,7 +598,7 @@ pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
style
}
-pub fn make_suggestion_styles(cx: &mut App) -> EditPredictionStyles {
+pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
EditPredictionStyles {
insertion: HighlightStyle {
color: Some(cx.theme().status().predictive),
@@ -1249,6 +1249,7 @@ impl NextScrollCursorCenterTopBottom {
pub struct EditorSnapshot {
pub mode: EditorMode,
show_gutter: bool,
+ offset_content: bool,
show_line_numbers: Option<bool>,
show_git_diff_gutter: Option<bool>,
show_code_actions: Option<bool>,
@@ -1825,7 +1826,11 @@ impl Editor {
Editor::new_internal(mode, buffer, project, None, window, cx)
}
- pub fn sticky_headers(&self, cx: &App) -> Option<Vec<OutlineItem<Anchor>>> {
+ pub fn sticky_headers(
+ &self,
+ style: &EditorStyle,
+ cx: &App,
+ ) -> Option<Vec<OutlineItem<Anchor>>> {
let multi_buffer = self.buffer().read(cx);
let multi_buffer_snapshot = multi_buffer.snapshot(cx);
let multi_buffer_visible_start = self
@@ -1843,7 +1848,7 @@ impl Editor {
.outline_items_containing(
Point::new(start_row, 0)..Point::new(end_row, 0),
true,
- self.style().map(|style| style.syntax.as_ref()),
+ Some(style.syntax.as_ref()),
)
.into_iter()
.map(|outline_item| OutlineItem {
@@ -2935,6 +2940,7 @@ impl Editor {
EditorSnapshot {
mode: self.mode.clone(),
show_gutter: self.show_gutter,
+ offset_content: self.offset_content,
show_line_numbers: self.show_line_numbers,
show_git_diff_gutter: self.show_git_diff_gutter,
show_code_actions: self.show_code_actions,
@@ -6895,7 +6901,7 @@ impl Editor {
};
let anchor = self.selections.newest_anchor().head();
- let position = self.to_pixel_point(anchor, &snapshot, window);
+ let position = self.to_pixel_point(anchor, &snapshot, window, cx);
if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
self.show_blame_popover(
buffer,
@@ -9208,7 +9214,8 @@ impl Editor {
let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
- let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
+ let line_origin =
+ self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
let mut origin = start_point
@@ -9950,8 +9957,7 @@ impl Editor {
}
pub fn render_context_menu(
- &self,
- style: &EditorStyle,
+ &mut self,
max_height_in_lines: u32,
window: &mut Window,
cx: &mut Context<Editor>,
@@ -9961,7 +9967,9 @@ impl Editor {
if !menu.visible() {
return None;
};
- Some(menu.render(style, max_height_in_lines, window, cx))
+ self.style
+ .as_ref()
+ .map(|style| menu.render(style, max_height_in_lines, window, cx))
}
fn render_context_menu_aside(
@@ -20364,8 +20372,11 @@ impl Editor {
self.style = Some(style);
}
- pub fn style(&self) -> Option<&EditorStyle> {
- self.style.as_ref()
+ pub fn style(&mut self, cx: &App) -> &EditorStyle {
+ if self.style.is_none() {
+ self.style = Some(self.create_style(cx));
+ }
+ self.style.as_ref().unwrap()
}
// Called by the element. This method is not designed to be called outside of the editor
@@ -22989,22 +23000,24 @@ impl Editor {
}
pub fn to_pixel_point(
- &self,
+ &mut self,
source: multi_buffer::Anchor,
editor_snapshot: &EditorSnapshot,
window: &mut Window,
+ cx: &App,
) -> Option<gpui::Point<Pixels>> {
let source_point = source.to_display_point(editor_snapshot);
- self.display_to_pixel_point(source_point, editor_snapshot, window)
+ self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
}
pub fn display_to_pixel_point(
- &self,
+ &mut self,
source: DisplayPoint,
editor_snapshot: &EditorSnapshot,
window: &mut Window,
+ cx: &App,
) -> Option<gpui::Point<Pixels>> {
- let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
+ let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
let text_layout_details = self.text_layout_details(window);
let scroll_top = text_layout_details
.scroll_anchor
@@ -23068,10 +23081,6 @@ impl Editor {
}
}
- pub fn last_gutter_dimensions(&self) -> &GutterDimensions {
- &self.gutter_dimensions
- }
-
pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
self.load_diff_task.clone()
}
@@ -23171,6 +23180,57 @@ impl Editor {
// skip any LSP updates for it.
self.active_diagnostics == ActiveDiagnostic::All || !self.mode().is_full()
}
+
+ fn create_style(&self, cx: &App) -> EditorStyle {
+ let settings = ThemeSettings::get_global(cx);
+
+ let mut text_style = match self.mode {
+ EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
+ color: cx.theme().colors().editor_foreground,
+ font_family: settings.ui_font.family.clone(),
+ font_features: settings.ui_font.features.clone(),
+ font_fallbacks: settings.ui_font.fallbacks.clone(),
+ font_size: rems(0.875).into(),
+ font_weight: settings.ui_font.weight,
+ line_height: relative(settings.buffer_line_height.value()),
+ ..Default::default()
+ },
+ EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
+ color: cx.theme().colors().editor_foreground,
+ font_family: settings.buffer_font.family.clone(),
+ font_features: settings.buffer_font.features.clone(),
+ font_fallbacks: settings.buffer_font.fallbacks.clone(),
+ font_size: settings.buffer_font_size(cx).into(),
+ font_weight: settings.buffer_font.weight,
+ line_height: relative(settings.buffer_line_height.value()),
+ ..Default::default()
+ },
+ };
+ if let Some(text_style_refinement) = &self.text_style_refinement {
+ text_style.refine(text_style_refinement)
+ }
+
+ let background = match self.mode {
+ EditorMode::SingleLine => cx.theme().system().transparent,
+ EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
+ EditorMode::Full { .. } => cx.theme().colors().editor_background,
+ EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
+ };
+
+ EditorStyle {
+ background,
+ border: cx.theme().colors().border,
+ local_player: cx.theme().players().local(),
+ text: text_style,
+ scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
+ syntax: cx.theme().syntax().clone(),
+ status: cx.theme().status().clone(),
+ inlay_hints_style: make_inlay_hints_style(cx),
+ edit_prediction_styles: make_suggestion_styles(cx),
+ unnecessary_code_fade: settings.unnecessary_code_fade,
+ show_underlines: self.diagnostics_enabled(),
+ }
+ }
}
fn edit_for_markdown_paste<'a>(
@@ -24698,94 +24758,98 @@ impl EditorSnapshot {
self.scroll_anchor.scroll_position(&self.display_snapshot)
}
- fn gutter_dimensions(
+ pub fn gutter_dimensions(
&self,
font_id: FontId,
font_size: Pixels,
- max_line_number_width: Pixels,
+ style: &EditorStyle,
+ window: &mut Window,
cx: &App,
- ) -> Option<GutterDimensions> {
- if !self.show_gutter {
- return None;
- }
-
- let ch_width = cx.text_system().ch_width(font_id, font_size).log_err()?;
- let ch_advance = cx.text_system().ch_advance(font_id, font_size).log_err()?;
+ ) -> GutterDimensions {
+ if self.show_gutter
+ && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
+ && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
+ {
+ let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
+ matches!(
+ ProjectSettings::get_global(cx).git.git_gutter,
+ GitGutterSetting::TrackedFiles
+ )
+ });
+ let gutter_settings = EditorSettings::get_global(cx).gutter;
+ let show_line_numbers = self
+ .show_line_numbers
+ .unwrap_or(gutter_settings.line_numbers);
+ let line_gutter_width = if show_line_numbers {
+ // Avoid flicker-like gutter resizes when the line number gains another digit by
+ // only resizing the gutter on files with > 10**min_line_number_digits lines.
+ let min_width_for_number_on_gutter =
+ ch_advance * gutter_settings.min_line_number_digits as f32;
+ self.max_line_number_width(style, window)
+ .max(min_width_for_number_on_gutter)
+ } else {
+ 0.0.into()
+ };
- let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
- matches!(
- ProjectSettings::get_global(cx).git.git_gutter,
- GitGutterSetting::TrackedFiles
- )
- });
- let gutter_settings = EditorSettings::get_global(cx).gutter;
- let show_line_numbers = self
- .show_line_numbers
- .unwrap_or(gutter_settings.line_numbers);
- let line_gutter_width = if show_line_numbers {
- // Avoid flicker-like gutter resizes when the line number gains another digit by
- // only resizing the gutter on files with > 10**min_line_number_digits lines.
- let min_width_for_number_on_gutter =
- ch_advance * gutter_settings.min_line_number_digits as f32;
- max_line_number_width.max(min_width_for_number_on_gutter)
- } else {
- 0.0.into()
- };
+ let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
+ let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
- let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
- let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
+ let git_blame_entries_width =
+ self.git_blame_gutter_max_author_length
+ .map(|max_author_length| {
+ let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
+ const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
- let git_blame_entries_width =
- self.git_blame_gutter_max_author_length
- .map(|max_author_length| {
- let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
- const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
+ /// The number of characters to dedicate to gaps and margins.
+ const SPACING_WIDTH: usize = 4;
- /// The number of characters to dedicate to gaps and margins.
- const SPACING_WIDTH: usize = 4;
+ let max_char_count = max_author_length.min(renderer.max_author_length())
+ + ::git::SHORT_SHA_LENGTH
+ + MAX_RELATIVE_TIMESTAMP.len()
+ + SPACING_WIDTH;
- let max_char_count = max_author_length.min(renderer.max_author_length())
- + ::git::SHORT_SHA_LENGTH
- + MAX_RELATIVE_TIMESTAMP.len()
- + SPACING_WIDTH;
+ ch_advance * max_char_count
+ });
- ch_advance * max_char_count
- });
+ let is_singleton = self.buffer_snapshot().is_singleton();
+
+ let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
+ left_padding += if !is_singleton {
+ ch_width * 4.0
+ } else if show_runnables || show_breakpoints {
+ ch_width * 3.0
+ } else if show_git_gutter && show_line_numbers {
+ ch_width * 2.0
+ } else if show_git_gutter || show_line_numbers {
+ ch_width
+ } else {
+ px(0.)
+ };
- let is_singleton = self.buffer_snapshot().is_singleton();
-
- let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
- left_padding += if !is_singleton {
- ch_width * 4.0
- } else if show_runnables || show_breakpoints {
- ch_width * 3.0
- } else if show_git_gutter && show_line_numbers {
- ch_width * 2.0
- } else if show_git_gutter || show_line_numbers {
- ch_width
- } else {
- px(0.)
- };
+ let shows_folds = is_singleton && gutter_settings.folds;
- let shows_folds = is_singleton && gutter_settings.folds;
+ let right_padding = if shows_folds && show_line_numbers {
+ ch_width * 4.0
+ } else if shows_folds || (!is_singleton && show_line_numbers) {
+ ch_width * 3.0
+ } else if show_line_numbers {
+ ch_width
+ } else {
+ px(0.)
+ };
- let right_padding = if shows_folds && show_line_numbers {
- ch_width * 4.0
- } else if shows_folds || (!is_singleton && show_line_numbers) {
- ch_width * 3.0
- } else if show_line_numbers {
- ch_width
+ GutterDimensions {
+ left_padding,
+ right_padding,
+ width: line_gutter_width + left_padding + right_padding,
+ margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
+ git_blame_entries_width,
+ }
+ } else if self.offset_content {
+ GutterDimensions::default_with_margin(font_id, font_size, cx)
} else {
- px(0.)
- };
-
- Some(GutterDimensions {
- left_padding,
- right_padding,
- width: line_gutter_width + left_padding + right_padding,
- margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
- git_blame_entries_width,
- })
+ GutterDimensions::default()
+ }
}
pub fn render_crease_toggle(
@@ -24868,6 +24932,28 @@ impl EditorSnapshot {
None
}
}
+
+ pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
+ let digit_count = self.widest_line_number().ilog10() + 1;
+ column_pixels(style, digit_count as usize, window)
+ }
+}
+
+pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
+ let font_size = style.text.font_size.to_pixels(window.rem_size());
+ let layout = window.text_system().shape_line(
+ SharedString::from(" ".repeat(column)),
+ font_size,
+ &[TextRun {
+ len: column,
+ font: style.text.font(),
+ color: Hsla::default(),
+ ..Default::default()
+ }],
+ None,
+ );
+
+ layout.width
}
impl Deref for EditorSnapshot {
@@ -24948,57 +25034,7 @@ impl Focusable for Editor {
impl Render for Editor {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
- let settings = ThemeSettings::get_global(cx);
-
- let mut text_style = match self.mode {
- EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
- color: cx.theme().colors().editor_foreground,
- font_family: settings.ui_font.family.clone(),
- font_features: settings.ui_font.features.clone(),
- font_fallbacks: settings.ui_font.fallbacks.clone(),
- font_size: rems(0.875).into(),
- font_weight: settings.ui_font.weight,
- line_height: relative(settings.buffer_line_height.value()),
- ..Default::default()
- },
- EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
- color: cx.theme().colors().editor_foreground,
- font_family: settings.buffer_font.family.clone(),
- font_features: settings.buffer_font.features.clone(),
- font_fallbacks: settings.buffer_font.fallbacks.clone(),
- font_size: settings.buffer_font_size(cx).into(),
- font_weight: settings.buffer_font.weight,
- line_height: relative(settings.buffer_line_height.value()),
- ..Default::default()
- },
- };
- if let Some(text_style_refinement) = &self.text_style_refinement {
- text_style.refine(text_style_refinement)
- }
-
- let background = match self.mode {
- EditorMode::SingleLine => cx.theme().system().transparent,
- EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
- EditorMode::Full { .. } => cx.theme().colors().editor_background,
- EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
- };
-
- EditorElement::new(
- &cx.entity(),
- EditorStyle {
- background,
- border: cx.theme().colors().border,
- local_player: cx.theme().players().local(),
- text: text_style,
- scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
- syntax: cx.theme().syntax().clone(),
- status: cx.theme().status().clone(),
- inlay_hints_style: make_inlay_hints_style(cx),
- edit_prediction_styles: make_suggestion_styles(cx),
- unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
- show_underlines: self.diagnostics_enabled(),
- },
- )
+ EditorElement::new(&cx.entity(), self.create_style(cx))
}
}
@@ -2218,10 +2218,9 @@ async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext)
init_test(cx, |_| {});
let mut cx = EditorTestContext::new(cx).await;
- let line_height = cx.editor(|editor, window, _| {
+ let line_height = cx.update_editor(|editor, window, cx| {
editor
- .style()
- .unwrap()
+ .style(cx)
.text
.line_height_in_pixels(window.rem_size())
});
@@ -2334,10 +2333,9 @@ async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext)
async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorTestContext::new(cx).await;
- let line_height = cx.editor(|editor, window, _| {
+ let line_height = cx.update_editor(|editor, window, cx| {
editor
- .style()
- .unwrap()
+ .style(cx)
.text
.line_height_in_pixels(window.rem_size())
});
@@ -2400,8 +2398,7 @@ async fn test_autoscroll(cx: &mut TestAppContext) {
let line_height = cx.update_editor(|editor, window, cx| {
editor.set_vertical_scroll_margin(2, cx);
editor
- .style()
- .unwrap()
+ .style(cx)
.text
.line_height_in_pixels(window.rem_size())
});
@@ -2480,10 +2477,9 @@ async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorTestContext::new(cx).await;
- let line_height = cx.editor(|editor, window, _cx| {
+ let line_height = cx.update_editor(|editor, window, cx| {
editor
- .style()
- .unwrap()
+ .style(cx)
.text
.line_height_in_pixels(window.rem_size())
});
@@ -28311,7 +28307,8 @@ async fn test_sticky_scroll(cx: &mut TestAppContext) {
let mut sticky_headers = |offset: ScrollOffset| {
cx.update_editor(|e, window, cx| {
e.scroll(gpui::Point { x: 0., y: offset }, None, window, cx);
- EditorElement::sticky_headers(&e, &e.snapshot(window, cx), cx)
+ let style = e.style(cx).clone();
+ EditorElement::sticky_headers(&e, &e.snapshot(window, cx), &style, cx)
.into_iter()
.map(
|StickyHeader {
@@ -28365,10 +28362,9 @@ async fn test_scroll_by_clicking_sticky_header(cx: &mut TestAppContext) {
});
let mut cx = EditorTestContext::new(cx).await;
- let line_height = cx.editor(|editor, window, _cx| {
+ let line_height = cx.update_editor(|editor, window, cx| {
editor
- .style()
- .unwrap()
+ .style(cx)
.text
.line_height_in_pixels(window.rem_size())
});
@@ -11,6 +11,7 @@ use crate::{
SelectedTextHighlight, Selection, SelectionDragState, SelectionEffects, SizingBehavior,
SoftWrap, StickyHeaderExcerpt, ToPoint, ToggleFold, ToggleFoldAll,
code_context_menus::{CodeActionsMenu, MENU_ASIDE_MAX_WIDTH, MENU_ASIDE_MIN_WIDTH, MENU_GAP},
+ column_pixels,
display_map::{
Block, BlockContext, BlockStyle, ChunkRendererId, DisplaySnapshot, EditorMargins,
HighlightKey, HighlightedChunk, ToDisplayPoint,
@@ -2269,7 +2270,8 @@ impl EditorElement {
};
let padding = ProjectSettings::get_global(cx).diagnostics.inline.padding as f32 * em_width;
- let min_x = self.column_pixels(
+ let min_x = column_pixels(
+ &self.style,
ProjectSettings::get_global(cx)
.diagnostics
.inline
@@ -2572,7 +2574,8 @@ impl EditorElement {
let padded_line_end = line_end + padding;
- let min_column_in_pixels = self.column_pixels(
+ let min_column_in_pixels = column_pixels(
+ &self.style,
ProjectSettings::get_global(cx).git.inline_blame.min_column as usize,
window,
);
@@ -2796,7 +2799,7 @@ impl EditorElement {
.enumerate()
.filter_map(|(i, indent_guide)| {
let single_indent_width =
- self.column_pixels(indent_guide.tab_size as usize, window);
+ column_pixels(&self.style, indent_guide.tab_size as usize, window);
let total_width = single_indent_width * indent_guide.depth as f32;
let start_x = Pixels::from(
ScrollOffset::from(content_origin.x + total_width)
@@ -2853,7 +2856,7 @@ impl EditorElement {
.wrap_guides(cx)
.into_iter()
.flat_map(|(guide, active)| {
- let wrap_position = self.column_pixels(guide, window);
+ let wrap_position = column_pixels(&self.style, guide, window);
let wrap_guide_x = wrap_position + horizontal_offset;
let display_wrap_guide = wrap_guide_x >= content_origin
&& wrap_guide_x <= hitbox.bounds.right() - vertical_scrollbar_width;
@@ -4619,6 +4622,7 @@ impl EditorElement {
gutter_dimensions: &GutterDimensions,
gutter_hitbox: &Hitbox,
text_hitbox: &Hitbox,
+ style: &EditorStyle,
window: &mut Window,
cx: &mut App,
) -> Option<StickyHeaders> {
@@ -4626,7 +4630,7 @@ impl EditorElement {
.show_line_numbers
.unwrap_or_else(|| EditorSettings::get_global(cx).gutter.line_numbers);
- let rows = Self::sticky_headers(self.editor.read(cx), snapshot, cx);
+ let rows = Self::sticky_headers(self.editor.read(cx), snapshot, style, cx);
let mut lines = Vec::<StickyHeaderLine>::new();
@@ -4685,6 +4689,7 @@ impl EditorElement {
pub(crate) fn sticky_headers(
editor: &Editor,
snapshot: &EditorSnapshot,
+ style: &EditorStyle,
cx: &App,
) -> Vec<StickyHeader> {
let scroll_top = snapshot.scroll_position().y;
@@ -4692,7 +4697,7 @@ impl EditorElement {
let mut end_rows = Vec::<DisplayRow>::new();
let mut rows = Vec::<StickyHeader>::new();
- let items = editor.sticky_headers(cx).unwrap_or_default();
+ let items = editor.sticky_headers(style, cx).unwrap_or_default();
for item in items {
let start_point = item.range.start.to_point(snapshot.buffer_snapshot());
@@ -5255,7 +5260,7 @@ impl EditorElement {
) -> Option<AnyElement> {
let max_height_in_lines = ((height - POPOVER_Y_PADDING) / line_height).floor() as u32;
self.editor.update(cx, |editor, cx| {
- editor.render_context_menu(&self.style, max_height_in_lines, window, cx)
+ editor.render_context_menu(max_height_in_lines, window, cx)
})
}
@@ -5282,16 +5287,18 @@ impl EditorElement {
window: &mut Window,
cx: &mut App,
) -> Option<AnyElement> {
- let position = self.editor.update(cx, |editor, _cx| {
+ let position = self.editor.update(cx, |editor, cx| {
let visible_start_point = editor.display_to_pixel_point(
DisplayPoint::new(visible_range.start, 0),
editor_snapshot,
window,
+ cx,
)?;
let visible_end_point = editor.display_to_pixel_point(
DisplayPoint::new(visible_range.end, 0),
editor_snapshot,
window,
+ cx,
)?;
let mouse_context_menu = editor.mouse_context_menu.as_ref()?;
@@ -5299,7 +5306,8 @@ impl EditorElement {
MenuPosition::PinnedToScreen(point) => (None, point),
MenuPosition::PinnedToEditor { source, offset } => {
let source_display_point = source.to_display_point(editor_snapshot);
- let source_point = editor.to_pixel_point(source, editor_snapshot, window)?;
+ let source_point =
+ editor.to_pixel_point(source, editor_snapshot, window, cx)?;
let position = content_origin + source_point + offset;
(Some(source_display_point), position)
}
@@ -7773,29 +7781,6 @@ impl EditorElement {
});
}
- fn column_pixels(&self, column: usize, window: &Window) -> Pixels {
- let style = &self.style;
- let font_size = style.text.font_size.to_pixels(window.rem_size());
- let layout = window.text_system().shape_line(
- SharedString::from(" ".repeat(column)),
- font_size,
- &[TextRun {
- len: column,
- font: style.text.font(),
- color: Hsla::default(),
- ..Default::default()
- }],
- None,
- );
-
- layout.width
- }
-
- fn max_line_number_width(&self, snapshot: &EditorSnapshot, window: &mut Window) -> Pixels {
- let digit_count = snapshot.widest_line_number().ilog10() + 1;
- self.column_pixels(digit_count as usize, window)
- }
-
fn shape_line_number(
&self,
text: SharedString,
@@ -8943,8 +8928,6 @@ impl Element for EditorElement {
max_lines,
} => {
let editor_handle = cx.entity();
- let max_line_number_width =
- self.max_line_number_width(&editor.snapshot(window, cx), window);
window.request_measured_layout(
Style::default(),
move |known_dimensions, available_space, window, cx| {
@@ -8954,7 +8937,6 @@ impl Element for EditorElement {
editor,
min_lines,
max_lines,
- max_line_number_width,
known_dimensions,
available_space.width,
window,
@@ -9041,15 +9023,10 @@ impl Element for EditorElement {
.gutter_dimensions(
font_id,
font_size,
- self.max_line_number_width(&snapshot, window),
+ style,
+ window,
cx,
- )
- .or_else(|| {
- self.editor.read(cx).offset_content.then(|| {
- GutterDimensions::default_with_margin(font_id, font_size, cx)
- })
- })
- .unwrap_or_default();
+ );
let text_width = bounds.size.width - gutter_dimensions.width;
let settings = EditorSettings::get_global(cx);
@@ -9740,6 +9717,7 @@ impl Element for EditorElement {
&gutter_dimensions,
&gutter_hitbox,
&text_hitbox,
+ &style,
window,
cx,
)
@@ -11456,7 +11434,6 @@ fn compute_auto_height_layout(
editor: &mut Editor,
min_lines: usize,
max_lines: Option<usize>,
- max_line_number_width: Pixels,
known_dimensions: Size<Option<Pixels>>,
available_width: AvailableSpace,
window: &mut Window,
@@ -11480,14 +11457,7 @@ fn compute_auto_height_layout(
let em_width = window.text_system().em_width(font_id, font_size).unwrap();
let mut snapshot = editor.snapshot(window, cx);
- let gutter_dimensions = snapshot
- .gutter_dimensions(font_id, font_size, max_line_number_width, cx)
- .or_else(|| {
- editor
- .offset_content
- .then(|| GutterDimensions::default_with_margin(font_id, font_size, cx))
- })
- .unwrap_or_default();
+ let gutter_dimensions = snapshot.gutter_dimensions(font_id, font_size, style, window, cx);
editor.gutter_dimensions = gutter_dimensions;
let text_width = width - gutter_dimensions.width;
@@ -11550,7 +11520,7 @@ mod tests {
});
let cx = &mut VisualTestContext::from_window(*window, cx);
let editor = window.root(cx).unwrap();
- let style = cx.update(|_, cx| editor.read(cx).style().unwrap().clone());
+ let style = cx.update(|_, cx| editor.update(cx, |editor, cx| editor.style(cx).clone()));
for x in 1..=100 {
let (_, state) = cx.draw(
@@ -11578,7 +11548,7 @@ mod tests {
});
let cx = &mut VisualTestContext::from_window(*window, cx);
let editor = window.root(cx).unwrap();
- let style = cx.update(|_, cx| editor.read(cx).style().unwrap().clone());
+ let style = cx.update(|_, cx| editor.update(cx, |editor, cx| editor.style(cx).clone()));
for x in 1..=100 {
let (_, state) = cx.draw(
@@ -11603,7 +11573,7 @@ mod tests {
});
let editor = window.root(cx).unwrap();
- let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
+ let style = editor.update(cx, |editor, cx| editor.style(cx).clone());
let line_height = window
.update(cx, |_, window, _| {
style.text.line_height_in_pixels(window.rem_size())
@@ -11751,7 +11721,7 @@ mod tests {
});
let editor = window.root(cx).unwrap();
- let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
+ let style = editor.update(cx, |editor, cx| editor.style(cx).clone());
let line_height = window
.update(cx, |_, window, _| {
style.text.line_height_in_pixels(window.rem_size())
@@ -11878,7 +11848,7 @@ mod tests {
});
let cx = &mut VisualTestContext::from_window(*window, cx);
let editor = window.root(cx).unwrap();
- let style = cx.update(|_, cx| editor.read(cx).style().unwrap().clone());
+ let style = cx.update(|_, cx| editor.update(cx, |editor, cx| editor.style(cx).clone()));
window
.update(cx, |editor, window, cx| {
@@ -11949,7 +11919,7 @@ mod tests {
});
let cx = &mut VisualTestContext::from_window(*window, cx);
let editor = window.root(cx).unwrap();
- let style = cx.update(|_, cx| editor.read(cx).style().unwrap().clone());
+ let style = cx.update(|_, cx| editor.update(cx, |editor, cx| editor.style(cx).clone()));
window
.update(cx, |editor, window, cx| {
editor.set_placeholder_text("hello", window, cx);
@@ -12189,7 +12159,7 @@ mod tests {
let cx = &mut VisualTestContext::from_window(*window, cx);
let editor = window.root(cx).unwrap();
- let style = cx.update(|_, cx| editor.read(cx).style().unwrap().clone());
+ let style = editor.update(cx, |editor, cx| editor.style(cx).clone());
window
.update(cx, |editor, _, cx| {
editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
@@ -59,7 +59,7 @@ impl MouseContextMenu {
x: editor.gutter_dimensions.width,
y: Pixels::ZERO,
};
- let source_position = editor.to_pixel_point(source, &editor_snapshot, window)?;
+ let source_position = editor.to_pixel_point(source, &editor_snapshot, window, cx)?;
let menu_position = MenuPosition::PinnedToEditor {
source,
offset: position - (source_position + content_origin),
@@ -283,8 +283,7 @@ impl EditorTestContext {
.head();
let pixel_position = editor.pixel_position_of_newest_cursor.unwrap();
let line_height = editor
- .style()
- .unwrap()
+ .style(cx)
.text
.line_height_in_pixels(window.rem_size());
let snapshot = editor.snapshot(window, cx);
@@ -1,9 +1,7 @@
use anyhow::{Context as _, Result};
use buffer_diff::{BufferDiff, BufferDiffSnapshot};
use editor::display_map::{BlockPlacement, BlockProperties, BlockStyle};
-use editor::{
- Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, multibuffer_context_lines,
-};
+use editor::{Editor, EditorEvent, ExcerptRange, MultiBuffer, multibuffer_context_lines};
use git::repository::{CommitDetails, CommitDiff, RepoPath};
use git::{GitHostingProviderRegistry, GitRemote, parse_git_remote_url};
use gpui::{
@@ -13,7 +11,7 @@ use gpui::{
};
use language::{
Anchor, Buffer, Capability, DiskState, File, LanguageRegistry, LineEnding, OffsetRangeExt as _,
- ReplicaId, Rope, TextBuffer,
+ Point, ReplicaId, Rope, TextBuffer,
};
use multi_buffer::PathKey;
use project::{Project, WorktreeId, git_store::Repository};
@@ -70,6 +68,7 @@ struct GitBlob {
display_name: Arc<str>,
}
+const COMMIT_MESSAGE_SORT_PREFIX: u64 = 0;
const FILE_NAMESPACE_SORT_PREFIX: u64 = 1;
impl CommitView {
@@ -147,6 +146,32 @@ impl CommitView {
) -> Self {
let language_registry = project.read(cx).languages().clone();
let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadOnly));
+
+ let message_buffer = cx.new(|cx| {
+ let mut buffer = Buffer::local(commit.message.clone(), cx);
+ buffer.set_capability(Capability::ReadOnly, cx);
+ buffer
+ });
+
+ multibuffer.update(cx, |multibuffer, cx| {
+ let snapshot = message_buffer.read(cx).snapshot();
+ let full_range = Point::zero()..snapshot.max_point();
+ let range = ExcerptRange {
+ context: full_range.clone(),
+ primary: full_range,
+ };
+ multibuffer.set_excerpt_ranges_for_path(
+ PathKey::with_sort_prefix(
+ COMMIT_MESSAGE_SORT_PREFIX,
+ RelPath::unix("commit message").unwrap().into(),
+ ),
+ message_buffer.clone(),
+ &snapshot,
+ vec![range],
+ cx,
+ )
+ });
+
let editor = cx.new(|cx| {
let mut editor =
Editor::for_multibuffer(multibuffer.clone(), Some(project.clone()), window, cx);
@@ -154,9 +179,38 @@ impl CommitView {
editor.disable_inline_diagnostics();
editor.set_show_breakpoints(false, cx);
editor.set_expand_all_diff_hunks(cx);
+ editor.disable_header_for_buffer(message_buffer.read(cx).remote_id(), cx);
+ editor.disable_indent_guides_for_buffer(message_buffer.read(cx).remote_id(), cx);
+
+ editor.insert_blocks(
+ [BlockProperties {
+ placement: BlockPlacement::Above(editor::Anchor::min()),
+ height: Some(1),
+ style: BlockStyle::Sticky,
+ render: Arc::new(|_| gpui::Empty.into_any_element()),
+ priority: 0,
+ }]
+ .into_iter()
+ .chain(
+ editor
+ .buffer()
+ .read(cx)
+ .buffer_anchor_to_anchor(&message_buffer, Anchor::MAX, cx)
+ .map(|anchor| BlockProperties {
+ placement: BlockPlacement::Below(anchor),
+ height: Some(1),
+ style: BlockStyle::Sticky,
+ render: Arc::new(|_| gpui::Empty.into_any_element()),
+ priority: 0,
+ }),
+ ),
+ None,
+ cx,
+ );
editor
});
+
let commit_sha = Arc::<str>::from(commit.sha.as_ref());
let first_worktree_id = project
@@ -166,7 +220,6 @@ impl CommitView {
.map(|worktree| worktree.read(cx).id());
let repository_clone = repository.clone();
- let commit_message = commit.message.clone();
cx.spawn(async move |this, cx| {
for file in commit_diff.files {
@@ -228,59 +281,6 @@ impl CommitView {
})?;
}
- let message_buffer = cx.new(|cx| {
- let mut buffer = Buffer::local(commit_message, cx);
- buffer.set_capability(Capability::ReadOnly, cx);
- buffer
- })?;
-
- this.update(cx, |this, cx| {
- this.multibuffer.update(cx, |multibuffer, cx| {
- let range = ExcerptRange {
- context: Anchor::MIN..Anchor::MAX,
- primary: Anchor::MIN..Anchor::MAX,
- };
- multibuffer.insert_excerpts_after(
- ExcerptId::min(),
- message_buffer.clone(),
- [range],
- cx,
- )
- });
-
- this.editor.update(cx, |editor, cx| {
- editor.disable_header_for_buffer(message_buffer.read(cx).remote_id(), cx);
- editor
- .disable_indent_guides_for_buffer(message_buffer.read(cx).remote_id(), cx);
-
- editor.insert_blocks(
- [BlockProperties {
- placement: BlockPlacement::Above(editor::Anchor::min()),
- height: Some(1),
- style: BlockStyle::Sticky,
- render: Arc::new(|_| gpui::Empty.into_any_element()),
- priority: 0,
- }]
- .into_iter()
- .chain(
- editor
- .buffer()
- .read(cx)
- .buffer_anchor_to_anchor(&message_buffer, Anchor::MAX, cx)
- .map(|anchor| BlockProperties {
- placement: BlockPlacement::Below(anchor),
- height: Some(1),
- style: BlockStyle::Sticky,
- render: Arc::new(|_| gpui::Empty.into_any_element()),
- priority: 0,
- }),
- ),
- None,
- cx,
- )
- });
- })?;
-
anyhow::Ok(())
})
.detach();
@@ -417,12 +417,23 @@ impl CommitView {
None
};
+ let gutter_width = self.editor.update(cx, |editor, cx| {
+ let snapshot = editor.snapshot(window, cx);
+ let style = editor.style(cx);
+ let font_id = window.text_system().resolve_font(&style.text.font());
+ let font_size = style.text.font_size.to_pixels(window.rem_size());
+ snapshot
+ .gutter_dimensions(font_id, font_size, style, window, cx)
+ .full_width()
+ });
+
h_flex()
.border_b_1()
.border_color(cx.theme().colors().border_variant)
+ .w_full()
.child(
h_flex()
- .w(self.editor.read(cx).last_gutter_dimensions().full_width())
+ .w(gutter_width)
.justify_center()
.child(self.render_commit_avatar(&commit.sha, rems_from_px(48.), window, cx)),
)
@@ -1011,7 +1022,9 @@ impl Render for CommitView {
.size_full()
.bg(cx.theme().colors().editor_background)
.child(self.render_header(window, cx))
- .child(div().flex_grow().child(self.editor.clone()))
+ .when(!self.editor.read(cx).is_empty(cx), |this| {
+ this.child(div().flex_grow().child(self.editor.clone()))
+ })
}
}
@@ -294,11 +294,10 @@ mod test {
async fn test_scroll(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
- let (line_height, visible_line_count) = cx.editor(|editor, window, _cx| {
+ let (line_height, visible_line_count) = cx.update_editor(|editor, window, cx| {
(
editor
- .style()
- .unwrap()
+ .style(cx)
.text
.line_height_in_pixels(window.rem_size()),
editor.visible_line_count().unwrap(),
@@ -2399,7 +2399,7 @@ async fn test_clipping_on_mode_change(cx: &mut gpui::TestAppContext) {
.end;
editor.last_bounds().unwrap().origin
+ editor
- .display_to_pixel_point(current_head, &snapshot, window)
+ .display_to_pixel_point(current_head, &snapshot, window, cx)
.unwrap()
});
pixel_position.x += px(100.);
@@ -304,11 +304,10 @@ impl NeovimBackedTestContext {
self.neovim.set_option(&format!("scrolloff={}", 3)).await;
// +2 to account for the vim command UI at the bottom.
self.neovim.set_option(&format!("lines={}", rows + 2)).await;
- let (line_height, visible_line_count) = self.editor(|editor, window, _cx| {
+ let (line_height, visible_line_count) = self.update_editor(|editor, window, cx| {
(
editor
- .style()
- .unwrap()
+ .style(cx)
.text
.line_height_in_pixels(window.rem_size()),
editor.visible_line_count().unwrap(),
@@ -174,17 +174,13 @@ impl Render for QuickActionBar {
.as_ref()
.is_some_and(|menu| matches!(menu.origin(), ContextMenuOrigin::QuickActionBar))
};
- let code_action_element = if is_deployed {
- editor.update(cx, |editor, cx| {
- if let Some(style) = editor.style() {
- editor.render_context_menu(style, MAX_CODE_ACTION_MENU_LINES, window, cx)
- } else {
- None
- }
+ let code_action_element = is_deployed
+ .then(|| {
+ editor.update(cx, |editor, cx| {
+ editor.render_context_menu(MAX_CODE_ACTION_MENU_LINES, window, cx)
+ })
})
- } else {
- None
- };
+ .flatten();
v_flex()
.child(
IconButton::new("toggle_code_actions_icon", IconName::BoltOutlined)