Skip to content

Commit 57af45d

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 253e619 commit 57af45d

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
@@ -147,7 +147,6 @@ pub struct TextFormat {
147147
pub tab_width: u16,
148148
pub max_wrap: u16,
149149
pub max_indent_retain: u16,
150-
pub wrap_indicator: Box<str>,
151150
pub wrap_indicator_highlight: Option<Highlight>,
152151
pub viewport_width: u16,
153152
pub soft_wrap_at_text_width: bool,
@@ -161,7 +160,6 @@ impl Default for TextFormat {
161160
tab_width: 4,
162161
max_wrap: 3,
163162
max_indent_retain: 4,
164-
wrap_indicator: Box::from(" "),
165163
viewport_width: 17,
166164
wrap_indicator_highlight: None,
167165
soft_wrap_at_text_width: false,
@@ -310,25 +308,8 @@ impl<'t> DocumentFormatter<'t> {
310308
.virtual_lines_at(self.char_pos, self.visual_pos, self.line_pos);
311309
self.visual_pos.col = indent_carry_over as usize;
312310
self.visual_pos.row += 1 + virtual_lines;
313-
let mut i = 0;
314311
let mut word_width = 0;
315-
let wrap_indicator = UnicodeSegmentation::graphemes(&*self.text_fmt.wrap_indicator, true)
316-
.map(|g| {
317-
i += 1;
318-
let grapheme = GraphemeWithSource::new(
319-
g.into(),
320-
self.visual_pos.col + word_width,
321-
self.text_fmt.tab_width,
322-
GraphemeSource::VirtualText {
323-
highlight: self.text_fmt.wrap_indicator_highlight,
324-
},
325-
);
326-
word_width += grapheme.width();
327-
grapheme
328-
});
329-
self.word_buf.splice(0..0, wrap_indicator);
330-
331-
for grapheme in &mut self.word_buf[i..] {
312+
for grapheme in &mut self.word_buf {
332313
let visual_x = self.visual_pos.col + word_width;
333314
grapheme
334315
.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
@@ -2315,10 +2315,6 @@ impl Document {
23152315
.and_then(|soft_wrap| soft_wrap.max_indent_retain)
23162316
.or(editor_soft_wrap.max_indent_retain)
23172317
.unwrap_or(40);
2318-
let wrap_indicator = language_soft_wrap
2319-
.and_then(|soft_wrap| soft_wrap.wrap_indicator.clone())
2320-
.or_else(|| config.soft_wrap.wrap_indicator.clone())
2321-
.unwrap_or_else(|| "↪ ".into());
23222318
let tab_width = self.tab_width() as u16;
23232319
TextFormat {
23242320
soft_wrap: enable_soft_wrap && viewport_width > 10,
@@ -2328,7 +2324,6 @@ impl Document {
23282324
// avoid spinning forever when the window manager
23292325
// sets the size to something tiny
23302326
viewport_width,
2331-
wrap_indicator: wrap_indicator.into_boxed_str(),
23322327
wrap_indicator_highlight: theme
23332328
.and_then(|theme| theme.find_highlight("ui.virtual.wrap")),
23342329
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)