Compare commits

...

2 Commits

Author SHA1 Message Date
trivernis 8c87021a53
Properly add icon support into tree rendering
The icon to render is passed as an additonal field
and rendered directly to the surface so that the
style can be rendered as well
1 year ago
trivernis 7ccbea2a31
Add rudimentary nerdfonts support 1 year ago

@ -1,10 +1,11 @@
use super::{Prompt, TreeOp, TreeView, TreeViewItem};
use super::{tree::TreeIcons, Prompt, TreeOp, TreeView, TreeViewItem};
use crate::{
compositor::{Component, Context, EventResult},
ctrl, key, shift, ui,
};
use anyhow::{bail, ensure, Result};
use helix_core::Position;
use helix_view::{
editor::{Action, ExplorerPosition},
graphics::{CursorKind, Rect},
@ -13,9 +14,9 @@ use helix_view::{
theme::Modifier,
Editor,
};
use std::cmp::Ordering;
use std::path::{Path, PathBuf};
use std::{borrow::Cow, fs::DirEntry};
use std::{cmp::Ordering, sync::Arc};
use tui::{
buffer::Buffer as Surface,
widgets::{Block, Borders, Widget},
@ -172,7 +173,7 @@ impl Explorer {
.unwrap_or_else(|_| "./".into())
.canonicalize()?;
Ok(Self {
tree: Self::new_tree_view(current_root.clone())?,
tree: Self::new_tree_view(current_root.clone(), Self::get_tree_icons(cx))?,
history: vec![],
show_help: false,
state: State::new(true, current_root),
@ -182,10 +183,44 @@ impl Explorer {
})
}
fn get_tree_icons(cx: &Context) -> TreeIcons {
let icons = cx.editor.icons.clone();
let defaults = TreeIcons::default();
let file_icon = icons
.ui
.as_ref()
.and_then(|s| s.get("file").cloned())
.unwrap_or(defaults.item);
let item = file_icon.clone();
let tree_closed = icons
.ui
.as_ref()
.and_then(|s| s.get("folder").cloned())
.unwrap_or(defaults.tree_closed);
let tree_opened = icons
.ui
.as_ref()
.and_then(|s| s.get("folder_opened").cloned())
.unwrap_or(defaults.tree_opened);
TreeIcons {
tree_closed,
tree_opened,
item,
icon_fn: Some(Arc::new(move |item| {
icons
.icon_from_path(Some(&PathBuf::from(item)))
.cloned()
.unwrap_or_else(|| file_icon.to_owned())
})),
}
}
#[cfg(test)]
fn from_path(root: PathBuf, column_width: u16) -> Result<Self> {
Ok(Self {
tree: Self::new_tree_view(root.clone())?,
tree: Self::new_tree_view(root.clone(), TreeIcons::default())?,
history: vec![],
show_help: false,
state: State::new(true, root),
@ -195,9 +230,11 @@ impl Explorer {
})
}
fn new_tree_view(root: PathBuf) -> Result<TreeView<FileInfo>> {
fn new_tree_view(root: PathBuf, icons: TreeIcons) -> Result<TreeView<FileInfo>> {
let root = FileInfo::root(root);
Ok(TreeView::build_tree(root)?.with_enter_fn(Self::toggle_current))
Ok(TreeView::build_tree(root)?
.with_enter_fn(Self::toggle_current)
.icons(icons))
}
fn push_history(&mut self, tree_view: TreeView<FileInfo>, current_root: PathBuf) {
@ -213,7 +250,7 @@ impl Explorer {
if self.state.current_root.eq(&root) {
return Ok(());
}
let tree = Self::new_tree_view(root.clone())?;
let tree = Self::new_tree_view(root.clone(), self.tree.icons.clone())?;
let old_tree = std::mem::replace(&mut self.tree, tree);
self.push_history(old_tree, self.state.current_root.clone());
self.state.current_root = root;

@ -1,7 +1,7 @@
use std::cmp::Ordering;
use std::{cmp::Ordering, sync::Arc};
use anyhow::Result;
use helix_view::theme::Modifier;
use helix_view::{icons::Icon, theme::Modifier};
use crate::{
compositor::{Component, Context, EventResult},
@ -290,6 +290,7 @@ pub struct TreeView<T: TreeViewItem> {
max_len: usize,
count: usize,
tree_symbol_style: String,
pub icons: TreeIcons,
#[allow(clippy::type_complexity)]
pre_render: Option<Box<dyn Fn(&mut Self, Rect) + 'static>>,
@ -325,6 +326,7 @@ impl<T: TreeViewItem> TreeView<T> {
on_next_key: None,
search_prompt: None,
search_str: "".into(),
icons: TreeIcons::default(),
})
}
@ -349,6 +351,11 @@ impl<T: TreeViewItem> TreeView<T> {
self
}
pub fn icons(mut self, icons: TreeIcons) -> Self {
self.icons = icons;
self
}
/// Reveal item in the tree based on the given `segments`.
///
/// The name of the root should be excluded.
@ -775,12 +782,33 @@ struct RenderedLine {
content: String,
selected: bool,
is_ancestor_of_current_item: bool,
icon: Icon,
}
struct RenderTreeParams<'a, T> {
tree: &'a Tree<T>,
prefix: &'a String,
level: usize,
selected: usize,
icons: &'a TreeIcons,
}
#[derive(Clone)]
pub struct TreeIcons {
pub tree_closed: Icon,
pub tree_opened: Icon,
pub item: Icon,
pub icon_fn: Option<Arc<dyn Fn(&str) -> Icon>>,
}
impl Default for TreeIcons {
fn default() -> Self {
Self {
tree_closed: Icon::unstyled('⏵'),
tree_opened: Icon::unstyled('⏷'),
item: Icon::unstyled(' '),
icon_fn: None,
}
}
}
fn render_tree<T: TreeViewItem>(
@ -789,28 +817,36 @@ fn render_tree<T: TreeViewItem>(
prefix,
level,
selected,
icons,
}: RenderTreeParams<T>,
) -> Vec<RenderedLine> {
let indent = if level > 0 {
let indicator = if tree.item().is_parent() {
if tree.is_opened {
"⏷"
} else {
"⏵"
}
let name = tree.item.name();
let icon = if tree.item().is_parent() {
if tree.is_opened {
icons.tree_opened.to_owned()
} else {
" "
};
format!("{}{} ", prefix, indicator)
icons.tree_closed.to_owned()
}
} else {
"".to_string()
if let Some(icon_fn) = icons.icon_fn.as_ref() {
icon_fn(&name)
} else {
icons.item.to_owned()
}
};
let indent = if level > 0 {
format!("{} ", prefix)
} else {
String::new()
};
let name = tree.item.name();
let head = RenderedLine {
indent,
selected: selected == tree.index,
is_ancestor_of_current_item: selected != tree.index && tree.get(selected).is_some(),
content: name,
icon,
};
let prefix = format!("{}{}", prefix, if level == 0 { "" } else { " " });
vec![head]
@ -821,6 +857,7 @@ fn render_tree<T: TreeViewItem>(
prefix: &prefix,
level: level + 1,
selected,
icons,
})
}))
.collect()
@ -861,12 +898,20 @@ impl<T: TreeViewItem + Clone> TreeView<T> {
style,
);
let x = area.x.saturating_add(indent_len);
surface.set_stringn(
x,
area.y,
line.icon.icon_char.to_string(),
2,
line.icon.style.map(|s| s.into()).unwrap_or(style),
);
let style = if line.selected {
style.add_modifier(Modifier::REVERSED)
} else {
style
};
let x = area.x.saturating_add(indent_len);
let x = x.saturating_add(2);
surface.set_stringn(
x,
area.y,
@ -915,6 +960,7 @@ impl<T: TreeViewItem + Clone> TreeView<T> {
prefix: &"".to_string(),
level: 0,
selected: self.selected,
icons: &self.icons,
};
let lines = render_tree(params);

@ -54,6 +54,13 @@ impl Icon {
self.style = Some(IconStyle::Default(style));
}
}
pub fn unstyled(icon_char: char) -> Self {
Self {
icon_char,
style: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]

Loading…
Cancel
Save