Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add narrow no-break space support #9604

Merged
merged 3 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions book/src/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,8 @@ Options for rendering whitespace with visible characters. Use `:set whitespace.r

| Key | Description | Default |
|-----|-------------|---------|
| `render` | Whether to render whitespace. May either be `"all"` or `"none"`, or a table with sub-keys `space`, `nbsp`, `tab`, and `newline` | `"none"` |
| `characters` | Literal characters to use when rendering whitespace. Sub-keys may be any of `tab`, `space`, `nbsp`, `newline` or `tabpad` | See example below |
| `render` | Whether to render whitespace. May either be `"all"` or `"none"`, or a table with sub-keys `space`, `nbsp`, `nnbsp`, `tab`, and `newline` | `"none"` |
| `characters` | Literal characters to use when rendering whitespace. Sub-keys may be any of `tab`, `space`, `nbsp`, `nnbsp`, `newline` or `tabpad` | See example below |

Example

Expand All @@ -265,11 +265,14 @@ render = "all"
[editor.whitespace.render]
space = "all"
tab = "all"
nbsp = "none"
nnbsp = "none"
newline = "none"

[editor.whitespace.characters]
space = "·"
nbsp = "⍽"
nnbsp = "␣"
tab = "→"
newline = "⏎"
tabpad = "·" # Tabs will look like "→···" (depending on tab width)
Expand Down
2 changes: 1 addition & 1 deletion helix-core/src/chars.rs
quentin-bettoum marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ pub fn char_is_whitespace(ch: char) -> bool {
'\u{0009}' | // Character Tabulation
'\u{0020}' | // Space
'\u{00A0}' | // No-break Space
'\u{180E}' | // Mongolian Vowel Separator
'\u{202F}' | // Narrow No-break Space
'\u{180E}' | // Mongolian Vowel Separator
'\u{205F}' | // Medium Mathematical Space
'\u{3000}' | // Ideographic Space
'\u{FEFF}' // Zero Width No-break Space
Expand Down
9 changes: 9 additions & 0 deletions helix-term/src/ui/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ pub struct TextRenderer<'a> {
pub indent_guide_style: Style,
pub newline: String,
pub nbsp: String,
pub nnbsp: String,
pub space: String,
pub tab: String,
pub virtual_tab: String,
Expand Down Expand Up @@ -395,6 +396,11 @@ impl<'a> TextRenderer<'a> {
} else {
" ".to_owned()
};
let nnbsp = if ws_render.nnbsp() == WhitespaceRenderValue::All {
ws_chars.nnbsp.into()
} else {
" ".to_owned()
};

let text_style = theme.get("ui.text");

Expand All @@ -405,6 +411,7 @@ impl<'a> TextRenderer<'a> {
indent_guide_char: editor_config.indent_guides.character.into(),
newline,
nbsp,
nnbsp,
space,
tab,
virtual_tab,
Expand Down Expand Up @@ -448,6 +455,7 @@ impl<'a> TextRenderer<'a> {
let width = grapheme.width();
let space = if is_virtual { " " } else { &self.space };
let nbsp = if is_virtual { " " } else { &self.nbsp };
let nnbsp = if is_virtual { " " } else { &self.nnbsp };
let tab = if is_virtual {
&self.virtual_tab
} else {
Expand All @@ -461,6 +469,7 @@ impl<'a> TextRenderer<'a> {
// TODO special rendering for other whitespaces?
Grapheme::Other { ref g } if g == " " => space,
Grapheme::Other { ref g } if g == "\u{00A0}" => nbsp,
Grapheme::Other { ref g } if g == "\u{202F}" => nnbsp,
Grapheme::Other { ref g } => g,
Grapheme::Newline => &self.newline,
};
Expand Down
18 changes: 17 additions & 1 deletion helix-tui/src/widgets/reflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use helix_core::unicode::width::UnicodeWidthStr;
use unicode_segmentation::UnicodeSegmentation;

const NBSP: &str = "\u{00a0}";
const NNBSP: &str = "\u{202f}";

/// A state machine to pack styled symbols into lines.
/// Cannot implement it as Iterator since it yields slices of the internal buffer (need streaming
Expand Down Expand Up @@ -58,7 +59,8 @@ impl<'a, 'b> LineComposer<'a> for WordWrapper<'a, 'b> {
let mut symbols_exhausted = true;
for StyledGrapheme { symbol, style } in &mut self.symbols {
symbols_exhausted = false;
let symbol_whitespace = symbol.chars().all(&char::is_whitespace) && symbol != NBSP;
let symbol_whitespace =
symbol.chars().all(&char::is_whitespace) && symbol != NBSP && symbol != NNBSP;

// Ignore characters wider that the total max width.
if symbol.width() as u16 > self.max_line_width
Expand Down Expand Up @@ -496,6 +498,20 @@ mod test {
assert_eq!(word_wrapper_space, vec!["AAAAAAAAAAAAAAA AAAA", "AAA",]);
}

#[test]
fn line_composer_word_wrapper_nnbsp() {
let width = 20;
let text = "AAAAAAAAAAAAAAA AAAA\u{202f}AAA";
let (word_wrapper, _) = run_composer(Composer::WordWrapper { trim: true }, text, width);
assert_eq!(word_wrapper, vec!["AAAAAAAAAAAAAAA", "AAAA\u{202f}AAA",]);

// Ensure that if the character was a regular space, it would be wrapped differently.
let text_space = text.replace('\u{202f}', " ");
let (word_wrapper_space, _) =
run_composer(Composer::WordWrapper { trim: true }, &text_space, width);
assert_eq!(word_wrapper_space, vec!["AAAAAAAAAAAAAAA AAAA", "AAA",]);
}

#[test]
fn line_composer_word_wrapper_preserve_indentation() {
let width = 20;
Expand Down
11 changes: 11 additions & 0 deletions helix-view/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ pub enum WhitespaceRender {
default: Option<WhitespaceRenderValue>,
space: Option<WhitespaceRenderValue>,
nbsp: Option<WhitespaceRenderValue>,
nnbsp: Option<WhitespaceRenderValue>,
tab: Option<WhitespaceRenderValue>,
newline: Option<WhitespaceRenderValue>,
},
Expand Down Expand Up @@ -707,6 +708,14 @@ impl WhitespaceRender {
}
}
}
pub fn nnbsp(&self) -> WhitespaceRenderValue {
match *self {
Self::Basic(val) => val,
Self::Specific { default, nnbsp, .. } => {
nnbsp.or(default).unwrap_or(WhitespaceRenderValue::None)
}
}
}
pub fn tab(&self) -> WhitespaceRenderValue {
match *self {
Self::Basic(val) => val,
Expand All @@ -730,6 +739,7 @@ impl WhitespaceRender {
pub struct WhitespaceCharacters {
pub space: char,
pub nbsp: char,
pub nnbsp: char,
pub tab: char,
pub tabpad: char,
pub newline: char,
Expand All @@ -740,6 +750,7 @@ impl Default for WhitespaceCharacters {
Self {
space: '·', // U+00B7
nbsp: '⍽', // U+237D
nnbsp: '␣', // U+2423
tab: '→', // U+2192
newline: '⏎', // U+23CE
tabpad: ' ',
Expand Down
Loading