Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File explorer and tree helper #2377

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions book/src/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ auto-pairs = false # defaults to `true`
The default pairs are <code>(){}[]''""``</code>, but these can be customized by
setting `auto-pairs` to a TOML table:

Example

```toml
[editor.auto-pairs]
'(' = ')'
Expand Down Expand Up @@ -229,3 +231,12 @@ Example:
render = true
character = "╎"
```

### `[editor.explorer]` Section
Sets explorer side width and style.

| Key | Description | Default |
| --- | ----------- | ------- |
| `column-width` | explorer side width | 30 |
| `style` | explorer item style, tree or list | tree |
| `position` | explorer widget position, embed or overlay | overlay |
34 changes: 34 additions & 0 deletions book/src/keymap.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,8 @@ This layer is a kludge of mappings, mostly pickers.
| `R` | Replace selections by clipboard contents | `replace_selections_with_clipboard` |
| `/` | Global search in workspace folder | `global_search` |
| `?` | Open command palette | `command_palette` |
| `e` | Open or focus explorer | `toggle_or_focus_explorer` |
| `E` | open explorer recursion | `open_explorer_recursion` |

> TIP: Global search displays results in a fuzzy picker, use `space + '` to bring it back up after opening a file.

Expand Down Expand Up @@ -402,3 +404,35 @@ Keys to use within prompt, Remapping currently not supported.
| `Tab` | Select next completion item |
| `BackTab` | Select previous completion item |
| `Enter` | Open selected |

# File explorer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be called tree explorer rather than file explorer? Given that later tree explorer functionality may be reused for something else.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just my two cents, but I have my doubts...

"tree explorer" sounds like a name which could be used in the code because - as you correctly point out - it could share its code base for different purposes. However, IMHO that name should not be presented to the user:

The user explores... files. Not trees. 🌳

Add to that the fact that "file explorer" is a pretty common term, e.g. in desktop environments, but also in other editors/IDEs.

If we really want to use the word "tree", I could imagine the name "file tree", but I still find "file explorer" more intuitive. :)

Keys to use within explorer, Remapping currently not supported.

| Key | Description |
| ----- | ------------- |
| `Escape` | Back to editor |
| `Ctrl-c` | Close explorer |
| `Enter` | Open file or toggle dir selected |
| `b` | Back to current root's parent |
| `f` | Filter items |
| `z` | Fold currrent level |
| `k`, `Shift-Tab`, `Up` | select previous item |
| `j`, `Tab`, `Down` | select next item |
Comment on lines +419 to +420
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to these keybindings, I think adding Ctrl-n for next and Ctrl-p for previous would be good defaults, as that matches the bindings for the file picker.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Space-e should close the explorer if you are focused on it over Ctrl-c.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or even ignore focus, Space-e just toggles the explorer. Then there is no need to focus the explorer in order to close it

| `h` | Scroll left |
| `l` | Scroll right |
| `G` | Move to last item |
| `Ctrl-d` | Move down half page |
| `Ctrl-u` | Move up half page |
| `Shift-d` | Move down a page |
| `Shift-u` | Move up a page |
| `/` | Search item |
| `?` | Search item reverse |
| `n` | Repeat last search |
| `Shift-n` | Repeat last search reverse |
| `gg` | Move to first item |
| `ge` | Move to last item |
Copy link

@well1791 well1791 Nov 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is already there

`G`                | Move to last item

better merge it with the other available option

Suggested change
| `ge` | Move to last item |
| `ge`, `G` | Move to last item |

| `gc` | Make current dir as root dir |
| `mf` | Create new file under current item's parent |
| `md` | Create new dir under current item's parent |
| `rf` | Remove file selected |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a suggestion on usability, probably it would be good to include that the removal will prompt for confirmation

| `rf`                     | Remove the selected file with confirmation
| `rd`                     | Remove the selected dir with confirmation

I mean, probably in the future there would be an option to not require confirmation, maybe some rF and/or rD

| `rd` | Remove dir selected |
42 changes: 41 additions & 1 deletion helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,10 @@ impl MappableCommand {
decrement, "Decrement item under cursor",
record_macro, "Record macro",
replay_macro, "Replay macro",
command_palette, "Open command palette",
command_palette, "Open command pallete",
toggle_or_focus_explorer, "toggle or focus explorer",
open_explorer_recursion, "open explorer recursion",
close_explorer, "close explorer",
);
}

Expand Down Expand Up @@ -2213,6 +2216,43 @@ fn file_picker_in_current_directory(cx: &mut Context) {
cx.push_layer(Box::new(overlayed(picker)));
}

fn toggle_or_focus_explorer(cx: &mut Context) {
cx.callback = Some(Box::new(
|compositor: &mut Compositor, cx: &mut compositor::Context| {
if let Some(editor) = compositor.find::<ui::EditorView>() {
match editor.explorer.as_mut() {
Some(explore) => explore.content.focus(),
None => match ui::Explorer::new(cx) {
Ok(explore) => editor.explorer = Some(overlayed(explore)),
Err(err) => cx.editor.set_error(format!("{}", err)),
},
}
}
},
));
}

fn open_explorer_recursion(cx: &mut Context) {
cx.callback = Some(Box::new(
|compositor: &mut Compositor, cx: &mut compositor::Context| {
if let Some(editor) = compositor.find::<ui::EditorView>() {
match ui::Explorer::new_explorer_recursion() {
Ok(explore) => editor.explorer = Some(overlayed(explore)),
Err(err) => cx.editor.set_error(format!("{}", err)),
}
}
},
));
}

fn close_explorer(cx: &mut Context) {
cx.callback = Some(Box::new(|compositor: &mut Compositor, _| {
if let Some(editor) = compositor.find::<ui::EditorView>() {
editor.explorer.take();
}
}));
}

fn buffer_picker(cx: &mut Context) {
let current = view!(cx.editor).doc;

Expand Down
2 changes: 2 additions & 0 deletions helix-term/src/keymap/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ pub fn default() -> HashMap<Mode, Keymap> {
"r" => rename_symbol,
"h" => select_references_to_symbol_under_cursor,
"?" => command_palette,
"e" => toggle_or_focus_explorer,
"E" => open_explorer_recursion,
},
"z" => { "View"
"z" | "c" => align_view_center,
Expand Down
42 changes: 40 additions & 2 deletions helix-term/src/ui/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
compositor::{Component, Context, Event, EventResult},
job, key,
keymap::{KeymapResult, Keymaps},
ui::{Completion, ProgressSpinners},
ui::{overlay::Overlay, Completion, Explorer, ProgressSpinners},
};

use helix_core::{
Expand Down Expand Up @@ -36,6 +36,7 @@ pub struct EditorView {
last_insert: (commands::MappableCommand, Vec<InsertEvent>),
pub(crate) completion: Option<Completion>,
spinners: ProgressSpinners,
pub(crate) explorer: Option<Overlay<Explorer>>,
}

#[derive(Debug, Clone)]
Expand All @@ -59,6 +60,7 @@ impl EditorView {
last_insert: (commands::MappableCommand::normal_mode, Vec::new()),
completion: None,
spinners: ProgressSpinners::default(),
explorer: None,
}
}

Expand Down Expand Up @@ -1118,6 +1120,11 @@ impl Component for EditorView {
event: Event,
context: &mut crate::compositor::Context,
) -> EventResult {
if let Some(explore) = self.explorer.as_mut() {
if let EventResult::Consumed(callback) = explore.handle_event(event, context) {
return EventResult::Consumed(callback);
}
}
let mut cx = commands::Context {
editor: context.editor,
count: None,
Expand Down Expand Up @@ -1260,7 +1267,17 @@ impl Component for EditorView {
surface.set_style(area, cx.editor.theme.get("ui.background"));
let config = cx.editor.config();
// if the terminal size suddenly changed, we need to trigger a resize
cx.editor.resize(area.clip_bottom(1)); // -1 from bottom for commandline
let mut editor_area = area.clip_bottom(1);
if self.explorer.is_some() && (config.explorer.is_embed()) {
editor_area = editor_area.clip_left(config.explorer.column_width as u16 + 2);
}
cx.editor.resize(editor_area); // -1 from bottom for commandline

if let Some(explore) = self.explorer.as_mut() {
if !explore.content.is_focus() && config.explorer.is_embed() {
explore.content.render(area, surface, cx);
}
}

for (view, is_focused) in cx.editor.tree.views() {
let doc = cx.editor.document(view.doc).unwrap();
Expand Down Expand Up @@ -1336,9 +1353,30 @@ impl Component for EditorView {
if let Some(completion) = self.completion.as_mut() {
completion.render(area, surface, cx);
}

if let Some(explore) = self.explorer.as_mut() {
if explore.content.is_focus() {
if config.explorer.is_embed() {
explore.content.render(area, surface, cx);
} else {
explore.render(area, surface, cx);
}
}
}
}

fn cursor(&self, _area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {
if let Some(explore) = &self.explorer {
if explore.content.is_focus() {
if editor.config().explorer.is_overlay() {
return explore.cursor(_area, editor);
}
let cursor = explore.content.cursor(_area, editor);
if cursor.0.is_some() {
return cursor;
}
}
}
match editor.cursor() {
// All block cursors are drawn manually
(pos, CursorKind::Block) => (pos, CursorKind::Hidden),
Expand Down
Loading