editor: Improve line number width by max lines. (#1291)

This commit is contained in:
Jason Lee 2025-09-25 22:21:09 +08:00 committed by GitHub
parent 6b7a481fc8
commit 13e2b08873
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 57 additions and 50 deletions

View file

@ -1,10 +1,9 @@
use gpui::*; use gpui::*;
use gpui_component::{ use gpui_component::{
highlighter::{HighlightTheme, Language}, highlighter::Language,
input::{InputEvent, InputState, TabSize, TextInput}, input::{InputEvent, InputState, TabSize, TextInput},
resizable::{h_resizable, resizable_panel, ResizableState}, resizable::{h_resizable, resizable_panel, ResizableState},
text::{TextView, TextViewStyle}, text::TextView,
ActiveTheme as _,
}; };
use story::Assets; use story::Assets;
@ -48,14 +47,6 @@ impl Example {
impl Render for Example { impl Render for Example {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = if cx.theme().mode.is_dark() {
HighlightTheme::default_dark()
} else {
HighlightTheme::default_light()
};
let is_dark = cx.theme().mode.is_dark();
h_resizable("container", self.resizable_state.clone()) h_resizable("container", self.resizable_state.clone())
.child( .child(
resizable_panel().child( resizable_panel().child(
@ -68,7 +59,7 @@ impl Render for Example {
TextInput::new(&self.input_state) TextInput::new(&self.input_state)
.h_full() .h_full()
.p_0() .p_0()
.appearance(false) .border_0()
.focus_bordered(false), .focus_bordered(false),
), ),
), ),
@ -87,12 +78,7 @@ impl Render for Example {
window, window,
cx, cx,
) )
.selectable() .selectable(),
.style(TextViewStyle {
highlight_theme: theme.clone(),
is_dark,
..Default::default()
}),
), ),
), ),
) )

View file

@ -4,7 +4,7 @@ use gpui::{
fill, point, px, relative, size, App, Bounds, Corners, Element, ElementId, ElementInputHandler, fill, point, px, relative, size, App, Bounds, Corners, Element, ElementId, ElementInputHandler,
Entity, GlobalElementId, Half, HighlightStyle, Hitbox, IntoElement, LayoutId, MouseButton, Entity, GlobalElementId, Half, HighlightStyle, Hitbox, IntoElement, LayoutId, MouseButton,
MouseMoveEvent, Path, Pixels, Point, ShapedLine, SharedString, Size, Style, TextAlign, TextRun, MouseMoveEvent, Path, Pixels, Point, ShapedLine, SharedString, Size, Style, TextAlign, TextRun,
UnderlineStyle, Window, TextStyle, UnderlineStyle, Window,
}; };
use ropey::Rope; use ropey::Rope;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -483,6 +483,45 @@ impl TextElement {
(visible_range, visible_top) (visible_range, visible_top)
} }
/// Return (line_number_width, line_number_len)
fn layout_line_numbers(
state: &InputState,
text: &Rope,
font_size: Pixels,
style: &TextStyle,
window: &mut Window,
) -> (Pixels, usize) {
let total_lines = text.lines_len();
let line_number_len = match total_lines {
0..=9999 => 5,
10000..=99999 => 6,
100000..=999999 => 7,
_ => 8,
};
let line_number_width = if state.mode.line_number() {
let empty_line_number = window.text_system().shape_line(
"+".repeat(line_number_len).into(),
font_size,
&[TextRun {
len: line_number_len,
font: style.font(),
color: gpui::black(),
background_color: None,
underline: None,
strikethrough: None,
}],
None,
);
empty_line_number.width + px(6.) + LINE_NUMBER_RIGHT_MARGIN
} else {
px(0.)
};
(line_number_width, line_number_len)
}
/// First usize is the offset of skipped. /// First usize is the offset of skipped.
fn highlight_lines( fn highlight_lines(
&mut self, &mut self,
@ -670,39 +709,23 @@ impl Element for TextElement {
let (display_text, text_color) = if is_empty { let (display_text, text_color) = if is_empty {
( (
Rope::from(placeholder.as_str()), &Rope::from(placeholder.as_str()),
cx.theme().muted_foreground, cx.theme().muted_foreground,
) )
} else if state.masked { } else if state.masked {
( (
Rope::from("*".repeat(text.chars().count())), &Rope::from("*".repeat(text.chars().count())),
cx.theme().foreground, cx.theme().foreground,
) )
} else { } else {
(text.clone(), cx.theme().foreground) (&text, cx.theme().foreground)
}; };
let text_style = window.text_style(); let text_style = window.text_style();
// Calculate the width of the line numbers // Calculate the width of the line numbers
let empty_line_number = window.text_system().shape_line( let (line_number_width, line_number_len) =
"++++++".into(), Self::layout_line_numbers(&state, &text, font_size, &text_style, window);
font_size,
&[TextRun {
len: 6,
font: style.font(),
color: gpui::black(),
background_color: None,
underline: None,
strikethrough: None,
}],
None,
);
let line_number_width = if state.mode.line_number() {
empty_line_number.width + px(6.) + LINE_NUMBER_RIGHT_MARGIN
} else {
px(0.)
};
let run = TextRun { let run = TextRun {
len: display_text.len(), len: display_text.len(),
@ -881,9 +904,8 @@ impl Element for TextElement {
let state = self.state.read(cx); let state = self.state.read(cx);
let line_numbers = if state.mode.line_number() { let line_numbers = if state.mode.line_number() {
let mut line_numbers = vec![]; let mut line_numbers = vec![];
let run_len = 6;
let other_line_runs = vec![TextRun { let other_line_runs = vec![TextRun {
len: run_len, len: line_number_len,
font: style.font(), font: style.font(),
color: cx.theme().muted_foreground, color: cx.theme().muted_foreground,
background_color: None, background_color: None,
@ -891,7 +913,7 @@ impl Element for TextElement {
strikethrough: None, strikethrough: None,
}]; }];
let current_line_runs = vec![TextRun { let current_line_runs = vec![TextRun {
len: run_len, len: line_number_len,
font: style.font(), font: style.font(),
color: cx.theme().foreground, color: cx.theme().foreground,
background_color: None, background_color: None,
@ -902,7 +924,7 @@ impl Element for TextElement {
// build line numbers // build line numbers
for (ix, line) in last_layout.lines.iter().enumerate() { for (ix, line) in last_layout.lines.iter().enumerate() {
let ix = last_layout.visible_range.start + ix; let ix = last_layout.visible_range.start + ix;
let line_no_text = format!("{:>6}", ix + 1); let line_no = format!("{:>width$}", ix + 1, width = line_number_len).into();
let runs = if current_row == Some(ix) { let runs = if current_row == Some(ix) {
&current_line_runs &current_line_runs
@ -911,12 +933,11 @@ impl Element for TextElement {
}; };
let mut sub_lines: SmallVec<[ShapedLine; 1]> = SmallVec::new(); let mut sub_lines: SmallVec<[ShapedLine; 1]> = SmallVec::new();
sub_lines.push(window.text_system().shape_line( sub_lines.push(
line_no_text.into(), window
font_size, .text_system()
&runs, .shape_line(line_no, font_size, &runs, None),
None, );
));
for _ in 0..line.wrap_boundaries.len() { for _ in 0..line.wrap_boundaries.len() {
sub_lines.push(ShapedLine::default()); sub_lines.push(ShapedLine::default());
} }