Skip to content

Commit 97a4d95

Browse files
authored
refactor: closes #69, move common functionality into new source manager module (#81)
1 parent edfdf0e commit 97a4d95

File tree

18 files changed

+579
-860
lines changed

18 files changed

+579
-860
lines changed

README.md

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,34 @@ structures in a sidebar **or** floating window.
55

66
![Neo-tree file system](https://github.com/nvim-neo-tree/resources/raw/main/images/Neo-tree-filesystem.png)
77

8+
### Breaking Changes BAD :bomb: :imp:
9+
10+
The biggest and most important feature of Neo-tree is that we will never
11+
knowingly push a breaking change and interrupt your day. Bugs happen, but
12+
breaking changes can always be avoided. When breaking changes are needed, there
13+
will be a new branch that you can opt into, when it is a good time for you.
14+
15+
### User Experience GOOD :slightly_smiling_face: :thumbsup:
16+
17+
Aside from being polite about breaking changes, Neo-tree is also focused on the
18+
little details of user experience. Everything should work exactly as you would
19+
expect a sidebar to work without all of the glitchy behavior that is normally
20+
accepted in (neo)vim sidebars. I can't stand glitchy behavior, and neither
21+
should you!
22+
23+
- Neo-tree won't let other buffers take over it's window.
24+
- Neo-tree won't leave it's window scrolled to the last line when there is
25+
plenty of room to display the whole tree.
26+
- Neo-tree does not need to be manually refreshed (set `use_libuv_file_watcher=true`)
27+
- Neo-tree can intelligently follow the current file (set `follow_current_file=true`)
28+
- Neo-tree is thoughtful about maintaining or setting focus on the right node
29+
- Neo-tree windows in different tabs are completely separate
30+
31+
Neo-tree is smooth, efficient, stable, and pays attention to the little details.
32+
If you find anything janky, wanky, broken, or unintuitive, please open an issue
33+
so we can fix it.
34+
35+
836
## Quickstart
937

1038
Example for packer:
@@ -152,7 +180,7 @@ highlighting can also be viewed at the [filesystem README](/lua/neo-tree/sources
152180
## Sources
153181

154182
Neo-tree is built on the idea of supporting various sources. Sources are
155-
basically interface implimentations whose job it is to provide a list of
183+
basically interface implementations whose job it is to provide a list of
156184
hierachical items to be rendered, along with commands that are appropriate to
157185
those items.
158186

@@ -180,33 +208,22 @@ filesystem is open in a sidebar:
180208
![Neo-tree git_status](https://github.com/nvim-neo-tree/resources/raw/main/images/Neo-tree-git_status.png)
181209

182210

183-
184-
## Status
185-
186-
This is a fully functional file browser with navigation, mutation,
187-
git status, and filtering. It can also display a list of open buffers. Other
188-
sources that may be added include things like tags, treesitter or lsp document
189-
structures, git status, etc.
190-
191211
## Configuration and Customization
192212

193213
This is designed to be flexible. The way that is acheived is by making
194-
everything a function, or a reference to a built-in function. All of the
195-
built-in functions can be replaced with your own implimentation, or you can
214+
everything a function, or a string that identifies a built-in function. All of the
215+
built-in functions can be replaced with your own implementation, or you can
196216
add new ones.
197217

198218
Each node in the tree is created from the renderer specified for the given node
199-
type, and each renderer is a list of component configs. Each component is a
200-
function, either built-in or specified in your own the setup() config. Those
201-
functions are called with the config, node, and state of the plugin, and return
202-
the text and highlight group for the component.
203-
204-
Additionally, each source has a `before_render()` function that you can
205-
override and use to gather any additonal information you want to use in your
206-
components. This function is currently used to gather the git status and
207-
diagnostics for the tree. If you want to skip that, override the function and
208-
leave that part out. If you want to show some other data, gather it in
209-
`before_render()`, create a component to display it, and reference that
219+
type, and each renderer is a list of component configs to be rendered in order
220+
for each node in the tree. Each component is a function, either built-in or
221+
specified in your config. Those functions are called with the config, node, and
222+
state of the plugin, and return the text and highlight group for the component.
223+
224+
Additionally, there is an events system that you can hook into. If you want to
225+
show some new data point related to your files, gather it in the
226+
`before_render` event, create a component to display it, and reference that
210227
component in the renderer for the `file` and/or `directory` type.
211228

212229
Details on how to configure everything is in the help file at `:h neo-tree` or
@@ -225,10 +242,8 @@ wanted something that was:
225242
### Easy to maintain and enhance
226243

227244
This plugin is designed to grow and be flexible. This is accomplished by making
228-
the code as decoupled and functional as possible. It shouldn't be necessary to
229-
touch any of the core plumbing to add new functionality. Aside from bug fixes,
230-
the code outside of the `sources` directory should not be touched to add new
231-
features. Hopefully new contributors will find it easy to work with.
245+
the code as decoupled and functional as possible. Hopefully new contributors
246+
will find it easy to work with.
232247

233248
One big difference between this plugin and the ones that came before it, which
234249
is also what finally pushed me over the edge into making a new plugin, is that

doc/neo-tree.txt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -465,23 +465,30 @@ require("neo-tree.events.queue").define_event(event_name, {
465465
setup = <function>,
466466
seed = <function>,
467467
teardown = <function>,
468-
debounce_frequency = <number>
468+
debounce_frequency = <number>,
469+
once = <boolean>,
470+
cancelled = <boolean>
469471
})
470472

471473
The setup function is run the first time the event is subscribed to. For an
472474
autocmd event, this would define the vim autocmd to connect it to fire_event().
473475

474-
The seed function is run at the begining of every event firing. The diagnostics
476+
The `seed` function is run at the begining of every event firing. The diagnostics
475477
event uses this to collect the diagnostic information and pass it to all
476478
subscribers.
477479

478-
The teardown function is used when the last subscriber unsubscribes, and cleans
480+
The `teardown` function is used when the last subscriber unsubscribes, and cleans
479481
up. This is like Dispose in other languages.
480482

481-
debounce_frequency is the minimum number of milliseconds between each invocation
483+
`debounce_frequenc`y is the minimum number of milliseconds between each invocation
482484
of the event. The first event is gauranteed to fire, as well as the last one, but
483485
in between events may be dropped if this is set to a number greater than zero.
484486

487+
`once` means to only fire this event handler once then mark it as `cancelled`.
488+
489+
`cancelled` means that this event handler will be skipped in all future event
490+
fires, and will be discarded on the next cleanup of the queue.
491+
485492

486493
================================================================================
487494
OTHER SOURCES ~

lua/neo-tree.lua

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ local events = require("neo-tree.events")
77
local log = require("neo-tree.log")
88
local popups = require("neo-tree.ui.popups")
99
local highlights = require("neo-tree.ui.highlights")
10+
local manager = require("neo-tree.sources.manager")
1011

1112
-- If you add a new source, you need to add it to the sources table.
1213
-- Each source should have a defaults module that contains the default values
@@ -19,7 +20,7 @@ local sources = {
1920

2021
local M = {}
2122

22-
-- Adding this as a shortcut because the module path is so long.
23+
-- TODO: DEPRECATED in 1.19, remove in 2.0
2324
M.fs = require("neo-tree.sources.filesystem")
2425

2526
local normalize_mappings = function(config)
@@ -68,80 +69,89 @@ local define_events = function()
6869
events_setup = true
6970
end
7071

71-
local src = function(source_name)
72-
if source_name == nil or source_name == "" then
72+
local check_source = function(source_name)
73+
if not utils.truthy(source_name) then
7374
source_name = M.config.default_source
7475
end
75-
local success, source = pcall(require, "neo-tree.sources." .. source_name)
76+
local success, result = pcall(require, "neo-tree.sources." .. source_name)
7677
if not success then
77-
error("Source " .. source_name .. " not found.")
78+
error("Source " .. source_name .. " could not be loaded: ", result)
7879
end
79-
source.name = source_name
80-
return source
80+
return source_name
8181
end
8282

8383
M.close_all_except = function(source_name)
84-
local source = src(source_name)
85-
local target_pos = utils.get_value(M, "config." .. source.name .. ".window.position", "left")
84+
source_name = check_source(source_name)
85+
local target_pos = utils.get_value(M, "config." .. source_name .. ".window.position", "left")
8686
for _, name in ipairs(sources) do
8787
if name ~= source_name then
8888
local pos = utils.get_value(M, "config." .. name .. ".window.position", "left")
8989
if pos == target_pos then
90-
M.close(name)
90+
manager.close(source_name)
9191
end
9292
end
9393
end
94-
M.close_all("float")
94+
renderer.close_all_floating_windows()
9595
end
9696

97-
M.close = function(source_name)
98-
return src(source_name).close()
99-
end
97+
M.close = manager.close
10098

10199
M.close_all = function(at_position)
102100
renderer.close_all_floating_windows()
103101
if type(at_position) == "string" and at_position > "" then
104102
for _, name in ipairs(sources) do
105103
local pos = utils.get_value(M, "config." .. name .. ".window.position", "left")
106104
if pos == at_position then
107-
M.close(name)
105+
manager.close(name)
108106
end
109107
end
110108
else
111109
for _, name in ipairs(sources) do
112-
M.close(name)
110+
manager.close(name)
113111
end
114112
end
115113
end
116114

117115
M.float = function(source_name, toggle_if_open)
118-
source_name = src(source_name).name
116+
source_name = check_source(source_name)
119117
if toggle_if_open then
120118
if renderer.close_floating_window(source_name) then
121119
-- It was open, and now it's not.
122120
return
123121
end
124122
end
125-
M.close_all("float")
126-
M.close(source_name) -- in case this source is open in a sidebar
127-
src(source_name).float()
123+
renderer.close_all_floating_windows()
124+
manager.close(source_name) -- in case this source is open in a sidebar
125+
manager.float(source_name)
128126
end
129127

128+
--TODO: Remove the close_others option in 2.0
130129
M.focus = function(source_name, close_others, toggle_if_open)
130+
source_name = check_source(source_name)
131131
if toggle_if_open then
132-
if M.close(source_name) then
132+
if manager.close(source_name) then
133133
-- It was open, and now it's not.
134134
return
135135
end
136136
end
137137
if close_others == nil then
138138
close_others = true
139139
end
140-
local source = src(source_name)
141140
if close_others then
142-
M.close_all_except(source.name)
141+
M.close_all_except(source_name)
142+
end
143+
manager.focus(source_name)
144+
end
145+
146+
M.reveal_current_file = function(source_name, toggle_if_open)
147+
source_name = check_source(source_name)
148+
if toggle_if_open then
149+
if manager.close(source_name) then
150+
-- It was open, and now it's not.
151+
return
152+
end
143153
end
144-
source.focus()
154+
manager.reveal_current_file(source_name)
145155
end
146156

147157
M.get_prior_window = function()
@@ -199,27 +209,25 @@ M.win_enter_event = function()
199209
end
200210
end
201211

212+
--TODO: Remove the do_not_focus and close_others options in 2.0
202213
M.show = function(source_name, do_not_focus, close_others, toggle_if_open)
214+
source_name = check_source(source_name)
203215
if toggle_if_open then
204-
if M.close(source_name) then
216+
if manager.close(source_name) then
205217
-- It was open, and now it's not.
206218
return
207219
end
208220
end
209221
if close_others == nil then
210222
close_others = true
211223
end
212-
local source = src(source_name)
213224
if close_others then
214-
M.close_all_except(source.name)
225+
M.close_all_except(source_name)
215226
end
216227
if do_not_focus then
217-
local current_win = vim.api.nvim_get_current_win()
218-
source.show(function()
219-
vim.api.nvim_set_current_win(current_win)
220-
end)
228+
manager.show(source_name)
221229
else
222-
source.show()
230+
manager.focus(source_name)
223231
end
224232
end
225233

@@ -308,7 +316,7 @@ M.setup = function(config)
308316

309317
-- setup the sources with the combined config
310318
for _, source_name in ipairs(sources) do
311-
src(source_name).setup(M.config[source_name], M.config)
319+
manager.setup(source_name, M.config[source_name], M.config)
312320
end
313321

314322
local event_handler = {

lua/neo-tree/sources/buffers/commands.lua

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,42 @@ local vim = vim
44
local cc = require("neo-tree.sources.common.commands")
55
local buffers = require("neo-tree.sources.buffers")
66
local utils = require("neo-tree.utils")
7+
local manager = require("neo-tree.sources.manager")
78

89
local M = {}
910

1011
M.add = function(state)
11-
cc.add(state, buffers.refresh)
12+
cc.add(state, M.refresh)
1213
end
1314

1415
M.buffer_delete = function(state)
1516
local node = state.tree:get_node()
1617
if node then
1718
vim.api.nvim_buf_delete(node.extra.bufnr, { force = false, unload = false })
18-
buffers.refresh()
19+
M.refresh()
1920
end
2021
end
2122
M.close_node = cc.close_node
2223

2324
---Marks node as copied, so that it can be pasted somewhere else.
2425
M.copy_to_clipboard = function(state)
25-
cc.copy_to_clipboard(state, buffers.redraw)
26+
cc.copy_to_clipboard(state, utils.wrap(manager.redraw, "buffers"))
2627
end
2728

2829
---Marks node as cut, so that it can be pasted (moved) somewhere else.
2930
M.cut_to_clipboard = function(state)
30-
cc.cut_to_clipboard(state, buffers.redraw)
31+
cc.cut_to_clipboard(state, utils.wrap(manager.redraw, "buffers"))
3132
end
3233

3334
M.show_debug_info = cc.show_debug_info
3435

3536
---Pastes all items from the clipboard to the current directory.
3637
M.paste_from_clipboard = function(state)
37-
cc.paste_from_clipboard(state, buffers.refresh)
38+
cc.paste_from_clipboard(state, M.refresh)
3839
end
3940

4041
M.delete = function(state)
41-
cc.delete(state, buffers.refresh)
42+
cc.delete(state, M.refresh)
4243
end
4344

4445
---Navigate up one level.
@@ -51,10 +52,10 @@ M.open = cc.open
5152
M.open_split = cc.open_split
5253
M.open_vsplit = cc.open_vsplit
5354

54-
M.refresh = buffers.refresh
55+
M.refresh = utils.wrap(manager.refresh, "buffers")
5556

5657
M.rename = function(state)
57-
cc.rename(state, buffers.refresh)
58+
cc.rename(state, M.refresh)
5859
end
5960

6061
M.set_root = function(state)

0 commit comments

Comments
 (0)