Compare commits

..

2 Commits

Author SHA1 Message Date
RoloEdits 27d8bd20bf
Merge 9f34b62a1e into 4281228da3 2025-07-25 04:02:20 +00:00
Rolo 9f34b62a1e feat: add support for basic icons 2025-07-24 20:55:57 -07:00
11 changed files with 224 additions and 163 deletions

View File

@ -3215,9 +3215,9 @@ fn buffer_picker(cx: &mut Context) {
spans.push(icon.to_span_with(|icon| format!("{icon} "))); spans.push(icon.to_span_with(|icon| format!("{icon} ")));
} }
spans.push(name.to_string().into()); spans.push(Span::raw(name.to_string()));
Spans::from(spans).into() Cell::from(Spans::from(spans))
}), }),
]; ];
let picker = Picker::new(columns, 2, items, (), |cx, meta, action| { let picker = Picker::new(columns, 2, items, (), |cx, meta, action| {
@ -3281,6 +3281,7 @@ fn jumplist_picker(cx: &mut Context) {
.as_deref() .as_deref()
.and_then(Path::to_str) .and_then(Path::to_str)
.unwrap_or(SCRATCH_BUFFER_NAME); .unwrap_or(SCRATCH_BUFFER_NAME);
let icons = ICONS.load(); let icons = ICONS.load();
let mut spans = Vec::with_capacity(2); let mut spans = Vec::with_capacity(2);
@ -3289,9 +3290,9 @@ fn jumplist_picker(cx: &mut Context) {
spans.push(icon.to_span_with(|icon| format!("{icon} "))); spans.push(icon.to_span_with(|icon| format!("{icon} ")));
} }
spans.push(name.to_string().into()); spans.push(Span::raw(name.to_string()));
Spans::from(spans).into() Cell::from(Spans::from(spans))
}), }),
ui::PickerColumn::new("flags", |item: &JumpMeta, _| { ui::PickerColumn::new("flags", |item: &JumpMeta, _| {
let mut flags = Vec::new(); let mut flags = Vec::new();

View File

@ -247,6 +247,7 @@ fn diag_picker(
"severity", "severity",
|item: &PickerDiagnostic, styles: &DiagnosticStyles| { |item: &PickerDiagnostic, styles: &DiagnosticStyles| {
let icons = ICONS.load(); let icons = ICONS.load();
match item.diag.severity { match item.diag.severity {
Some(DiagnosticSeverity::HINT) => { Some(DiagnosticSeverity::HINT) => {
Span::styled(format!("{} HINT", icons.diagnostic().hint()), styles.hint) Span::styled(format!("{} HINT", icons.diagnostic().hint()), styles.hint)
@ -415,9 +416,10 @@ pub fn symbol_picker(cx: &mut Context) {
let call = move |_editor: &mut Editor, compositor: &mut Compositor| { let call = move |_editor: &mut Editor, compositor: &mut Compositor| {
let columns = [ let columns = [
ui::PickerColumn::new("kind", |item: &SymbolInformationItem, _| { ui::PickerColumn::new("kind", |item: &SymbolInformationItem, _| {
let icons = ICONS.load();
let name = display_symbol_kind(item.symbol.kind); let name = display_symbol_kind(item.symbol.kind);
let icons = ICONS.load();
if let Some(icon) = icons.kind().get(name) { if let Some(icon) = icons.kind().get(name) {
icon.to_span_with(|icon| format!("{icon} {name}")).into() icon.to_span_with(|icon| format!("{icon} {name}")).into()
} else { } else {

View File

@ -107,8 +107,8 @@ impl menu::Item for CompletionItem {
}; };
let icons = ICONS.load(); let icons = ICONS.load();
let name = &kind.0[0].content;
let name = &kind.0[0].content;
let is_folder = kind.0[0].content == "folder"; let is_folder = kind.0[0].content == "folder";
if let Some(icon) = icons.kind().get(name) { if let Some(icon) = icons.kind().get(name) {

View File

@ -9,6 +9,7 @@ use helix_core::{visual_offset_from_block, Position, RopeSlice};
use helix_stdx::rope::RopeSliceExt; use helix_stdx::rope::RopeSliceExt;
use helix_view::editor::{WhitespaceConfig, WhitespaceRenderValue}; use helix_view::editor::{WhitespaceConfig, WhitespaceRenderValue};
use helix_view::graphics::Rect; use helix_view::graphics::Rect;
use helix_view::icons::ICONS;
use helix_view::theme::Style; use helix_view::theme::Style;
use helix_view::view::ViewPosition; use helix_view::view::ViewPosition;
use helix_view::{Document, Theme}; use helix_view::{Document, Theme};
@ -206,38 +207,41 @@ impl<'a> TextRenderer<'a> {
viewport: Rect, viewport: Rect,
) -> TextRenderer<'a> { ) -> TextRenderer<'a> {
let editor_config = doc.config.load(); let editor_config = doc.config.load();
let WhitespaceConfig {
render: ws_render, let WhitespaceConfig { render } = &editor_config.whitespace;
characters: ws_chars,
} = &editor_config.whitespace;
let tab_width = doc.tab_width(); let tab_width = doc.tab_width();
let tab = if ws_render.tab() == WhitespaceRenderValue::All {
std::iter::once(ws_chars.tab) let icons = ICONS.load();
.chain(std::iter::repeat(ws_chars.tabpad).take(tab_width - 1))
let whitespace = icons.ui().r#virtual();
let tab = if render.tab() == WhitespaceRenderValue::All {
std::iter::once(whitespace.tab())
.chain(std::iter::repeat(whitespace.tabpad()).take(tab_width - 1))
.collect() .collect()
} else { } else {
" ".repeat(tab_width) " ".repeat(tab_width)
}; };
let virtual_tab = " ".repeat(tab_width); let virtual_tab = " ".repeat(tab_width);
let newline = if ws_render.newline() == WhitespaceRenderValue::All { let newline = if render.newline() == WhitespaceRenderValue::All {
ws_chars.newline.into() whitespace.newline().into()
} else { } else {
" ".to_owned() " ".to_owned()
}; };
let space = if ws_render.space() == WhitespaceRenderValue::All { let space = if render.space() == WhitespaceRenderValue::All {
ws_chars.space.into() whitespace.space().into()
} else { } else {
" ".to_owned() " ".to_owned()
}; };
let nbsp = if ws_render.nbsp() == WhitespaceRenderValue::All { let nbsp = if render.nbsp() == WhitespaceRenderValue::All {
ws_chars.nbsp.into() whitespace.nbsp().into()
} else { } else {
" ".to_owned() " ".to_owned()
}; };
let nnbsp = if ws_render.nnbsp() == WhitespaceRenderValue::All { let nnbsp = if render.nnbsp() == WhitespaceRenderValue::All {
ws_chars.nnbsp.into() whitespace.nnbsp().into()
} else { } else {
" ".to_owned() " ".to_owned()
}; };
@ -271,6 +275,7 @@ impl<'a> TextRenderer<'a> {
offset, offset,
} }
} }
/// Draws a single `grapheme` at the current render position with a specified `style`. /// Draws a single `grapheme` at the current render position with a specified `style`.
pub fn draw_decoration_grapheme( pub fn draw_decoration_grapheme(
&mut self, &mut self,

View File

@ -235,7 +235,8 @@ impl EditorView {
theme: &Theme, theme: &Theme,
) { ) {
let editor_rulers = &editor.config().rulers; let editor_rulers = &editor.config().rulers;
let ruler_theme = theme
let style = theme
.try_get("ui.virtual.ruler") .try_get("ui.virtual.ruler")
.unwrap_or_else(|| Style::default().bg(Color::Red)); .unwrap_or_else(|| Style::default().bg(Color::Red));
@ -253,7 +254,12 @@ impl EditorView {
.filter_map(|ruler| ruler.checked_sub(1 + view_offset.horizontal_offset as u16)) .filter_map(|ruler| ruler.checked_sub(1 + view_offset.horizontal_offset as u16))
.filter(|ruler| ruler < &viewport.width) .filter(|ruler| ruler < &viewport.width)
.map(|ruler| viewport.clip_left(ruler).with_width(1)) .map(|ruler| viewport.clip_left(ruler).with_width(1))
.for_each(|area| surface.set_style(area, ruler_theme)) .for_each(|area| {
let icons = ICONS.load();
for y in area.y..area.height {
surface.set_string(area.x, y, icons.ui().r#virtual().ruler(), style);
}
});
} }
fn viewport_byte_range( fn viewport_byte_range(
@ -583,7 +589,7 @@ impl EditorView {
let mut x = viewport.x; let mut x = viewport.x;
let current_doc = view!(editor).doc; let current_doc = view!(editor).doc;
for (idx, doc) in editor.documents().enumerate() { for doc in editor.documents() {
let fname = doc let fname = doc
.path() .path()
.unwrap_or(&scratch) .unwrap_or(&scratch)
@ -598,24 +604,9 @@ impl EditorView {
bufferline_inactive bufferline_inactive
}; };
let lang = doc.language_name().unwrap_or(DEFAULT_LANGUAGE_NAME);
let icons = ICONS.load(); let icons = ICONS.load();
// Render the separator before the text if the current document is not first. let lang = doc.language_name().unwrap_or(DEFAULT_LANGUAGE_NAME);
if idx > 0 {
let used_width = viewport.x.saturating_sub(x);
let rem_width = surface.area.width.saturating_sub(used_width);
x = surface
.set_stringn(
x,
viewport.y,
icons.ui().bufferline().separator(),
rem_width as usize,
bufferline_inactive,
)
.0;
}
if let Some(icon) = icons if let Some(icon) = icons
.fs() .fs()

View File

@ -32,6 +32,7 @@ pub use text::Text;
use helix_view::Editor; use helix_view::Editor;
use tui::text::{Span, Spans, ToSpan}; use tui::text::{Span, Spans, ToSpan};
use tui::widgets::Cell;
use std::path::Path; use std::path::Path;
use std::{error::Error, path::PathBuf}; use std::{error::Error, path::PathBuf};
@ -333,7 +334,7 @@ pub fn file_explorer(root: PathBuf, editor: &Editor) -> Result<FileExplorer, std
spans.push(icon.to_span_with(|icon| format!("{icon} "))); spans.push(icon.to_span_with(|icon| format!("{icon} ")));
spans.push(Span::raw(name)); spans.push(Span::raw(name));
Spans::from(spans).into() Cell::from(Spans::from(spans))
} else { } else {
name.into() name.into()
} }

View File

@ -236,6 +236,7 @@ where
}); });
let icons = ICONS.load(); let icons = ICONS.load();
for sev in &context.editor.config().statusline.diagnostics { for sev in &context.editor.config().statusline.diagnostics {
match sev { match sev {
Severity::Hint if hints > 0 => { Severity::Hint if hints > 0 => {
@ -486,10 +487,10 @@ fn render_file_type<'a, F>(context: &mut RenderContext<'a>, write: F)
where where
F: Fn(&mut RenderContext<'a>, Span<'a>) + Copy, F: Fn(&mut RenderContext<'a>, Span<'a>) + Copy,
{ {
let lang = context.doc.language_name().unwrap_or(DEFAULT_LANGUAGE_NAME);
let icons = ICONS.load(); let icons = ICONS.load();
let lang = context.doc.language_name().unwrap_or(DEFAULT_LANGUAGE_NAME);
if let Some(icon) = icons if let Some(icon) = icons
.fs() .fs()
.from_optional_path_or_lang(context.doc.path().map(|path| path.as_path()), lang) .from_optional_path_or_lang(context.doc.path().map(|path| path.as_path()), lang)
@ -577,10 +578,13 @@ fn render_separator<'a, F>(context: &mut RenderContext<'a>, write: F)
where where
F: Fn(&mut RenderContext<'a>, Span<'a>) + Copy, F: Fn(&mut RenderContext<'a>, Span<'a>) + Copy,
{ {
let sep = &context.editor.config().statusline.separator;
let style = context.editor.theme.get("ui.statusline.separator"); let style = context.editor.theme.get("ui.statusline.separator");
write(context, Span::styled(sep.to_string(), style)); let icons = ICONS.load();
let separator = icons.ui().statusline().separator().to_string();
write(context, Span::styled(separator, style));
} }
fn render_spacer<'a, F>(context: &mut RenderContext<'a>, write: F) fn render_spacer<'a, F>(context: &mut RenderContext<'a>, write: F)

View File

@ -43,6 +43,7 @@ use helix_core::{
ChangeSet, Diagnostic, LineEnding, Range, Rope, RopeBuilder, Selection, Syntax, Transaction, ChangeSet, Diagnostic, LineEnding, Range, Rope, RopeBuilder, Selection, Syntax, Transaction,
}; };
use crate::icons::{Icons, ICONS};
use crate::{ use crate::{
editor::Config, editor::Config,
events::{DocumentDidChange, SelectionDidChange}, events::{DocumentDidChange, SelectionDidChange},
@ -2243,8 +2244,10 @@ impl Document {
.unwrap_or(40); .unwrap_or(40);
let wrap_indicator = language_soft_wrap let wrap_indicator = language_soft_wrap
.and_then(|soft_wrap| soft_wrap.wrap_indicator.clone()) .and_then(|soft_wrap| soft_wrap.wrap_indicator.clone())
.or_else(|| config.soft_wrap.wrap_indicator.clone()) .unwrap_or_else(|| {
.unwrap_or_else(|| "".into()); let icons: arc_swap::access::DynGuard<Icons> = ICONS.load();
icons.ui().r#virtual().wrap().to_string()
});
let tab_width = self.tab_width() as u16; let tab_width = self.tab_width() as u16;
TextFormat { TextFormat {
soft_wrap: enable_soft_wrap && viewport_width > 10, soft_wrap: enable_soft_wrap && viewport_width > 10,

View File

@ -508,7 +508,6 @@ pub struct StatusLineConfig {
pub left: Vec<StatusLineElement>, pub left: Vec<StatusLineElement>,
pub center: Vec<StatusLineElement>, pub center: Vec<StatusLineElement>,
pub right: Vec<StatusLineElement>, pub right: Vec<StatusLineElement>,
pub separator: String,
pub mode: ModeConfig, pub mode: ModeConfig,
pub diagnostics: Vec<Severity>, pub diagnostics: Vec<Severity>,
pub workspace_diagnostics: Vec<Severity>, pub workspace_diagnostics: Vec<Severity>,
@ -534,7 +533,6 @@ impl Default for StatusLineConfig {
E::Position, E::Position,
E::FileEncoding, E::FileEncoding,
], ],
separator: String::from(""),
mode: ModeConfig::default(), mode: ModeConfig::default(),
diagnostics: vec![Severity::Warning, Severity::Error], diagnostics: vec![Severity::Warning, Severity::Error],
workspace_diagnostics: vec![Severity::Warning, Severity::Error], workspace_diagnostics: vec![Severity::Warning, Severity::Error],
@ -753,14 +751,12 @@ impl std::str::FromStr for GutterType {
#[serde(default)] #[serde(default)]
pub struct WhitespaceConfig { pub struct WhitespaceConfig {
pub render: WhitespaceRender, pub render: WhitespaceRender,
pub characters: WhitespaceCharacters,
} }
impl Default for WhitespaceConfig { impl Default for WhitespaceConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
render: WhitespaceRender::Basic(WhitespaceRenderValue::None), render: WhitespaceRender::Basic(WhitespaceRenderValue::None),
characters: WhitespaceCharacters::default(),
} }
} }
} }
@ -886,30 +882,6 @@ where
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(default)]
pub struct WhitespaceCharacters {
pub space: char,
pub nbsp: char,
pub nnbsp: char,
pub tab: char,
pub tabpad: char,
pub newline: char,
}
impl Default for WhitespaceCharacters {
fn default() -> Self {
Self {
space: '·', // U+00B7
nbsp: '', // U+237D
nnbsp: '', // U+2423
tab: '', // U+2192
newline: '', // U+23CE
tabpad: ' ',
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(default, rename_all = "kebab-case")] #[serde(default, rename_all = "kebab-case")]
pub struct IndentGuidesConfig { pub struct IndentGuidesConfig {

View File

@ -127,17 +127,18 @@ pub fn diff<'doc>(
let icons = ICONS.load(); let icons = ICONS.load();
let (icon, style) = if hunk.is_pure_insertion() { let (icon, style) = if hunk.is_pure_insertion() {
(icons.gutter().added(), added) (icons.ui().gutter().added(), added)
} else if hunk.is_pure_removal() { } else if hunk.is_pure_removal() {
if !first_visual_line { if !first_visual_line {
return None; return None;
} }
(icons.gutter().removed(), deleted) (icons.ui().gutter().removed(), deleted)
} else { } else {
(icons.gutter().modified(), modified) (icons.ui().gutter().modified(), modified)
}; };
write!(out, "{}", icon).unwrap(); write!(out, "{icon}").unwrap();
Some(style) Some(style)
}, },
) )

View File

@ -20,7 +20,6 @@ pub struct Icons {
diagnostic: Diagnostic, diagnostic: Diagnostic,
vcs: Vcs, vcs: Vcs,
dap: Dap, dap: Dap,
gutter: Gutter,
ui: Ui, ui: Ui,
} }
@ -50,11 +49,6 @@ impl Icons {
&self.dap &self.dap
} }
#[inline]
pub fn gutter(&self) -> &Gutter {
&self.gutter
}
#[inline] #[inline]
pub fn ui(&self) -> &Ui { pub fn ui(&self) -> &Ui {
&self.ui &self.ui
@ -113,48 +107,46 @@ impl Kind {
return None; return None;
} }
let icon = match kind { match kind {
"file" => self.file()?, "file" => self.file(),
"folder" => self.folder()?, "folder" => self.folder(),
"module" => self.module()?, "module" => self.module(),
"namespace" => self.namespace()?, "namespace" => self.namespace(),
"package" => self.package()?, "package" => self.package(),
"class" => self.class()?, "class" => self.class(),
"method" => self.method()?, "method" => self.method(),
"property" => self.property()?, "property" => self.property(),
"field" => self.field()?, "field" => self.field(),
"construct" => self.constructor()?, "construct" => self.constructor(),
"enum" => self.r#enum()?, "enum" => self.r#enum(),
"interface" => self.interface()?, "interface" => self.interface(),
"function" => self.function()?, "function" => self.function(),
"variable" => self.variable()?, "variable" => self.variable(),
"constant" => self.constant()?, "constant" => self.constant(),
"string" => self.string()?, "string" => self.string(),
"number" => self.number()?, "number" => self.number(),
"boolean" => self.boolean()?, "boolean" => self.boolean(),
"array" => self.array()?, "array" => self.array(),
"object" => self.object()?, "object" => self.object(),
"key" => self.key()?, "key" => self.key(),
"null" => self.null()?, "null" => self.null(),
"enum_member" => self.enum_member()?, "enum_member" => self.enum_member(),
"struct" => self.r#struct()?, "struct" => self.r#struct(),
"event" => self.event()?, "event" => self.event(),
"operator" => self.operator()?, "operator" => self.operator(),
"typeparam" => self.type_parameter()?, "typeparam" => self.type_parameter(),
"color" => self.color(), "color" => Some(self.color()),
"keyword" => self.keyword()?, "keyword" => self.keyword(),
"value" => self.value()?, "value" => self.value(),
"snippet" => self.snippet()?, "snippet" => self.snippet(),
"reference" => self.reference()?, "reference" => self.reference(),
"text" => self.text()?, "text" => self.text(),
"unit" => self.unit()?, "unit" => self.unit(),
"word" => self.word()?, "word" => self.word(),
"spellcheck" => self.spellcheck()?, "spellcheck" => self.spellcheck(),
_ => return None, _ => None,
}; }
Some(icon)
} }
#[inline] #[inline]
@ -556,21 +548,23 @@ pub struct Fs {
mime: HashMap<String, Icon>, mime: HashMap<String, Icon>,
} }
macro_rules! mimes { macro_rules! iconmap {
( $( $key:literal => { glyph: $glyph:expr $(, color: $color:expr)? } ),* $(,)? ) => {{ ( $( $key:literal => { glyph: $glyph:expr $(, color: $color:expr)? } ),* $(,)? ) => {{
let mut map = HashMap::new(); HashMap::from(
$( [
map.insert(String::from($key), Icon { $(
glyph: String::from($glyph), (String::from($key), Icon {
color: None $(.or( Some(Color::from_hex($color).unwrap())) )?, glyph: String::from($glyph),
}); color: None $(.or( Some(Color::from_hex($color).unwrap())) )?,
)* }),
map )*
]
)
}}; }};
} }
static MIMES: once_cell::sync::Lazy<HashMap<String, Icon>> = once_cell::sync::Lazy::new(|| { static MIMES: once_cell::sync::Lazy<HashMap<String, Icon>> = once_cell::sync::Lazy::new(|| {
mimes! { iconmap! {
// Language name // Language name
"git-commit" => {glyph: "", color: "#f15233" }, "git-commit" => {glyph: "", color: "#f15233" },
"git-rebase" => {glyph: "", color: "#f15233" }, "git-rebase" => {glyph: "", color: "#f15233" },
@ -695,13 +689,11 @@ impl Fs {
return None; return None;
} }
let dir = if is_open { if is_open {
self.directory_open.as_deref().unwrap_or("󰝰") self.directory_open.as_deref().or(Some("󰝰"))
} else { } else {
self.directory.as_deref().unwrap_or("󰉋") self.directory.as_deref().or(Some("󰉋"))
}; }
Some(dir)
} }
#[inline] #[inline]
@ -808,6 +800,41 @@ impl Dap {
} }
} }
#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Default)]
#[serde(default, deny_unknown_fields)]
pub struct Ui {
workspace: Option<Icon>,
gutter: Gutter,
#[serde(rename = "virtual")]
r#virtual: Virtual,
statusline: Statusline,
}
impl Ui {
/// Returns a workspace diagnostic icon.
///
/// If no icon is set in the config, it will return `W` by default.
#[inline]
pub fn workspace(&self) -> Icon {
self.workspace.clone().unwrap_or_else(|| Icon::from("W"))
}
#[inline]
pub fn gutter(&self) -> &Gutter {
&self.gutter
}
#[inline]
pub fn r#virtual(&self) -> &Virtual {
&self.r#virtual
}
#[inline]
pub fn statusline(&self) -> &Statusline {
&self.statusline
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)]
pub struct Gutter { pub struct Gutter {
added: Option<String>, added: Option<String>,
@ -832,46 +859,100 @@ impl Gutter {
} }
} }
#[derive(Debug, Default, PartialEq, Eq, Clone)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)]
pub struct Icon { pub struct Virtual {
glyph: String, // Whitespace
color: Option<Color>, space: Option<String>,
nbsp: Option<String>,
nnbsp: Option<String>,
tab: Option<String>,
newline: Option<String>,
tabpad: Option<String>,
// Soft-wrap
wrap: Option<String>,
// Indentation guide
indentation: Option<String>,
// Ruler
ruler: Option<String>,
} }
#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Default)] impl Virtual {
#[serde(default, deny_unknown_fields)]
pub struct Ui {
workspace: Option<Icon>,
bufferline: BufferLine,
}
impl Ui {
/// Returns a workspace diagnostic icon.
///
/// If no icon is set in the config, it will return `W` by default.
#[inline] #[inline]
pub fn workspace(&self) -> Icon { pub fn space(&self) -> &str {
self.workspace.clone().unwrap_or_else(|| Icon::from("W")) // Default: U+00B7
self.space.as_deref().unwrap_or("·")
} }
#[inline] #[inline]
pub fn bufferline(&self) -> &BufferLine { pub fn nbsp(&self) -> &str {
&self.bufferline // Default: U+237D
self.nbsp.as_deref().unwrap_or("")
}
#[inline]
pub fn nnbsp(&self) -> &str {
// Default: U+2423
self.nnbsp.as_deref().unwrap_or("")
}
#[inline]
pub fn tab(&self) -> &str {
// Default: U+2192
self.tab.as_deref().unwrap_or("")
}
#[inline]
pub fn newline(&self) -> &str {
// Default: U+23CE
self.newline.as_deref().unwrap_or("")
}
#[inline]
pub fn tabpad(&self) -> &str {
// Default: U+23CE
self.tabpad.as_deref().unwrap_or(" ")
}
#[inline]
pub fn wrap(&self) -> &str {
// Default: U+21AA
self.wrap.as_deref().unwrap_or("")
}
#[inline]
pub fn indentation(&self) -> &str {
// Default: U+254E
self.indentation.as_deref().unwrap_or("")
}
#[inline]
pub fn ruler(&self) -> &str {
// TODO: Default: U+00A6: ¦
self.ruler.as_deref().unwrap_or(" ")
} }
} }
#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Default)] #[derive(Debug, Deserialize, PartialEq, Eq, Clone, Default)]
pub struct BufferLine { pub struct Statusline {
separator: Option<String>, separator: Option<String>,
} }
impl BufferLine { impl Statusline {
#[inline] #[inline]
pub fn separator(&self) -> &str { pub fn separator(&self) -> &str {
self.separator.as_deref().unwrap_or("") self.separator.as_deref().unwrap_or("")
} }
} }
#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub struct Icon {
glyph: String,
color: Option<Color>,
}
impl Icon { impl Icon {
pub fn glyph(&self) -> &str { pub fn glyph(&self) -> &str {
self.glyph.as_str() self.glyph.as_str()