Merge remote-tracking branch 'tree-explorer-icons/tree_explorer_icons'

imgbot
trivernis 2 years ago
commit 9d1d6a74ec
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -5,6 +5,7 @@
# Merged PRs
- [File explorer and tree helper](https://github.com/helix-editor/helix/pull/2377)
- [with Icons](https://github.com/r0l1/helix/tree/tree_explorer_icons)
- [Add LSP workspace command picker](https://github.com/helix-editor/helix/pull/3140)
And others I forgot about...

@ -338,33 +338,6 @@ impl Registry {
.map(|(_, client)| client.as_ref())
}
pub fn restart(
&mut self,
language_config: &LanguageConfiguration,
) -> Result<Option<Arc<Client>>> {
let config = match &language_config.language_server {
Some(config) => config,
None => return Ok(None),
};
let scope = language_config.scope.clone();
match self.inner.entry(scope) {
Entry::Vacant(_) => Ok(None),
Entry::Occupied(mut entry) => {
// initialize a new client
let id = self.counter.fetch_add(1, Ordering::Relaxed);
let NewClientResult(client, incoming) = start_client(id, language_config, config)?;
self.incoming.push(UnboundedReceiverStream::new(incoming));
entry.insert((id, client.clone()));
Ok(Some(client))
}
}
}
pub fn get(&mut self, language_config: &LanguageConfiguration) -> Result<Option<Arc<Client>>> {
let config = match &language_config.language_server {
Some(config) => config,
@ -385,6 +358,7 @@ impl Registry {
}
}
}
pub fn restart(&mut self, language_config: &LanguageConfiguration) -> Result<Arc<Client>> {
let config = language_config
.language_server

@ -1559,16 +1559,6 @@ fn run_shell_command(
Ok(())
}
fn lsp_restart(
cx: &mut compositor::Context,
_args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
let current_document = doc!(cx.editor).id();
cx.editor.restart_language_server(current_document);
Ok(())
}
pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
TypableCommand {
name: "quit",

@ -26,6 +26,27 @@ macro_rules! get_theme {
};
}
const ICONS: &'static [&'static str] =
&["", "", "", "", "", "ﰟ", "", "", "", "ﯤ", "", "ﬥ"];
const ICONS_EXT: &'static [&'static str] = &[
".rs", ".md", ".js", ".c", ".png", ".svg", ".css", ".html", ".lua", ".ts", ".py", ".json",
];
const ICONS_COLORS: &'static [helix_view::theme::Color] = &[
helix_view::theme::Color::Rgb(227, 134, 84),
helix_view::theme::Color::LightCyan,
helix_view::theme::Color::Yellow,
helix_view::theme::Color::Blue,
helix_view::theme::Color::Yellow,
helix_view::theme::Color::Yellow,
helix_view::theme::Color::Green,
helix_view::theme::Color::Blue,
helix_view::theme::Color::Red,
helix_view::theme::Color::Blue,
helix_view::theme::Color::Red,
];
#[derive(Debug, Clone, Copy, PartialEq)]
enum FileType {
File,
@ -39,18 +60,24 @@ enum FileType {
#[derive(Debug, Clone)]
struct FileInfo {
file_type: FileType,
expanded: bool,
path: PathBuf,
}
impl FileInfo {
fn new(path: PathBuf, file_type: FileType) -> Self {
Self { path, file_type }
Self {
path,
file_type,
expanded: false,
}
}
fn root(path: PathBuf) -> Self {
Self {
file_type: FileType::Root,
path,
expanded: true,
}
}
@ -59,6 +86,7 @@ impl FileInfo {
Self {
file_type: FileType::Parent,
path: p.to_path_buf(),
expanded: false,
}
}
@ -159,6 +187,7 @@ impl TreeItem for FileInfo {
Self {
file_type,
path: self.path.join(entry.file_name()),
expanded: false,
}
})
})
@ -167,6 +196,7 @@ impl TreeItem for FileInfo {
ret.push(Self {
path: self.path.clone(),
file_type: FileType::Placeholder,
expanded: false,
})
}
Ok(ret)
@ -179,6 +209,33 @@ impl TreeItem for FileInfo {
self.get_text().contains(s)
}
}
fn icon(&self) -> Option<(&'static str, &'static helix_view::theme::Color)> {
return match self.file_type {
FileType::Dir => {
if self.expanded {
//Some(("", &helix_view::theme::Color::Yellow))
Some(("", &helix_view::theme::Color::Yellow))
} else {
// Some(("", &helix_view::theme::Color::Yellow))
Some(("", &helix_view::theme::Color::Yellow))
}
}
FileType::File => {
for (i, ext) in ICONS_EXT.iter().enumerate() {
if self.get_text().ends_with(ext) {
let color = ICONS_COLORS
.iter()
.nth(i)
.unwrap_or(&helix_view::theme::Color::Blue);
return ICONS.iter().nth(i).map(|c| (*c, color));
}
}
return Some(("", &helix_view::theme::Color::LightBlue));
}
_ => None,
};
}
}
// #[derive(Default, Debug, Clone)]
@ -254,7 +311,9 @@ impl Explorer {
let current_root = std::env::current_dir().unwrap_or_else(|_| "./".into());
let items = Self::get_items(current_root.clone(), cx)?;
Ok(Self {
tree: Tree::build_tree(items).with_enter_fn(Self::toggle_current),
tree: Tree::build_tree(items)
.with_enter_fn(Self::toggle_current)
.with_folded_fn(Self::fold_current),
state: State::new(true, current_root),
repeat_motion: None,
prompt: None,
@ -266,8 +325,9 @@ impl Explorer {
let current_root = std::env::current_dir().unwrap_or_else(|_| "./".into());
let parent = FileInfo::parent(&current_root);
let root = FileInfo::root(current_root.clone());
let mut tree =
Tree::build_from_root(root, usize::MAX / 2)?.with_enter_fn(Self::toggle_current);
let mut tree = Tree::build_from_root(root, usize::MAX / 2)?
.with_enter_fn(Self::toggle_current)
.with_folded_fn(Self::fold_current);
tree.insert_current_level(parent);
Ok(Self {
tree,
@ -319,7 +379,12 @@ impl Explorer {
if item.file_type == FileType::Placeholder {
return;
}
let head_area = render_block(area.clip_bottom(area.height - 2), surface, Borders::BOTTOM);
let head_area = render_block(
area.clip_bottom(area.height - 2),
surface,
Borders::BOTTOM,
None,
);
let path_str = format!("{}", item.path.display());
surface.set_stringn(
head_area.x,
@ -434,6 +499,12 @@ impl Explorer {
));
}
fn fold_current(item: &mut FileInfo, _cx: &mut Context, _state: &mut State) {
if item.path.is_dir() {
item.expanded = false;
}
}
fn toggle_current(
item: &mut FileInfo,
cx: &mut Context,
@ -453,7 +524,7 @@ impl Explorer {
}
};
if meta.is_file() {
if let Err(e) = cx.editor.open(&item.path, Action::Replace) {
if let Err(e) = cx.editor.open(&item.path.clone(), Action::Replace) {
cx.editor.set_error(format!("{e}"));
}
state.focus = false;
@ -461,6 +532,7 @@ impl Explorer {
}
if item.path.is_dir() {
item.expanded = true;
if cx.editor.config().explorer.is_list() || item.file_type == FileType::Parent {
match Self::get_items(item.path.clone(), cx) {
Ok(items) => {
@ -481,19 +553,28 @@ impl Explorer {
let background = cx.editor.theme.get("ui.background");
let column_width = cx.editor.config().explorer.column_width as u16;
surface.clear_with(area, background);
let area = render_block(area, surface, Borders::ALL);
let area = render_block(area, surface, Borders::ALL, None);
let mut preview_area = area.clip_left(column_width + 1);
if let Some((_, prompt)) = self.prompt.as_mut() {
let area = preview_area.clip_bottom(2);
let promp_area =
render_block(preview_area.clip_top(area.height), surface, Borders::TOP);
let promp_area = render_block(
preview_area.clip_top(area.height),
surface,
Borders::TOP,
None,
);
prompt.render(promp_area, surface, cx);
preview_area = area;
}
self.render_preview(preview_area, surface, cx.editor);
let list_area = render_block(area.clip_right(preview_area.width), surface, Borders::RIGHT);
let list_area = render_block(
area.clip_right(preview_area.width),
surface,
Borders::RIGHT,
None,
);
self.tree.render(list_area, surface, cx, &mut self.state);
}
@ -502,14 +583,21 @@ impl Explorer {
let side_area = area
.with_width(area.width.min(config.column_width as u16 + 2))
.clip_bottom(1);
let background = cx.editor.theme.get("ui.background");
let background = cx.editor.theme.get("ui.statusline");
surface.clear_with(side_area, background);
let preview_area = area.clip_left(side_area.width).clip_bottom(2);
let prompt_area = area.clip_top(side_area.height);
let list_area =
render_block(side_area.clip_left(1), surface, Borders::RIGHT).clip_bottom(1);
let border_style = cx.editor.theme.get("ui.explorer.border");
let list_area = render_block(
side_area.clip_left(1),
surface,
Borders::RIGHT,
Some(border_style),
)
.clip_bottom(1);
self.tree.render(list_area, surface, cx, &mut self.state);
{
@ -543,7 +631,7 @@ impl Explorer {
}
let area = Rect::new(preview_area.x, y, width, height);
surface.clear_with(area, background);
let area = render_block(area, surface, Borders::all());
let area = render_block(area, surface, Borders::all(), None);
self.render_preview(area, surface, cx.editor);
}
@ -569,9 +657,7 @@ impl Explorer {
}
key!(Esc) | ctrl!('c') => self.tree.restore_recycle(),
_ => {
if let EventResult::Consumed(_) =
prompt.handle_event(&Event::Key(event.clone()), cx)
{
if let EventResult::Consumed(_) = prompt.handle_event(&Event::Key(*event), cx) {
self.tree.filter(prompt.line(), cx, &mut self.state);
}
self.prompt = Some((action, prompt));
@ -618,9 +704,7 @@ impl Explorer {
}
key!(Esc) | ctrl!('c') => self.tree.restore_view(),
_ => {
if let EventResult::Consumed(_) =
prompt.handle_event(&Event::Key(event.clone()), cx)
{
if let EventResult::Consumed(_) = prompt.handle_event(&Event::Key(*event), cx) {
if search_next {
self.tree.search_next(cx, prompt.line(), &mut self.state);
} else {
@ -676,7 +760,7 @@ impl Explorer {
}
(_, key!(Esc) | ctrl!('c')) => {}
_ => {
prompt.handle_event(&Event::Key(event.clone()), cx);
prompt.handle_event(&Event::Key(*event), cx);
self.prompt = Some((action, prompt));
}
}
@ -720,7 +804,7 @@ impl Component for Explorer {
/// Process input events, return true if handled.
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
let key_event = match event {
Event::Key(event) => event,
Event::Key(event) => *event,
Event::Resize(..) => return EventResult::Consumed(None),
_ => return EventResult::Ignored(None),
};
@ -728,7 +812,7 @@ impl Component for Explorer {
return EventResult::Ignored(None);
}
if let Some(mut on_next_key) = self.on_next_key.take() {
return on_next_key(cx, self, key_event);
return on_next_key(cx, self, &key_event);
}
if let EventResult::Consumed(c) = self.handle_prompt_event(&key_event, cx) {
@ -761,7 +845,9 @@ impl Component for Explorer {
match Self::get_items(p.to_path_buf(), cx) {
Ok(items) => {
self.state.current_root = p.to_path_buf();
self.tree = Tree::build_tree(items).with_enter_fn(Self::toggle_current);
self.tree = Tree::build_tree(items)
.with_enter_fn(Self::toggle_current)
.with_folded_fn(Self::fold_current);
}
Err(e) => cx.editor.set_error(format!("{e}")),
}
@ -863,8 +949,17 @@ fn get_preview(p: impl AsRef<Path>, max_line: usize) -> Result<Vec<String>> {
.collect())
}
fn render_block(area: Rect, surface: &mut Surface, borders: Borders) -> Rect {
let block = Block::default().borders(borders);
fn render_block(
area: Rect,
surface: &mut Surface,
borders: Borders,
border_style: Option<helix_view::theme::Style>,
) -> Rect {
let mut block = Block::default().borders(borders);
if let Some(style) = border_style {
block = block.border_style(style);
}
//let block = Block::default();
let inner = block.inner(area);
block.render(area, surface);
inner

@ -20,6 +20,7 @@ pub trait TreeItem: Sized {
fn text(&self, cx: &mut Context, selected: bool, params: &mut Self::Params) -> Spans;
fn is_child(&self, other: &Self) -> bool;
fn cmp(&self, other: &Self) -> Ordering;
fn icon(&self) -> Option<(&'static str, &'static helix_view::theme::Color)>;
fn filter(&self, cx: &mut Context, s: &str, params: &mut Self::Params) -> bool {
self.text(cx, false, params)
@ -178,7 +179,7 @@ impl<T: TreeItem> Tree<T> {
col: 0,
max_len: 0,
count: 0,
tree_symbol_style: "ui.text".into(),
tree_symbol_style: "ui.explorer.guide".into(),
pre_render: None,
on_opened_fn: None,
on_folded_fn: None,
@ -471,6 +472,7 @@ impl<T: TreeItem> Tree<T> {
self.max_len = 0;
self.row = std::cmp::min(self.row, area.height.saturating_sub(1) as usize);
let style = cx.editor.theme.get(&self.tree_symbol_style);
let folder_style = cx.editor.theme.get("special");
let last_item_index = self.items.len().saturating_sub(1);
let skip = self.selected.saturating_sub(self.row);
let iter = self
@ -484,9 +486,9 @@ impl<T: TreeItem> Tree<T> {
let mut area = Rect::new(area.x, area.y + row, area.width, 1);
let indent = if elem.level > 0 {
if index + skip != last_item_index {
format!("{}├─", "│ ".repeat(elem.level - 1))
format!("{}", "│ ".repeat(elem.level - 1))
} else {
format!("└─{}", "┴─".repeat(elem.level - 1))
format!("{}", "".repeat(elem.level - 1))
}
} else {
"".to_string()
@ -502,14 +504,20 @@ impl<T: TreeItem> Tree<T> {
};
let mut start_index = self.col.saturating_sub(indent_len);
let mut text = elem.item.text(cx, skip + index == self.selected, params);
self.max_len = self.max_len.max(text.width() + indent.len());
self.max_len = self.max_len.max(text.width() + indent.len() - 2);
for span in text.0.iter_mut() {
if area.width == 0 {
return;
}
if start_index == 0 {
surface.set_span(area.x, area.y, span, area.width);
area = area.clip_left(span.width() as u16);
let mut icon_offset = 0;
if let Some((icon, color)) = elem.item.icon() {
let style = folder_style.fg(*color);
surface.set_string(area.x, area.y, icon, style);
icon_offset = 2;
}
surface.set_span(area.x + icon_offset, area.y, span, area.width - icon_offset);
area = area.clip_left((span.width() - icon_offset as usize) as u16);
} else {
let span_width = span.width();
if start_index > span_width {
@ -527,10 +535,13 @@ impl<T: TreeItem> Tree<T> {
}
})
.collect();
let mut cont = String::new();
cont.push_str("");
cont.push_str(&content);
surface.set_string_truncated(
area.x,
area.y,
&content,
&cont,
area.width as usize,
|_| span.style,
false,

@ -134,31 +134,19 @@ pub struct ExplorerConfig {
impl ExplorerConfig {
pub fn is_embed(&self) -> bool {
match self.position {
ExplorerPosition::Embed => true,
ExplorerPosition::Overlay => false,
}
return self.position == ExplorerPosition::Embed;
}
pub fn is_overlay(&self) -> bool {
match self.position {
ExplorerPosition::Embed => false,
ExplorerPosition::Overlay => true,
}
return self.position == ExplorerPosition::Overlay;
}
pub fn is_list(&self) -> bool {
match self.style {
ExplorerStyle::List => true,
ExplorerStyle::Tree => false,
}
return self.style == ExplorerStyle::List;
}
pub fn is_tree(&self) -> bool {
match self.style {
ExplorerStyle::List => false,
ExplorerStyle::Tree => true,
}
return self.style == ExplorerStyle::Tree;
}
}

Loading…
Cancel
Save