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_component::{
highlighter::{HighlightTheme, Language},
highlighter::Language,
input::{InputEvent, InputState, TabSize, TextInput},
resizable::{h_resizable, resizable_panel, ResizableState},
text::{TextView, TextViewStyle},
ActiveTheme as _,
text::TextView,
};
use story::Assets;
@ -48,14 +47,6 @@ impl Example {
impl Render for Example {
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())
.child(
resizable_panel().child(
@ -68,7 +59,7 @@ impl Render for Example {
TextInput::new(&self.input_state)
.h_full()
.p_0()
.appearance(false)
.border_0()
.focus_bordered(false),
),
),
@ -87,12 +78,7 @@ impl Render for Example {
window,
cx,
)
.selectable()
.style(TextViewStyle {
highlight_theme: theme.clone(),
is_dark,
..Default::default()
}),
.selectable(),
),
),
)

View file

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