Skip to content

Commit 6939d87

Browse files
committed
Add config option "soft-wrap.indicator-on-gutter"
1 parent 80530d8 commit 6939d87

7 files changed

Lines changed: 73 additions & 29 deletions

File tree

helix-core/src/doc_formatter.rs

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,9 @@ pub struct TextFormat {
149149
pub max_wrap: u16,
150150
pub max_indent_retain: u16,
151151
pub wrap_indicator: Box<str>,
152+
pub wrap_indicator_width: u16,
152153
pub wrap_indicator_highlight: Option<Highlight>,
154+
pub wrap_indicator_on_gutter: bool,
153155
pub viewport_width: u16,
154156
pub soft_wrap_at_text_width: bool,
155157
}
@@ -163,6 +165,8 @@ impl Default for TextFormat {
163165
max_wrap: 3,
164166
max_indent_retain: 4,
165167
wrap_indicator: Box::from(" "),
168+
wrap_indicator_width: 1,
169+
wrap_indicator_on_gutter: false,
166170
viewport_width: 17,
167171
wrap_indicator_highlight: None,
168172
soft_wrap_at_text_width: false,
@@ -313,21 +317,24 @@ impl<'t> DocumentFormatter<'t> {
313317
self.visual_pos.row += 1 + virtual_lines;
314318
let mut i = 0;
315319
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);
320+
321+
if !self.text_fmt.wrap_indicator_on_gutter {
322+
let wrap_indicator =
323+
UnicodeSegmentation::graphemes(&*self.text_fmt.wrap_indicator, true).map(|g| {
324+
i += 1;
325+
let grapheme = GraphemeWithSource::new(
326+
g.into(),
327+
self.visual_pos.col + word_width,
328+
self.text_fmt.tab_width,
329+
GraphemeSource::VirtualText {
330+
highlight: self.text_fmt.wrap_indicator_highlight,
331+
},
332+
);
333+
word_width += grapheme.width();
334+
grapheme
335+
});
336+
self.word_buf.splice(0..0, wrap_indicator);
337+
}
331338

332339
for grapheme in &mut self.word_buf[i..] {
333340
let visual_x = self.visual_pos.col + word_width;

helix-core/src/doc_formatter/test.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ impl TextFormat {
99
max_wrap: 3,
1010
max_indent_retain: 4,
1111
wrap_indicator: ".".into(),
12+
wrap_indicator_width: 1,
13+
wrap_indicator_on_gutter: false,
1214
wrap_indicator_highlight: None,
1315
// use a prime number to allow lining up too often with repeat
1416
viewport_width: 17,

helix-core/src/syntax/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,8 @@ pub struct SoftWrap {
576576
///
577577
/// Defaults to ↪
578578
pub wrap_indicator: Option<String>,
579+
/// Show the indicator on the `line-numbers` gutter instead of as part of the document
580+
pub indicator_on_gutter: Option<bool>,
579581
/// Softwrap at `text_width` instead of viewport width if it is shorter
580582
pub wrap_at_text_width: Option<bool>,
581583
}

helix-term/src/ui/editor.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -647,9 +647,9 @@ impl EditorView {
647647
pub fn render_gutter<'d>(
648648
editor: &'d Editor,
649649
doc: &'d Document,
650-
view: &View,
650+
view: &'d View,
651651
viewport: Rect,
652-
theme: &Theme,
652+
theme: &'d Theme,
653653
is_focused: bool,
654654
decoration_manager: &mut DecorationManager<'d>,
655655
) {
@@ -674,7 +674,6 @@ impl EditorView {
674674
let mut text = String::with_capacity(width);
675675
let cursors = cursors.clone();
676676
let gutter_decoration = move |renderer: &mut TextRenderer, pos: LinePos| {
677-
// TODO handle softwrap in gutters
678677
let selected = cursors.contains(&pos.doc_line);
679678
let x = viewport.x + offset;
680679
let y = pos.visual_line;

helix-view/src/annotations/diagnostics.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ impl InlineDiagnosticsConfig {
9999
max_wrap: self.max_wrap.min(width / 4),
100100
max_indent_retain: 0,
101101
wrap_indicator: "".into(),
102+
wrap_indicator_width: 0,
103+
wrap_indicator_on_gutter: false,
102104
wrap_indicator_highlight: None,
103105
viewport_width: width,
104106
soft_wrap_at_text_width: true,

helix-view/src/document.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use helix_core::encoding::Encoding;
1212
use helix_core::snippets::{ActiveSnippet, SnippetRenderCtx};
1313
use helix_core::syntax::config::LanguageServerFeature;
1414
use helix_core::text_annotations::{InlineAnnotation, Overlay};
15+
use helix_core::unicode::segmentation::UnicodeSegmentation;
1516
use helix_event::TaskController;
1617
use helix_lsp::util::lsp_pos_to_pos;
1718
use helix_stdx::faccess::{copy_metadata, readonly};
@@ -2258,11 +2259,25 @@ impl Document {
22582259
.and_then(|soft_wrap| soft_wrap.max_indent_retain)
22592260
.or(editor_soft_wrap.max_indent_retain)
22602261
.unwrap_or(40);
2262+
let wrap_indicator_on_gutter = language_soft_wrap
2263+
.and_then(|soft_wrap| soft_wrap.indicator_on_gutter)
2264+
.or(config.soft_wrap.indicator_on_gutter)
2265+
.unwrap_or(false);
22612266
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());
2267+
.and_then(|soft_wrap| soft_wrap.wrap_indicator.as_deref())
2268+
.or_else(|| config.soft_wrap.wrap_indicator.as_deref())
2269+
.unwrap_or_else(|| "↪ ");
2270+
22652271
let tab_width = self.tab_width() as u16;
2272+
2273+
let wrap_indicator = if wrap_indicator_on_gutter {
2274+
wrap_indicator.trim_end_matches(" ")
2275+
} else {
2276+
&wrap_indicator
2277+
};
2278+
let wrap_indicator_width =
2279+
UnicodeSegmentation::graphemes(wrap_indicator, true).count() as u16;
2280+
22662281
TextFormat {
22672282
soft_wrap: enable_soft_wrap && viewport_width > 10,
22682283
tab_width,
@@ -2271,7 +2286,9 @@ impl Document {
22712286
// avoid spinning forever when the window manager
22722287
// sets the size to something tiny
22732288
viewport_width,
2274-
wrap_indicator: wrap_indicator.into_boxed_str(),
2289+
wrap_indicator: wrap_indicator.into(),
2290+
wrap_indicator_width,
2291+
wrap_indicator_on_gutter,
22752292
wrap_indicator_highlight: theme
22762293
.and_then(|theme| theme.find_highlight("ui.virtual.wrap")),
22772294
soft_wrap_at_text_width,

helix-view/src/gutter.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ impl GutterType {
2121
self,
2222
editor: &'doc Editor,
2323
doc: &'doc Document,
24-
view: &View,
25-
theme: &Theme,
24+
view: &'doc View,
25+
theme: &'doc Theme,
2626
is_focused: bool,
2727
) -> GutterFn<'doc> {
2828
match self {
@@ -142,8 +142,8 @@ pub fn diff<'doc>(
142142
pub fn line_numbers<'doc>(
143143
editor: &'doc Editor,
144144
doc: &'doc Document,
145-
view: &View,
146-
theme: &Theme,
145+
view: &'doc View,
146+
theme: &'doc Theme,
147147
is_focused: bool,
148148
) -> GutterFn<'doc> {
149149
let text = doc.text().slice(..);
@@ -190,13 +190,25 @@ pub fn line_numbers<'doc>(
190190
linenr
191191
};
192192

193+
let text_fmt = doc.text_format(view.inner_width(doc), Some(theme));
194+
195+
// TODO: the output would look weird if wrap indicator on gutter is enabled,
196+
// a long wrap indicator is used and there are no lines being wrapped
197+
let width = if text_fmt.wrap_indicator_on_gutter {
198+
width.max(text_fmt.wrap_indicator_width.into())
199+
} else {
200+
width
201+
};
202+
193203
if first_visual_line {
194204
write!(out, "{:>1$}", display_num, width).unwrap();
205+
} else if text_fmt.wrap_indicator_on_gutter {
206+
write!(out, "{:>1$}", text_fmt.wrap_indicator, width).unwrap();
195207
} else {
196208
write!(out, "{:>1$}", " ", width).unwrap();
197209
}
198210

199-
first_visual_line.then_some(style)
211+
Some(style)
200212
}
201213
},
202214
)
@@ -205,8 +217,11 @@ pub fn line_numbers<'doc>(
205217
/// The width of a "line-numbers" gutter
206218
///
207219
/// The width of the gutter depends on the number of lines in the document,
208-
/// whether there is content on the last line (the `~` line), and the
209-
/// `editor.gutters.line-numbers.min-width` settings.
220+
/// whether there is content on the last line (the `~` line), the
221+
/// `editor.gutters.line-numbers.min-width` settings and the width of
222+
/// the wrap indicator (if `soft-wrap` is enabled and the indicator is
223+
/// set to be displayed on the gutter). The wrap indicator isn't taken
224+
/// into account here, as that would lead to infinite recursion.
210225
fn line_numbers_width(view: &View, doc: &Document) -> usize {
211226
let text = doc.text();
212227
let last_line = text.len_lines().saturating_sub(1);

0 commit comments

Comments
 (0)