Skip to content

Commit c52f51a

Browse files
committed
Refacto: rewrite everything
- The tree is created with libuv functions, which makes it blazingly fast. - The tree may now be faster than any other vim trees, it can handle directories with thousands of files without any latency at all (tested on 40K files, works flawlessly). - More solid logic for opening and closing the tree. - tree state is remembered (closing / opening a folder keeps opened subdirectories open) - detection of multiple git projects in the tree - more icon support - smart rendering - smart updates - ms windows support - gx replacement function running xdg-open on linux, open on macos
1 parent e1fbabf commit c52f51a

File tree

16 files changed

+1061
-1003
lines changed

16 files changed

+1061
-1003
lines changed

README.md

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
## Notice
44

5-
- This plugin does not work on windows.
5+
This plugin doesn't support windows
66

77
## Install
88

99
Install with [vim-plug](https://github.com/junegunn/vim-plug):
1010
```vim
11+
Plug 'kyazdani42/nvim-web-devicons' " for file icons
1112
Plug 'kyazdani42/nvim-tree.lua'
1213
```
1314

@@ -16,19 +17,18 @@ Plug 'kyazdani42/nvim-tree.lua'
1617
```vim
1718
let g:lua_tree_side = 'right' | 'left' "left by default
1819
let g:lua_tree_size = 40 "30 by default
19-
let g:lua_tree_ignore = [ '.git', 'node_modules', '.cache' ] "empty by default, not working on mac atm
20+
let g:lua_tree_ignore = [ '.git', 'node_modules', '.cache' ] "empty by default
2021
let g:lua_tree_auto_open = 1 "0 by default, opens the tree when typing `vim $DIR` or `vim`
2122
let g:lua_tree_auto_close = 1 "0 by default, closes the tree when it's the last window
22-
let g:lua_tree_follow = 1 "0 by default, this option will bind BufEnter to the LuaTreeFindFile command
23-
" :help LuaTreeFindFile for more info
23+
let g:lua_tree_follow = 1 "0 by default, this option allows the cursor to be updated when entering a buffer
2424
let g:lua_tree_show_icons = {
2525
\ 'git': 1,
2626
\ 'folders': 0,
2727
\ 'files': 0,
2828
\}
2929
"If 0, do not show the icons for one of 'git' 'folder' and 'files'
3030
"1 by default, notice that if 'files' is 1, it will only display
31-
"if web-devicons is installed and on your runtimepath
31+
"if nvim-web-devicons is installed and on your runtimepath
3232
3333
" You can edit keybindings be defining this variable
3434
" You don't have to define all keys.
@@ -44,14 +44,17 @@ let g:lua_tree_bindings = {
4444
\ 'rename': 'r'
4545
\ }
4646
47+
let g:lua_tree_icons = {
48+
\
49+
\ }
50+
4751
nnoremap <C-n> :LuaTreeToggle<CR>
4852
nnoremap <leader>r :LuaTreeRefresh<CR>
4953
nnoremap <leader>n :LuaTreeFindFile<CR>
5054
5155
set termguicolors " this variable must be enabled for colors to be applied properly
5256
5357
" a list of groups can be found at `:help lua_tree_highlight`
54-
highlight LuaTreeFolderName guibg=cyan gui=bold,underline
5558
highlight LuaTreeFolderIcon guibg=blue
5659
```
5760

@@ -60,7 +63,8 @@ highlight LuaTreeFolderIcon guibg=blue
6063
- move around like in any vim buffer
6164
- `<CR>` on `..` will cd in the above directory
6265
- `.` will cd in the directory under the cursor
63-
- type `a` to add a file
66+
- type `a` to add a file. Adding a directory requires leaving a leading `/` at the end of the path.
67+
> you can add multiple directories by doing foo/bar/baz/f and it will add foo bar and baz directories and f as a file
6468
- type `r` to rename a file
6569
- type `d` to delete a file (will prompt for confirmation)
6670
- if the file is a directory, `<CR>` will open the directory
@@ -72,21 +76,20 @@ highlight LuaTreeFolderIcon guibg=blue
7276
- Double left click acts like `<CR>`
7377
- Double right click acts like `.`
7478

79+
## Note
80+
81+
This plugin is very fast. It has been tested on 40K items which opens instantly. \
82+
The performance of that plugin is due to the use of the `libuv` `scandir` and `scandir_next` functions instead of spawning an `ls` process which can get slow on large files when combining with `stat` to get file informations.
83+
7584
## Features
76-
- [x] Open file in current buffer or in split with FzF like bindings (`<CR>`, `<C-v>`, `<C-x>`, `<C-t>`)
77-
- [x] File icons with vim-devicons
78-
- [x] Syntax highlighting ([exa](https://github.com/ogham/exa) like)
79-
- [x] Change directory with `.`
80-
- [x] Add / Rename / delete files
81-
- [x] Git integration
82-
- [x] Mouse support
85+
- Open file in current buffer or in split with FzF like bindings (`<CR>`, `<C-v>`, `<C-x>`, `<C-t>`)
86+
- File icons with vim-devicons
87+
- Syntax highlighting ([exa](https://github.com/ogham/exa) like)
88+
- Change directory with `.`
89+
- Add / Rename / delete files
90+
- Git integration
91+
- Mouse support
8392

8493
## Screenshot
8594

8695
![alt text](.github/screenshot.png?raw=true "file explorer")
87-
88-
## TODO
89-
90-
- Tree creation could be async
91-
- bufferize tree
92-
- better default colors (use vim highlight groups)

doc/nvim-tree-lua.txt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ open the tree with :LuaTreeToggle
1919
==============================================================================
2020
COMMANDS *nvim-tree-commands*
2121

22+
|:LuaTreeOpen| *:LuaTreeOpen*
23+
24+
opens the tree
25+
26+
|:LuaTreeClose| *:LuaTreeClose*
27+
28+
closes the tree
29+
2230
|:LuaTreeToggle| *:LuaTreeToggle*
2331

2432
open or close the tree
@@ -32,7 +40,8 @@ refresh the tree
3240
The command will change the cursor in the tree for the current bufname.
3341

3442
It will also open the leafs of the tree leading to the file in the buffer
35-
(if you opened a file with something else than the LuaTree, like `fzf`)
43+
(if you opened a file with something else than the LuaTree, like `fzf` or
44+
`:split`)
3645

3746
==============================================================================
3847
OPTIONS *nvim-tree-options*
@@ -130,8 +139,7 @@ default keybindings will be applied to undefined keys.
130139
File icons with vim-devicons.
131140

132141
Uses other type of icons so a good font support is recommended.
133-
If the tree renders weird glyphs, install correct fonts or try to change
134-
your terminal.
142+
If the tree renders weird glyphs, install the correct fonts.
135143

136144
Syntax highlighting uses g:terminal_color_ from colorschemes, fallbacks to
137145
ugly colors otherwise.

lua/lib/colors.lua

Lines changed: 83 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,56 @@
11
local api = vim.api
2-
local get_colors = require 'lib/config'.get_colors
3-
4-
local colors = get_colors()
52

63
local M = {}
74

8-
local function create_hl()
5+
local function get_color_from_hl(hl_name, fallback)
6+
local id = vim.api.nvim_get_hl_id_by_name(hl_name)
7+
if not id then return fallback end
8+
9+
local hl = vim.api.nvim_get_hl_by_id(id, true)
10+
if not hl or not hl.foreground then return fallback end
11+
12+
return hl.foreground
13+
end
14+
15+
local function get_colors()
16+
return {
17+
red = vim.g.terminal_color_1 or get_color_from_hl('Keyword', 'Red'),
18+
green = vim.g.terminal_color_2 or get_color_from_hl('Character', 'Green'),
19+
yellow = vim.g.terminal_color_3 or get_color_from_hl('PreProc', 'Yellow'),
20+
blue = vim.g.terminal_color_4 or get_color_from_hl('Include', 'Blue'),
21+
purple = vim.g.terminal_color_5 or get_color_from_hl('Define', 'Purple'),
22+
cyan = vim.g.terminal_color_6 or get_color_from_hl('Conditional', 'Cyan'),
23+
dark_red = vim.g.terminal_color_9 or get_color_from_hl('Keyword', 'DarkRed'),
24+
orange = vim.g.terminal_color_11 or get_color_from_hl('Number', 'Orange'),
25+
}
26+
end
27+
28+
local function get_hl_groups()
29+
local colors = get_colors()
30+
931
return {
1032
Symlink = { gui = 'bold', fg = colors.cyan },
11-
FolderName = { gui = 'bold', fg = colors.blue },
1233
FolderIcon = { fg = '#90a4ae' },
1334

1435
ExecFile = { gui = 'bold', fg = colors.green },
1536
SpecialFile = { gui = 'bold,underline', fg = colors.yellow },
1637
ImageFile = { gui = 'bold', fg = colors.purple },
17-
MarkdownFile = { fg = colors.purple },
38+
39+
GitDirty = { fg = colors.dark_red },
40+
GitStaged = { fg = colors.green },
41+
GitMerge = { fg = colors.orange },
42+
GitRenamed = { fg = colors.purple },
43+
GitNew = { fg = colors.yellow },
44+
45+
-- TODO: remove those when we add this to nvim-web-devicons
46+
MarkdownIcon = { fg = colors.purple },
1847
LicenseIcon = { fg = colors.yellow },
1948
YamlIcon = { fg = colors.yellow },
2049
TomlIcon = { fg = colors.yellow },
2150
GitignoreIcon = { fg = colors.yellow },
2251
JsonIcon = { fg = colors.yellow },
23-
2452
LuaIcon = { fg = '#42a5f5' },
53+
GoIcon = { fg = '#7Fd5EA' },
2554
PythonIcon = { fg = colors.green },
2655
ShellIcon = { fg = colors.green },
2756
JavascriptIcon = { fg = colors.yellow },
@@ -31,34 +60,62 @@ local function create_hl()
3160
RustIcon = { fg = colors.orange },
3261
VimIcon = { fg = colors.green },
3362
TypescriptIcon = { fg = colors.blue },
34-
35-
GitDirty = { fg = colors.dark_red },
36-
GitStaged = { fg = colors.green },
37-
GitMerge = { fg = colors.orange },
38-
GitRenamed = { fg = colors.purple },
39-
GitNew = { fg = colors.yellow }
4063
}
4164
end
4265

43-
local HIGHLIGHTS = create_hl()
44-
45-
local LINKS = {
46-
Normal = 'Normal',
47-
EndOfBuffer = 'EndOfBuffer',
48-
CursorLine = 'CursorLine',
49-
VertSplit = 'VertSplit',
50-
CursorColumn = 'CursorColumn'
66+
-- TODO: remove those when we add this to nvim-web-devicons
67+
M.hl_groups = {
68+
['LICENSE'] = 'LicenseIcon';
69+
['license'] = 'LicenseIcon';
70+
['vim'] = 'VimIcon';
71+
['.vimrc'] = 'VimIcon';
72+
['c'] = 'CIcon';
73+
['cpp'] = 'CIcon';
74+
['python'] = 'PythonIcon';
75+
['lua'] = 'LuaIcon';
76+
['rs'] = 'RustIcon';
77+
['sh'] = 'ShellIcon';
78+
['csh'] = 'ShellIcon';
79+
['zsh'] = 'ShellIcon';
80+
['bash'] = 'ShellIcon';
81+
['md'] = 'MarkdownIcon';
82+
['json'] = 'JsonIcon';
83+
['toml'] = 'TomlIcon';
84+
['go'] = 'GoIcon';
85+
['yaml'] = 'YamlIcon';
86+
['yml'] = 'YamlIcon';
87+
['conf'] = 'GitignoreIcon';
88+
['javascript'] = 'JavascriptIcon';
89+
['typescript'] = 'TypescriptIcon';
90+
['jsx'] = 'ReactIcon';
91+
['tsx'] = 'ReactIcon';
92+
['htm'] = 'HtmlIcon';
93+
['html'] = 'HtmlIcon';
94+
['slim'] = 'HtmlIcon';
95+
['haml'] = 'HtmlIcon';
96+
['ejs'] = 'HtmlIcon';
5197
}
5298

53-
function M.init_colors()
54-
colors = get_colors()
55-
HIGHLIGHTS = create_hl()
56-
for k, d in pairs(HIGHLIGHTS) do
99+
local function get_links()
100+
return {
101+
FolderName = 'Directory',
102+
Normal = 'Normal',
103+
EndOfBuffer = 'EndOfBuffer',
104+
CursorLine = 'CursorLine',
105+
VertSplit = 'VertSplit',
106+
CursorColumn = 'CursorColumn'
107+
}
108+
end
109+
110+
function M.setup()
111+
local higlight_groups = get_hl_groups()
112+
for k, d in pairs(higlight_groups) do
57113
local gui = d.gui or 'NONE'
58114
api.nvim_command('hi def LuaTree'..k..' gui='..gui..' guifg='..d.fg)
59115
end
60116

61-
for k, d in pairs(LINKS) do
117+
local links = get_links()
118+
for k, d in pairs(links) do
62119
api.nvim_command('hi def link LuaTree'..k..' '..d)
63120
end
64121
end

lua/lib/config.lua

Lines changed: 39 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,50 @@
1-
local api = vim.api
2-
31
local M = {}
42

5-
local function get(var, fallback)
6-
if api.nvim_call_function('exists', { var }) == 1 then
7-
return api.nvim_get_var(var)
8-
else
9-
return fallback
10-
end
11-
end
12-
13-
local function get_color_from_hl(hl_name, fallback)
14-
local id = api.nvim_get_hl_id_by_name(hl_name)
15-
if not id then return fallback end
3+
function M.get_icon_state()
4+
local show_icons = vim.g.lua_tree_show_icons or { git = 1, folders = 1, files = 1 }
5+
local icons = {
6+
default = nil,
7+
git_icons = {
8+
unstaged = "",
9+
staged = "",
10+
unmerged = "",
11+
renamed = "",
12+
untracked = ""
13+
}
14+
}
1615

17-
local hl = api.nvim_get_hl_by_id(id, true)
18-
if not hl or not hl.foreground then return fallback end
16+
local user_icons = vim.g.lua_tree_icons
17+
if user_icons then
18+
if user_icons.default then
19+
icons.default = user_icons.default
20+
end
21+
for key, val in pairs(user_icons.git) do
22+
if icons.git_icons[key] then
23+
icons.git_icons[key] = val
24+
end
25+
end
26+
end
1927

20-
return hl.foreground
28+
return {
29+
show_file_icon = show_icons.files == 1 and vim.g.nvim_web_devicons == 1,
30+
show_folder_icon = show_icons.folders == 1,
31+
show_git_icon = show_icons.git == 1,
32+
icons = icons
33+
}
2134
end
2235

23-
local HAS_DEV_ICONS = api.nvim_call_function('exists', { "*WebDevIconsGetFileTypeSymbol" }) == 1
24-
25-
local show_icons = get('lua_tree_show_icons', { git = 1, folders = 1, files = 1 })
26-
27-
M.SHOW_FILE_ICON = HAS_DEV_ICONS and show_icons.files == 1
28-
M.SHOW_FOLDER_ICON = show_icons.folders == 1
29-
M.SHOW_GIT_ICON = show_icons.git == 1
30-
31-
function M.get_colors()
36+
function M.get_bindings()
37+
local keybindings = vim.g.lua_tree_bindings or {}
3238
return {
33-
red = get('terminal_color_1', get_color_from_hl('Keyword', 'Red')),
34-
green = get('terminal_color_2', get_color_from_hl('Character', 'Green')),
35-
yellow = get('terminal_color_3', get_color_from_hl('PreProc', 'Yellow')),
36-
blue = get('terminal_color_4', get_color_from_hl('Include', 'Blue')),
37-
purple = get('terminal_color_5', get_color_from_hl('Define', 'Purple')),
38-
cyan = get('terminal_color_6', get_color_from_hl('Conditional', 'Cyan')),
39-
orange = get('terminal_color_11', get_color_from_hl('Number', 'Orange')),
40-
dark_red = get('terminal_color_9', get_color_from_hl('Keyword', 'DarkRed')),
39+
edit = keybindings.edit or '<CR>',
40+
edit_vsplit = keybindings.edit_vsplit or '<C-v>',
41+
edit_split = keybindings.edit_split or '<C-x>',
42+
edit_tab = keybindings.edit_tab or '<C-t>',
43+
cd = keybindings.cd or '<C-]>',
44+
create = keybindings.create or 'a',
45+
remove = keybindings.remove or 'd',
46+
rename = keybindings.rename or 'r',
4147
}
4248
end
4349

44-
local keybindings = get('lua_tree_bindings', {});
45-
46-
M.bindings = {
47-
edit = keybindings.edit or '<CR>',
48-
edit_vsplit = keybindings.edit_vsplit or '<C-v>',
49-
edit_split = keybindings.edit_split or '<C-x>',
50-
edit_tab = keybindings.edit_tab or '<C-t>',
51-
cd = keybindings.cd or '.',
52-
create = keybindings.create or 'a',
53-
remove = keybindings.remove or 'd',
54-
rename = keybindings.rename or 'r',
55-
}
56-
5750
return M

0 commit comments

Comments
 (0)