Skip to content

Commit b98904b

Browse files
committed
feat: add new "split" window.position, acts like netrw, closes #23
1 parent 901a3ec commit b98904b

File tree

3 files changed

+107
-58
lines changed

3 files changed

+107
-58
lines changed

lua/neo-tree.lua

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,19 @@ M.buffer_enter_event = function(args)
245245
end
246246

247247
-- For all others, make sure another buffer is not hijacking our window
248+
-- ..but not if the position is "split"
248249
local prior_buf = vim.fn.bufnr("#")
249250
if prior_buf < 1 then
250251
return
251252
end
252253
local prior_type = vim.api.nvim_buf_get_option(prior_buf, "filetype")
253254
if prior_type == "neo-tree" then
255+
local position = vim.api.nvim_buf_get_var(prior_buf, "neo_tree_position")
256+
if position == "split" then
257+
-- nothing to do here, files are supposed to open in same window
258+
return
259+
end
260+
254261
local current_tabnr = vim.api.nvim_get_current_tabpage()
255262
local neo_tree_tabnr = vim.api.nvim_buf_get_var(prior_buf, "neo_tree_tabnr")
256263
if neo_tree_tabnr ~= current_tabnr then
@@ -276,7 +283,7 @@ M.buffer_enter_event = function(args)
276283
pcall(vim.cmd, "bdelete " .. bufname)
277284
local fake_state = {
278285
window = {
279-
position = "left",
286+
position = position,
280287
},
281288
}
282289
utils.open_file(fake_state, bufname)

lua/neo-tree/ui/renderer.lua

Lines changed: 65 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ local utils = require("neo-tree.utils")
77
local highlights = require("neo-tree.ui.highlights")
88
local popups = require("neo-tree.ui.popups")
99
local events = require("neo-tree.events")
10+
local keymap = require("nui.utils.keymap")
11+
local autocmd = require("nui.utils.autocmd")
1012
local log = require("neo-tree.log")
1113

1214
local M = {}
@@ -357,6 +359,7 @@ local enable_auto_close_floats = function()
357359
end
358360

359361
create_window = function(state)
362+
local bufname = string.format("neo-tree %s [%s]", state.name, state.tabnr)
360363
local win_options = {
361364
size = utils.resolve_config_option(state, "window.width", "40"),
362365
position = utils.resolve_config_option(state, "window.position", "left"),
@@ -400,39 +403,61 @@ create_window = function(state)
400403
win:unmount()
401404
end)
402405
end, { once = true })
406+
state.winid = win.winid
407+
state.bufnr = win.bufnr
408+
vim.api.nvim_buf_set_name(state.bufnr, bufname)
403409

404410
-- why is this necessary?
405411
vim.api.nvim_set_current_win(win.winid)
406412
enable_auto_close_floats()
413+
elseif win_options.position == "split" then
414+
local winid = vim.api.nvim_get_current_win()
415+
local bufnr = vim.fn.bufnr(bufname)
416+
if bufnr < 1 then
417+
bufnr = vim.api.nvim_create_buf(false, false)
418+
vim.api.nvim_buf_set_name(bufnr, bufname)
419+
end
420+
state.winid = winid
421+
state.bufnr = bufnr
422+
vim.api.nvim_buf_set_option(bufnr, "buftype", "nofile")
423+
vim.api.nvim_buf_set_option(bufnr, "swapfile", false)
424+
vim.api.nvim_buf_set_option(bufnr, "filetype", "neo-tree")
425+
vim.api.nvim_buf_set_option(bufnr, "modifiable", false)
426+
vim.api.nvim_win_set_buf(winid, bufnr)
407427
else
408428
win = NuiSplit(win_options)
409429
win:mount()
430+
state.winid = win.winid
431+
state.bufnr = win.bufnr
432+
vim.api.nvim_buf_set_name(state.bufnr, bufname)
410433
end
411434

412-
state.winid = win.winid
413-
state.bufnr = win.bufnr
414-
415435
if type(state.bufnr) == "number" then
416-
local bufname = string.format("neo-tree %s [%s]", state.name, state.tabnr)
417-
vim.api.nvim_buf_set_name(state.bufnr, bufname)
418436
vim.api.nvim_buf_set_var(state.bufnr, "neo_tree_source", state.name)
419-
vim.api.nvim_buf_set_var(state.bufnr, "neo_tree_winid", state.winid)
420437
vim.api.nvim_buf_set_var(state.bufnr, "neo_tree_tabnr", state.tabnr)
438+
vim.api.nvim_buf_set_var(state.bufnr, "neo_tree_position", state.window.position)
439+
vim.api.nvim_buf_set_var(state.bufnr, "neo_tree_winid", state.winid)
421440
end
422441

423-
-- Used to track the position of the cursor within the tree as it gains and loses focus
424-
--
425-
-- Note `WinEnter` is often too early to restore the cursor position so we do not set
426-
-- that up here, and instead trigger those events manually after drawing the tree (not
427-
-- to mention that it would be too late to register `WinEnter` here for the first
428-
-- iteration of that event on the tree window)
429-
win:on({ "WinLeave" }, function()
430-
state.position.save()
431-
end)
442+
if win == nil then
443+
autocmd.buf.define(state.bufnr, "WinLeave", function()
444+
state.position.save()
445+
end)
446+
else
447+
-- Used to track the position of the cursor within the tree as it gains and loses focus
448+
--
449+
-- Note `WinEnter` is often too early to restore the cursor position so we do not set
450+
-- that up here, and instead trigger those events manually after drawing the tree (not
451+
-- to mention that it would be too late to register `WinEnter` here for the first
452+
-- iteration of that event on the tree window)
453+
win:on({ "WinLeave" }, function()
454+
state.position.save()
455+
end)
432456

433-
win:on({ "BufDelete" }, function()
434-
win:unmount()
435-
end, { once = true })
457+
win:on({ "BufDelete" }, function()
458+
win:unmount()
459+
end, { once = true })
460+
end
436461

437462
local skip_this_mapping = {
438463
["none"] = true,
@@ -452,7 +477,7 @@ create_window = function(state)
452477
func = state.commands[func]
453478
end
454479
if type(func) == "function" then
455-
win:map("n", cmd, function()
480+
keymap.set(state.bufnr, "n", cmd, function()
456481
func(state)
457482
end, map_options)
458483
else
@@ -483,14 +508,21 @@ end
483508
---@return boolean True if the window exists and is valid, false otherwise.
484509
M.window_exists = function(state)
485510
local window_exists
486-
if state.winid == nil then
511+
local winid = utils.get_value(state, "winid", 0, true)
512+
local bufnr = utils.get_value(state, "bufnr", 0, true)
513+
local position = utils.get_value(state, "window.position", "left", true)
514+
515+
if winid == 0 then
487516
window_exists = false
517+
elseif position == "split" then
518+
window_exists = vim.api.nvim_win_is_valid(winid)
519+
and vim.api.nvim_buf_is_valid(bufnr)
520+
and vim.api.nvim_win_get_buf(winid) == bufnr
488521
else
489-
local isvalid = M.is_window_valid(state.winid)
490-
window_exists = isvalid and (vim.api.nvim_win_get_number(state.winid) > 0)
522+
local isvalid = M.is_window_valid(winid)
523+
window_exists = isvalid and (vim.api.nvim_win_get_number(winid) > 0)
491524
if not window_exists then
492525
state.winid = nil
493-
local bufnr = utils.get_value(state, "bufnr", 0, true)
494526
if bufnr > 0 and vim.api.nvim_buf_is_valid(bufnr) then
495527
state.bufnr = nil
496528
local success, err = pcall(vim.api.nvim_buf_delete, bufnr, { force = true })
@@ -525,13 +557,17 @@ draw = function(nodes, state, parent_id)
525557
end
526558

527559
-- Create the tree if it doesn't exist.
528-
if not M.window_exists(state) then
560+
if not parent_id and not M.window_exists(state) then
529561
create_window(state)
530562
create_tree(state)
531563
end
532564

533565
-- draw the given nodes
534-
state.tree:set_nodes(nodes, parent_id)
566+
local success, msg = pcall(state.tree.set_nodes, state.tree, nodes, parent_id)
567+
if not success then
568+
log.error("Error setting nodes: ", msg)
569+
log.error(vim.inspect(state.tree:get_nodes()))
570+
end
535571
if parent_id ~= nil then
536572
-- this is a dynamic fetch of children that were not previously loaded
537573
local node = state.tree:get_node(parent_id)
@@ -558,8 +594,10 @@ M.show_nodes = function(sourceItems, state, parentId)
558594
events.fire_event(events.BEFORE_RENDER, state)
559595
local level = 0
560596
if parentId ~= nil then
561-
local parent = state.tree:get_node(parentId)
562-
level = parent:get_depth()
597+
local success, parent = pcall(state.tree.get_node, state.tree, parentId)
598+
if success then
599+
level = parent:get_depth()
600+
end
563601
end
564602
local nodes = create_nodes(sourceItems, state, level)
565603
draw(nodes, state, parentId)

lua/neo-tree/utils.lua

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -215,40 +215,44 @@ M.open_file = function(state, path, open_cmd)
215215
events.fire_event(events.FILE_OPENED, path)
216216
return
217217
end
218-
-- use last window if possible
219-
local suitable_window_found = false
220-
local nt = require("neo-tree")
221-
if nt.config.open_files_in_last_window then
222-
local prior_window = nt.get_prior_window()
223-
if prior_window > 0 then
224-
local success = pcall(vim.api.nvim_set_current_win, prior_window)
225-
if success then
226-
suitable_window_found = true
218+
if state.window.position == "split" then
219+
vim.cmd(open_cmd .. " " .. path)
220+
else
221+
-- use last window if possible
222+
local suitable_window_found = false
223+
local nt = require("neo-tree")
224+
if nt.config.open_files_in_last_window then
225+
local prior_window = nt.get_prior_window()
226+
if prior_window > 0 then
227+
local success = pcall(vim.api.nvim_set_current_win, prior_window)
228+
if success then
229+
suitable_window_found = true
230+
end
227231
end
228232
end
229-
end
230-
-- find a suitable window to open the file in
231-
if not suitable_window_found then
232-
if state.window.position == "right" then
233-
vim.cmd("wincmd t")
234-
else
233+
-- find a suitable window to open the file in
234+
if not suitable_window_found then
235+
if state.window.position == "right" then
236+
vim.cmd("wincmd t")
237+
else
238+
vim.cmd("wincmd w")
239+
end
240+
end
241+
local attempts = 0
242+
while attempts < 4 and vim.bo.filetype == "neo-tree" do
243+
attempts = attempts + 1
235244
vim.cmd("wincmd w")
236245
end
237-
end
238-
local attempts = 0
239-
while attempts < 4 and vim.bo.filetype == "neo-tree" do
240-
attempts = attempts + 1
241-
vim.cmd("wincmd w")
242-
end
243-
-- TODO: make this configurable, see issue #43
244-
if vim.bo.filetype == "neo-tree" then
245-
-- Neo-tree must be the only window, restore it's status as a sidebar
246-
local winid = vim.api.nvim_get_current_win()
247-
local width = M.get_value(state, "window.width", 40)
248-
vim.cmd("vsplit " .. path)
249-
vim.api.nvim_win_set_width(winid, width)
250-
else
251-
vim.cmd(open_cmd .. " " .. path)
246+
-- TODO: make this configurable, see issue #43
247+
if vim.bo.filetype == "neo-tree" then
248+
-- Neo-tree must be the only window, restore it's status as a sidebar
249+
local winid = vim.api.nvim_get_current_win()
250+
local width = M.get_value(state, "window.width", 40)
251+
vim.cmd("vsplit " .. path)
252+
vim.api.nvim_win_set_width(winid, width)
253+
else
254+
vim.cmd(open_cmd .. " " .. path)
255+
end
252256
end
253257
events.fire_event(events.FILE_OPENED, path)
254258
end

0 commit comments

Comments
 (0)