Add narrow no-break space support (#9604)
This commit is contained in:
parent
2d9e336f64
commit
614a744d24
4 changed files with 42 additions and 3 deletions
|
@ -255,8 +255,8 @@ Options for rendering whitespace with visible characters. Use `:set whitespace.r
|
||||||
|
|
||||||
| Key | Description | Default |
|
| 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"` |
|
| `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`, `newline` or `tabpad` | See example below |
|
| `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
|
Example
|
||||||
|
|
||||||
|
@ -267,11 +267,14 @@ render = "all"
|
||||||
[editor.whitespace.render]
|
[editor.whitespace.render]
|
||||||
space = "all"
|
space = "all"
|
||||||
tab = "all"
|
tab = "all"
|
||||||
|
nbsp = "none"
|
||||||
|
nnbsp = "none"
|
||||||
newline = "none"
|
newline = "none"
|
||||||
|
|
||||||
[editor.whitespace.characters]
|
[editor.whitespace.characters]
|
||||||
space = "·"
|
space = "·"
|
||||||
nbsp = "⍽"
|
nbsp = "⍽"
|
||||||
|
nnbsp = "␣"
|
||||||
tab = "→"
|
tab = "→"
|
||||||
newline = "⏎"
|
newline = "⏎"
|
||||||
tabpad = "·" # Tabs will look like "→···" (depending on tab width)
|
tabpad = "·" # Tabs will look like "→···" (depending on tab width)
|
||||||
|
|
|
@ -341,6 +341,7 @@ pub struct TextRenderer<'a> {
|
||||||
pub indent_guide_style: Style,
|
pub indent_guide_style: Style,
|
||||||
pub newline: String,
|
pub newline: String,
|
||||||
pub nbsp: String,
|
pub nbsp: String,
|
||||||
|
pub nnbsp: String,
|
||||||
pub space: String,
|
pub space: String,
|
||||||
pub tab: String,
|
pub tab: String,
|
||||||
pub virtual_tab: String,
|
pub virtual_tab: String,
|
||||||
|
@ -395,6 +396,11 @@ impl<'a> TextRenderer<'a> {
|
||||||
} else {
|
} else {
|
||||||
" ".to_owned()
|
" ".to_owned()
|
||||||
};
|
};
|
||||||
|
let nnbsp = if ws_render.nnbsp() == WhitespaceRenderValue::All {
|
||||||
|
ws_chars.nnbsp.into()
|
||||||
|
} else {
|
||||||
|
" ".to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
let text_style = theme.get("ui.text");
|
let text_style = theme.get("ui.text");
|
||||||
|
|
||||||
|
@ -405,6 +411,7 @@ impl<'a> TextRenderer<'a> {
|
||||||
indent_guide_char: editor_config.indent_guides.character.into(),
|
indent_guide_char: editor_config.indent_guides.character.into(),
|
||||||
newline,
|
newline,
|
||||||
nbsp,
|
nbsp,
|
||||||
|
nnbsp,
|
||||||
space,
|
space,
|
||||||
tab,
|
tab,
|
||||||
virtual_tab,
|
virtual_tab,
|
||||||
|
@ -448,6 +455,7 @@ impl<'a> TextRenderer<'a> {
|
||||||
let width = grapheme.width();
|
let width = grapheme.width();
|
||||||
let space = if is_virtual { " " } else { &self.space };
|
let space = if is_virtual { " " } else { &self.space };
|
||||||
let nbsp = if is_virtual { " " } else { &self.nbsp };
|
let nbsp = if is_virtual { " " } else { &self.nbsp };
|
||||||
|
let nnbsp = if is_virtual { " " } else { &self.nnbsp };
|
||||||
let tab = if is_virtual {
|
let tab = if is_virtual {
|
||||||
&self.virtual_tab
|
&self.virtual_tab
|
||||||
} else {
|
} else {
|
||||||
|
@ -461,6 +469,7 @@ impl<'a> TextRenderer<'a> {
|
||||||
// TODO special rendering for other whitespaces?
|
// TODO special rendering for other whitespaces?
|
||||||
Grapheme::Other { ref g } if g == " " => space,
|
Grapheme::Other { ref g } if g == " " => space,
|
||||||
Grapheme::Other { ref g } if g == "\u{00A0}" => nbsp,
|
Grapheme::Other { ref g } if g == "\u{00A0}" => nbsp,
|
||||||
|
Grapheme::Other { ref g } if g == "\u{202F}" => nnbsp,
|
||||||
Grapheme::Other { ref g } => g,
|
Grapheme::Other { ref g } => g,
|
||||||
Grapheme::Newline => &self.newline,
|
Grapheme::Newline => &self.newline,
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,6 +4,7 @@ use helix_core::unicode::width::UnicodeWidthStr;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
const NBSP: &str = "\u{00a0}";
|
const NBSP: &str = "\u{00a0}";
|
||||||
|
const NNBSP: &str = "\u{202f}";
|
||||||
|
|
||||||
/// A state machine to pack styled symbols into lines.
|
/// A state machine to pack styled symbols into lines.
|
||||||
/// Cannot implement it as Iterator since it yields slices of the internal buffer (need streaming
|
/// Cannot implement it as Iterator since it yields slices of the internal buffer (need streaming
|
||||||
|
@ -58,7 +59,8 @@ impl<'a, 'b> LineComposer<'a> for WordWrapper<'a, 'b> {
|
||||||
let mut symbols_exhausted = true;
|
let mut symbols_exhausted = true;
|
||||||
for StyledGrapheme { symbol, style } in &mut self.symbols {
|
for StyledGrapheme { symbol, style } in &mut self.symbols {
|
||||||
symbols_exhausted = false;
|
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.
|
// Ignore characters wider that the total max width.
|
||||||
if symbol.width() as u16 > self.max_line_width
|
if symbol.width() as u16 > self.max_line_width
|
||||||
|
@ -496,6 +498,20 @@ mod test {
|
||||||
assert_eq!(word_wrapper_space, vec!["AAAAAAAAAAAAAAA AAAA", "AAA",]);
|
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]
|
#[test]
|
||||||
fn line_composer_word_wrapper_preserve_indentation() {
|
fn line_composer_word_wrapper_preserve_indentation() {
|
||||||
let width = 20;
|
let width = 20;
|
||||||
|
|
|
@ -702,6 +702,7 @@ pub enum WhitespaceRender {
|
||||||
default: Option<WhitespaceRenderValue>,
|
default: Option<WhitespaceRenderValue>,
|
||||||
space: Option<WhitespaceRenderValue>,
|
space: Option<WhitespaceRenderValue>,
|
||||||
nbsp: Option<WhitespaceRenderValue>,
|
nbsp: Option<WhitespaceRenderValue>,
|
||||||
|
nnbsp: Option<WhitespaceRenderValue>,
|
||||||
tab: Option<WhitespaceRenderValue>,
|
tab: Option<WhitespaceRenderValue>,
|
||||||
newline: Option<WhitespaceRenderValue>,
|
newline: Option<WhitespaceRenderValue>,
|
||||||
},
|
},
|
||||||
|
@ -733,6 +734,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 {
|
pub fn tab(&self) -> WhitespaceRenderValue {
|
||||||
match *self {
|
match *self {
|
||||||
Self::Basic(val) => val,
|
Self::Basic(val) => val,
|
||||||
|
@ -756,6 +765,7 @@ impl WhitespaceRender {
|
||||||
pub struct WhitespaceCharacters {
|
pub struct WhitespaceCharacters {
|
||||||
pub space: char,
|
pub space: char,
|
||||||
pub nbsp: char,
|
pub nbsp: char,
|
||||||
|
pub nnbsp: char,
|
||||||
pub tab: char,
|
pub tab: char,
|
||||||
pub tabpad: char,
|
pub tabpad: char,
|
||||||
pub newline: char,
|
pub newline: char,
|
||||||
|
@ -766,6 +776,7 @@ impl Default for WhitespaceCharacters {
|
||||||
Self {
|
Self {
|
||||||
space: '·', // U+00B7
|
space: '·', // U+00B7
|
||||||
nbsp: '⍽', // U+237D
|
nbsp: '⍽', // U+237D
|
||||||
|
nnbsp: '␣', // U+2423
|
||||||
tab: '→', // U+2192
|
tab: '→', // U+2192
|
||||||
newline: '⏎', // U+23CE
|
newline: '⏎', // U+23CE
|
||||||
tabpad: ' ',
|
tabpad: ' ',
|
||||||
|
|
Loading…
Add table
Reference in a new issue