Skip to content

Commit 98f1cca

Browse files
committed
feat(view): render soft-wrap indicators in the gutter
Moves the soft-wrap indicator into the gutter to keep the text flush and take an advantage of otherwise empty space. Fixes: #14802
1 parent 68c7e87 commit 98f1cca

5 files changed

Lines changed: 26 additions & 43 deletions

File tree

helix-core/src/doc_formatter.rs

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ pub struct TextFormat {
148148
pub tab_width: u16,
149149
pub max_wrap: u16,
150150
pub max_indent_retain: u16,
151-
pub wrap_indicator: Box<str>,
152151
pub wrap_indicator_highlight: Option<Highlight>,
153152
pub viewport_width: u16,
154153
pub soft_wrap_at_text_width: bool,
@@ -162,7 +161,6 @@ impl Default for TextFormat {
162161
tab_width: 4,
163162
max_wrap: 3,
164163
max_indent_retain: 4,
165-
wrap_indicator: Box::from(" "),
166164
viewport_width: 17,
167165
wrap_indicator_highlight: None,
168166
soft_wrap_at_text_width: false,
@@ -311,25 +309,8 @@ impl<'t> DocumentFormatter<'t> {
311309
.virtual_lines_at(self.char_pos, self.visual_pos, self.line_pos);
312310
self.visual_pos.col = indent_carry_over as usize;
313311
self.visual_pos.row += 1 + virtual_lines;
314-
let mut i = 0;
315312
let mut word_width = 0;
316-
let wrap_indicator = UnicodeSegmentation::graphemes(&*self.text_fmt.wrap_indicator, true)
317-
.map(|g| {
318-
i += 1;
319-
let grapheme = GraphemeWithSource::new(
320-
g.into(),
321-
self.visual_pos.col + word_width,
322-
self.text_fmt.tab_width,
323-
GraphemeSource::VirtualText {
324-
highlight: self.text_fmt.wrap_indicator_highlight,
325-
},
326-
);
327-
word_width += grapheme.width();
328-
grapheme
329-
});
330-
self.word_buf.splice(0..0, wrap_indicator);
331-
332-
for grapheme in &mut self.word_buf[i..] {
313+
for grapheme in &mut self.word_buf {
333314
let visual_x = self.visual_pos.col + word_width;
334315
grapheme
335316
.grapheme

helix-core/src/doc_formatter/test.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ impl TextFormat {
88
tab_width: 2,
99
max_wrap: 3,
1010
max_indent_retain: 4,
11-
wrap_indicator: ".".into(),
1211
wrap_indicator_highlight: None,
1312
// use a prime number to allow lining up too often with repeat
1413
viewport_width: 17,
@@ -59,11 +58,11 @@ fn softwrap_text(text: &str) -> String {
5958
fn basic_softwrap() {
6059
assert_eq!(
6160
softwrap_text(&"foo ".repeat(10)),
62-
"foo foo foo foo \n.foo foo foo foo \n.foo foo "
61+
"foo foo foo foo \nfoo foo foo foo \nfoo foo "
6362
);
6463
assert_eq!(
6564
softwrap_text(&"fooo ".repeat(10)),
66-
"fooo fooo fooo \n.fooo fooo fooo \n.fooo fooo fooo \n.fooo "
65+
"fooo fooo fooo \nfooo fooo fooo \nfooo fooo fooo \nfooo "
6766
);
6867

6968
// check that we don't wrap unnecessarily
@@ -74,39 +73,39 @@ fn basic_softwrap() {
7473
fn softwrap_indentation() {
7574
assert_eq!(
7675
softwrap_text("\t\tfoo1 foo2 foo3 foo4 foo5 foo6\n"),
77-
" foo1 foo2 \n.....foo3 foo4 \n.....foo5 foo6 \n "
76+
" foo1 foo2 \n....foo3 foo4 \n....foo5 foo6 \n "
7877
);
7978
assert_eq!(
8079
softwrap_text("\t\t\tfoo1 foo2 foo3 foo4 foo5 foo6\n"),
81-
" foo1 foo2 \n.foo3 foo4 foo5 \n.foo6 \n "
80+
" foo1 foo2 \nfoo3 foo4 foo5 \nfoo6 \n "
8281
);
8382
}
8483

8584
#[test]
8685
fn long_word_softwrap() {
8786
assert_eq!(
88-
softwrap_text("\t\txxxx1xxxx2xxxx3xxxx4xxxx5xxxx6xxxx7xxxx8xxxx9xxx\n"),
89-
" xxxx1xxxx2xxx\n.....x3xxxx4xxxx5\n.....xxxx6xxxx7xx\n.....xx8xxxx9xxx \n "
87+
softwrap_text("\t\txxxx1xxxx2xxxx3xxxx4xxxxx5xxxx6xxxx7xxx8xxxx9xxx\n"),
88+
" xxxx1xxxx2xxx\n....x3xxxx4xxxxx5\n....xxxx6xxxx7xxx\n....8xxxx9xxx \n "
9089
);
9190
assert_eq!(
9291
softwrap_text("xxxxxxxx1xxxx2xxx\n"),
93-
"xxxxxxxx1xxxx2xxx\n. \n "
92+
"xxxxxxxx1xxxx2xxx\n \n "
9493
);
9594
assert_eq!(
96-
softwrap_text("\t\txxxx1xxxx 2xxxx3xxxx4xxxx5xxxx6xxxx7xxxx8xxxx9xxx\n"),
97-
" xxxx1xxxx \n.....2xxxx3xxxx4x\n.....xxx5xxxx6xxx\n.....x7xxxx8xxxx9\n.....xxx \n "
95+
softwrap_text("\t\txxxx1xxxx 2xxxx3xxxx4xxxx5xxxx6xxxx7xxxx8xxxxxxx9xxx\n"),
96+
" xxxx1xxxx \n....2xxxx3xxxx4xx\n....xx5xxxx6xxxx7\n....xxxx8xxxxxxx9\n....xxx \n "
9897
);
9998
assert_eq!(
100-
softwrap_text("\t\txxxx1xxx 2xxxx3xxxx4xxxx5xxxx6xxxx7xxxx8xxxx9xxx\n"),
101-
" xxxx1xxx 2xxx\n.....x3xxxx4xxxx5\n.....xxxx6xxxx7xx\n.....xx8xxxx9xxx \n "
99+
softwrap_text("\t\txxxx1xxx 2xxxx3xxxx4xxxxx5xxxx6xxxx7xxxxx8xxxx9xx\n"),
100+
" xxxx1xxx 2xxx\n....x3xxxx4xxxxx5\n....xxxx6xxxx7xxx\n....xx8xxxx9xx \n "
102101
);
103102
}
104103

105104
#[test]
106105
fn softwrap_multichar_grapheme() {
107106
assert_eq!(
108107
softwrap_text("xxxx xxxx xxx a\u{0301}bc\n"),
109-
"xxxx xxxx xxx \n.ábc \n "
108+
"xxxx xxxx xxx \nábc \n "
110109
)
111110
}
112111

@@ -158,7 +157,7 @@ fn overlay() {
158157
Overlay::new(16, "X"),
159158
]
160159
),
161-
"fo f o foo \n.foo Xoo foo foo \n.foo foo foo "
160+
"fo f o foo \nfoo Xoo foo foo \nfoo foo foo "
162161
);
163162
}
164163

@@ -184,7 +183,7 @@ fn annotation() {
184183
true,
185184
&[InlineAnnotation::new(0, "foo ")]
186185
),
187-
"foo foo foo foo \n.foo foo foo foo \n.foo foo foo "
186+
"foo foo foo foo \nfoo foo foo foo \nfoo foo foo "
188187
);
189188
}
190189

helix-view/src/annotations/diagnostics.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ impl InlineDiagnosticsConfig {
9898
tab_width: 4,
9999
max_wrap: self.max_wrap.min(width / 4),
100100
max_indent_retain: 0,
101-
wrap_indicator: "".into(),
102101
wrap_indicator_highlight: None,
103102
viewport_width: width,
104103
soft_wrap_at_text_width: true,

helix-view/src/document.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2258,10 +2258,6 @@ impl Document {
22582258
.and_then(|soft_wrap| soft_wrap.max_indent_retain)
22592259
.or(editor_soft_wrap.max_indent_retain)
22602260
.unwrap_or(40);
2261-
let wrap_indicator = language_soft_wrap
2262-
.and_then(|soft_wrap| soft_wrap.wrap_indicator.clone())
2263-
.or_else(|| config.soft_wrap.wrap_indicator.clone())
2264-
.unwrap_or_else(|| "↪ ".into());
22652261
let tab_width = self.tab_width() as u16;
22662262
TextFormat {
22672263
soft_wrap: enable_soft_wrap && viewport_width > 10,
@@ -2271,7 +2267,6 @@ impl Document {
22712267
// avoid spinning forever when the window manager
22722268
// sets the size to something tiny
22732269
viewport_width,
2274-
wrap_indicator: wrap_indicator.into_boxed_str(),
22752270
wrap_indicator_highlight: theme
22762271
.and_then(|theme| theme.find_highlight("ui.virtual.wrap")),
22772272
soft_wrap_at_text_width,

helix-view/src/gutter.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,15 @@ pub fn line_numbers<'doc>(
163163
.char_to_line(doc.selection(view.id).primary().cursor(text));
164164

165165
let line_number = editor.config().line_number;
166+
let wrap_indicator = editor
167+
.config()
168+
.soft_wrap
169+
.wrap_indicator
170+
.as_ref()
171+
.map_or_else(
172+
|| "↪".into(),
173+
|indicator| indicator.clone().chars().take(width).collect::<String>(),
174+
);
166175
let mode = editor.mode;
167176

168177
Box::new(
@@ -193,10 +202,10 @@ pub fn line_numbers<'doc>(
193202
if first_visual_line {
194203
write!(out, "{:>1$}", display_num, width).unwrap();
195204
} else {
196-
write!(out, "{:>1$}", " ", width).unwrap();
205+
write!(out, "{:>1$}", wrap_indicator, width).unwrap();
197206
}
198207

199-
first_visual_line.then_some(style)
208+
Some(style)
200209
}
201210
},
202211
)

0 commit comments

Comments
 (0)