Skip to content

Commit 578a81f

Browse files
committed
feat: add support for basic icons
1 parent 0ee5850 commit 578a81f

File tree

13 files changed

+831
-40
lines changed

13 files changed

+831
-40
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

helix-term/src/commands.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use helix_core::{
4444
use helix_view::{
4545
document::{FormatterError, Mode, SCRATCH_BUFFER_NAME},
4646
editor::Action,
47+
icons::ICONS,
4748
info::Info,
4849
input::KeyEvent,
4950
keyboard::KeyCode,
@@ -3296,12 +3297,28 @@ fn changed_file_picker(cx: &mut Context) {
32963297

32973298
let columns = [
32983299
PickerColumn::new("change", |change: &FileChange, data: &FileChangeData| {
3300+
let icons = ICONS.load();
32993301
match change {
3300-
FileChange::Untracked { .. } => Span::styled("+ untracked", data.style_untracked),
3301-
FileChange::Modified { .. } => Span::styled("~ modified", data.style_modified),
3302-
FileChange::Conflict { .. } => Span::styled("x conflict", data.style_conflict),
3303-
FileChange::Deleted { .. } => Span::styled("- deleted", data.style_deleted),
3304-
FileChange::Renamed { .. } => Span::styled("> renamed", data.style_renamed),
3302+
FileChange::Untracked { .. } => Span::styled(
3303+
format!("{} untracked", icons.vcs().added()),
3304+
data.style_untracked,
3305+
),
3306+
FileChange::Modified { .. } => Span::styled(
3307+
format!("{} modified", icons.vcs().modified()),
3308+
data.style_modified,
3309+
),
3310+
FileChange::Conflict { .. } => Span::styled(
3311+
format!("{} conflict", icons.vcs().conflict()),
3312+
data.style_conflict,
3313+
),
3314+
FileChange::Deleted { .. } => Span::styled(
3315+
format!("{} deleted", icons.vcs().removed()),
3316+
data.style_deleted,
3317+
),
3318+
FileChange::Renamed { .. } => Span::styled(
3319+
format!("{} renamed", icons.vcs().renamed()),
3320+
data.style_renamed,
3321+
),
33053322
}
33063323
.into()
33073324
}),

helix-term/src/commands/lsp.rs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use helix_view::{
2222
document::{DocumentInlayHints, DocumentInlayHintsId},
2323
editor::Action,
2424
handlers::lsp::SignatureHelpInvoked,
25+
icons::ICONS,
2526
theme::Style,
2627
Document, View,
2728
};
@@ -242,11 +243,22 @@ fn diag_picker(
242243
ui::PickerColumn::new(
243244
"severity",
244245
|item: &PickerDiagnostic, styles: &DiagnosticStyles| {
246+
let icons = ICONS.load();
245247
match item.diag.severity {
246-
Some(DiagnosticSeverity::HINT) => Span::styled("HINT", styles.hint),
247-
Some(DiagnosticSeverity::INFORMATION) => Span::styled("INFO", styles.info),
248-
Some(DiagnosticSeverity::WARNING) => Span::styled("WARN", styles.warning),
249-
Some(DiagnosticSeverity::ERROR) => Span::styled("ERROR", styles.error),
248+
Some(DiagnosticSeverity::HINT) => {
249+
Span::styled(format!("{} HINT", icons.diagnostic().hint()), styles.hint)
250+
}
251+
Some(DiagnosticSeverity::INFORMATION) => {
252+
Span::styled(format!("{} INFO", icons.diagnostic().info()), styles.info)
253+
}
254+
Some(DiagnosticSeverity::WARNING) => Span::styled(
255+
format!("{} WARN", icons.diagnostic().warning()),
256+
styles.warning,
257+
),
258+
Some(DiagnosticSeverity::ERROR) => Span::styled(
259+
format!("{} ERROR", icons.diagnostic().error()),
260+
styles.error,
261+
),
250262
_ => Span::raw(""),
251263
}
252264
.into()
@@ -397,7 +409,12 @@ pub fn symbol_picker(cx: &mut Context) {
397409
let call = move |_editor: &mut Editor, compositor: &mut Compositor| {
398410
let columns = [
399411
ui::PickerColumn::new("kind", |item: &SymbolInformationItem, _| {
400-
display_symbol_kind(item.symbol.kind).into()
412+
let icons = ICONS.load();
413+
let name = display_symbol_kind(item.symbol.kind);
414+
icons
415+
.lsp()
416+
.get(name)
417+
.map_or_else(|| name.into(), |symbol| format!("{symbol} {name}").into())
401418
}),
402419
// Some symbols in the document symbol picker may have a URI that isn't
403420
// the current file. It should be rare though, so we concatenate that
@@ -515,7 +532,12 @@ pub fn workspace_symbol_picker(cx: &mut Context) {
515532
};
516533
let columns = [
517534
ui::PickerColumn::new("kind", |item: &SymbolInformationItem, _| {
518-
display_symbol_kind(item.symbol.kind).into()
535+
let icons = ICONS.load();
536+
let name = display_symbol_kind(item.symbol.kind);
537+
icons
538+
.lsp()
539+
.get(name)
540+
.map_or_else(|| name.into(), |symbol| format!("{symbol} {name}").into())
519541
}),
520542
ui::PickerColumn::new("name", |item: &SymbolInformationItem, _| {
521543
item.symbol.name.as_str().into()

helix-term/src/config.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ use crate::keymap;
22
use crate::keymap::{merge_keys, KeyTrie};
33
use helix_loader::merge_toml_values;
44
use helix_view::document::Mode;
5+
use helix_view::icons::{Icons, ICONS};
56
use serde::Deserialize;
67
use std::collections::HashMap;
78
use std::fmt::Display;
89
use std::fs;
910
use std::io::Error as IOError;
11+
use std::sync::Arc;
1012
use toml::de::Error as TomlError;
1113

1214
#[derive(Debug, Clone, PartialEq)]
@@ -22,6 +24,7 @@ pub struct ConfigRaw {
2224
pub theme: Option<String>,
2325
pub keys: Option<HashMap<Mode, KeyTrie>>,
2426
pub editor: Option<toml::Value>,
27+
pub icons: Option<toml::Value>,
2528
}
2629

2730
impl Default for Config {
@@ -64,6 +67,7 @@ impl Config {
6467
global.and_then(|file| toml::from_str(&file).map_err(ConfigLoadError::BadConfig));
6568
let local_config: Result<ConfigRaw, ConfigLoadError> =
6669
local.and_then(|file| toml::from_str(&file).map_err(ConfigLoadError::BadConfig));
70+
6771
let res = match (global_config, local_config) {
6872
(Ok(global), Ok(local)) => {
6973
let mut keys = keymap::default();
@@ -84,6 +88,18 @@ impl Config {
8488
.map_err(ConfigLoadError::BadConfig)?,
8589
};
8690

91+
let icons: Icons = match (global.icons, local.icons) {
92+
(None, None) => Icons::default(),
93+
(None, Some(val)) | (Some(val), None) => {
94+
val.try_into().map_err(ConfigLoadError::BadConfig)?
95+
}
96+
(Some(global), Some(local)) => merge_toml_values(global, local, 3)
97+
.try_into()
98+
.map_err(ConfigLoadError::BadConfig)?,
99+
};
100+
101+
ICONS.store(Arc::new(icons));
102+
87103
Config {
88104
theme: local.theme.or(global.theme),
89105
keys,
@@ -100,6 +116,14 @@ impl Config {
100116
if let Some(keymap) = config.keys {
101117
merge_keys(&mut keys, keymap);
102118
}
119+
120+
let icons = config.icons.map_or_else(
121+
|| Ok(Icons::default()),
122+
|val| val.try_into().map_err(ConfigLoadError::BadConfig),
123+
)?;
124+
125+
ICONS.store(Arc::new(icons));
126+
103127
Config {
104128
theme: config.theme,
105129
keys,

helix-term/src/handlers/document_colors.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use helix_view::{
88
document::DocumentColorSwatches,
99
events::{DocumentDidChange, DocumentDidOpen, LanguageServerExited, LanguageServerInitialized},
1010
handlers::{lsp::DocumentColorsEvent, Handlers},
11+
icons::ICONS,
1112
DocumentId, Editor, Theme,
1213
};
1314
use tokio::time::Instant;
@@ -120,9 +121,11 @@ fn attach_document_colors(
120121
let mut color_swatches_padding = Vec::with_capacity(doc_colors.len());
121122
let mut colors = Vec::with_capacity(doc_colors.len());
122123

124+
let icons = ICONS.load();
125+
123126
for (pos, color) in doc_colors {
124127
color_swatches_padding.push(InlineAnnotation::new(pos, " "));
125-
color_swatches.push(InlineAnnotation::new(pos, "■"));
128+
color_swatches.push(InlineAnnotation::new(pos, icons.lsp().color()));
126129
colors.push(Theme::rgb_highlight(
127130
(color.red * 255.) as u8,
128131
(color.green * 255.) as u8,

helix-term/src/ui/completion.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::{
99
use helix_core::snippets::{ActiveSnippet, RenderedSnippet, Snippet};
1010
use helix_core::{self as core, chars, fuzzy::MATCHER, Change, Transaction};
1111
use helix_lsp::{lsp, util, OffsetEncoding};
12+
use helix_view::icons::ICONS;
1213
use helix_view::{
1314
editor::CompleteAction,
1415
handlers::lsp::SignatureHelpInvoked,
@@ -45,7 +46,7 @@ impl menu::Item for CompletionItem {
4546
CompletionItem::Other(core::CompletionItem { label, .. }) => label,
4647
};
4748

48-
let kind = match self {
49+
let mut kind = match self {
4950
CompletionItem::Lsp(LspCompletionItem { item, .. }) => match item.kind {
5051
Some(lsp::CompletionItemKind::TEXT) => "text".into(),
5152
Some(lsp::CompletionItemKind::METHOD) => "method".into(),
@@ -78,9 +79,13 @@ impl menu::Item for CompletionItem {
7879
})
7980
.and_then(Color::from_hex)
8081
.map_or("color".into(), |color| {
82+
let icons = ICONS.load();
8183
Spans::from(vec![
8284
Span::raw("color "),
83-
Span::styled("■", Style::default().fg(color)),
85+
Span::styled(
86+
icons.lsp().color().to_string(),
87+
Style::default().fg(color),
88+
),
8489
])
8590
}),
8691
Some(lsp::CompletionItemKind::FILE) => "file".into(),
@@ -101,6 +106,13 @@ impl menu::Item for CompletionItem {
101106
CompletionItem::Other(core::CompletionItem { kind, .. }) => kind.as_ref().into(),
102107
};
103108

109+
let icons = ICONS.load();
110+
let name = &kind.0[0].content;
111+
112+
if let Some(icon) = icons.lsp().get(name) {
113+
kind.0[0].content = format!("{icon} {name}").into();
114+
}
115+
104116
let label = Span::styled(
105117
label,
106118
if deprecated {

helix-term/src/ui/editor.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ use helix_core::{
2424
};
2525
use helix_view::{
2626
annotations::diagnostics::DiagnosticFilter,
27-
document::{Mode, SCRATCH_BUFFER_NAME},
27+
document::{Mode, DEFAULT_LANGUAGE_NAME, SCRATCH_BUFFER_NAME},
2828
editor::{CompleteAction, CursorShapeConfig},
2929
graphics::{Color, CursorKind, Modifier, Rect, Style},
30+
icons::ICONS,
3031
input::{KeyEvent, MouseButton, MouseEvent, MouseEventKind},
3132
keyboard::{KeyCode, KeyModifiers},
3233
Document, Editor, Theme, View,
@@ -647,7 +648,21 @@ impl EditorView {
647648
bufferline_inactive
648649
};
649650

650-
let text = format!(" {}{} ", fname, if doc.is_modified() { "[+]" } else { "" });
651+
let lang = doc.language_name().unwrap_or(DEFAULT_LANGUAGE_NAME);
652+
653+
let icons = ICONS.load();
654+
let icon = icons.mime().get(lang);
655+
656+
let text = if lang == icon {
657+
format!(" {} {}", fname, if doc.is_modified() { "[+] " } else { "" })
658+
} else {
659+
format!(
660+
" {icon} {} {}",
661+
fname,
662+
if doc.is_modified() { "[+] " } else { "" }
663+
)
664+
};
665+
651666
let used_width = viewport.x.saturating_sub(x);
652667
let rem_width = surface.area.width.saturating_sub(used_width);
653668

helix-term/src/ui/statusline.rs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use helix_core::{coords_at_pos, encoding, Position};
22
use helix_lsp::lsp::DiagnosticSeverity;
33
use helix_view::document::DEFAULT_LANGUAGE_NAME;
4+
use helix_view::icons::ICONS;
45
use helix_view::{
56
document::{Mode, SCRATCH_BUFFER_NAME},
67
graphics::Rect,
@@ -240,10 +241,12 @@ where
240241
counts
241242
});
242243

244+
let icons = ICONS.load();
245+
243246
if warnings > 0 {
244247
write(
245248
context,
246-
"●".to_string(),
249+
icons.diagnostic().warning().to_string(),
247250
Some(context.editor.theme.get("warning")),
248251
);
249252
write(context, format!(" {} ", warnings), None);
@@ -252,7 +255,7 @@ where
252255
if errors > 0 {
253256
write(
254257
context,
255-
"●".to_string(),
258+
icons.diagnostic().error().to_string(),
256259
Some(context.editor.theme.get("error")),
257260
);
258261
write(context, format!(" {} ", errors), None);
@@ -282,10 +285,12 @@ where
282285
write(context, " W ".into(), None);
283286
}
284287

288+
let icons = ICONS.load();
289+
285290
if warnings > 0 {
286291
write(
287292
context,
288-
"●".to_string(),
293+
icons.diagnostic().warning().to_string(),
289294
Some(context.editor.theme.get("warning")),
290295
);
291296
write(context, format!(" {} ", warnings), None);
@@ -294,7 +299,7 @@ where
294299
if errors > 0 {
295300
write(
296301
context,
297-
"●".to_string(),
302+
icons.diagnostic().error().to_string(),
298303
Some(context.editor.theme.get("error")),
299304
);
300305
write(context, format!(" {} ", errors), None);
@@ -410,9 +415,13 @@ fn render_file_type<F>(context: &mut RenderContext, write: F)
410415
where
411416
F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
412417
{
413-
let file_type = context.doc.language_name().unwrap_or(DEFAULT_LANGUAGE_NAME);
418+
let icons = ICONS.load();
414419

415-
write(context, format!(" {} ", file_type), None);
420+
let icon = icons
421+
.mime()
422+
.get(context.doc.language_name().unwrap_or(DEFAULT_LANGUAGE_NAME));
423+
424+
write(context, format!(" {} ", icon), None);
416425
}
417426

418427
fn render_file_name<F>(context: &mut RenderContext, write: F)
@@ -514,13 +523,18 @@ fn render_version_control<F>(context: &mut RenderContext, write: F)
514523
where
515524
F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
516525
{
517-
let head = context
518-
.doc
519-
.version_control_head()
520-
.unwrap_or_default()
521-
.to_string();
526+
let head = context.doc.version_control_head().unwrap_or_default();
527+
528+
let icons = ICONS.load();
529+
let icon = icons.vcs().icon();
530+
531+
let vcs = if head.is_empty() {
532+
format!("{head}")
533+
} else {
534+
format!("{icon} {head}")
535+
};
522536

523-
write(context, head, None);
537+
write(context, vcs, None);
524538
}
525539

526540
fn render_register<F>(context: &mut RenderContext, write: F)

0 commit comments

Comments
 (0)