Skip to content
Draft
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
142 changes: 115 additions & 27 deletions lua/broot/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ local M = {
---@field config_files string[]? Paths to configuration files to pass to broot with the `--conf` argument. Values are passed to `vim.fn.expand` before being stored, so you can use environment variables and `~` in them.
---@field default_directory string|(fun(): string?)|nil `broot.broot()` calls this to determine the directory to launch broot in if the user doesn't supply one explicitly. If this function returns nil, `vim.fn.getcwd()` is used instead.
---@field create_user_commands boolean? If true, define the `:Broot` command
---@field replace_netrw boolean? If true, replace the netrw plugin and `Explore` commands

---@param opts SetupOpts
function M.setup(opts)
Expand All @@ -36,6 +37,10 @@ function M.setup(opts)
require("broot.user_commands").create_user_commands()
end

if opts.replace_netrw then
require("broot.netrw").replace_netrw()
end

M.config = vim.tbl_extend("force", M.config, opts)
end

Expand Down Expand Up @@ -73,9 +78,26 @@ function M._window_size()
}
end

local function reset_cursor(window)
-- Moving the cursor back to {1, 0} puts the window where we expect.
vim.api.nvim_win_set_cursor(window, { 1, 0 })
end

local function on_resize(window)
if vim.api.nvim_win_get_config(window).relative ~= "" then
-- Resize floating windows to full screen.
local window_resize = M._window_size()
vim.api.nvim_win_set_width(window, window_resize.width)
vim.api.nvim_win_set_height(window, window_resize.height)
end
reset_cursor(window)
end

---@class BrootOpts
---@field extra_args string[]? A list of extra arguments to pass to broot. These are used in addition to, rather than instead of, the global `extra_args` set with `broot.setup()`.
---@field directory string? The directory to launch broot in. If not given, the `default_directory` function set in `broot.setup()` is used.
---@field buffer integer? The buffer to launch broot in. If not given, a new buffer is created.
---@field window integer? The window to launch broot in. If not given, a new floating window is opened.

---@param opts BrootOpts?
function M.broot(opts)
Expand All @@ -88,28 +110,43 @@ function M.broot(opts)
extra_args = vim.tbl_map(vim.fn.shellescape, opts.extra_args)
end

-- Create an unlisted `scratch-buffer`.
local buffer_id = vim.api.nvim_create_buf(false, true)
if buffer_id == 0 then
error "Failed to create buffer"
local buffer
if opts.buffer ~= nil then
---@type integer
buffer = opts.buffer
else
-- Create an unlisted `scratch-buffer`.
buffer = vim.api.nvim_create_buf(false, true)
if buffer == 0 then
error "Failed to create buffer"
end
end
-- Don't warn when exiting the Broot buffer.
vim.api.nvim_buf_set_option(buffer_id, "modified", false)
vim.api.nvim_buf_set_option(buffer, "modified", false)

local window_size = M._window_size()

local window_id = vim.api.nvim_open_win(buffer_id, true, {
relative = "editor",
row = 0,
col = 0,
width = window_size.width,
height = window_size.height,
style = "minimal",
})
if window_id == 0 then
error "Failed to open window"
local window
if opts.window ~= nil then
---@type window
window = opts.window
M._make_window_minimal(window)
vim.api.nvim_win_set_buf(window, buffer)
else
local window_size = M._window_size()
window = vim.api.nvim_open_win(buffer, true, {
relative = "editor",
row = 0,
col = 0,
width = window_size.width,
height = window_size.height,
style = "minimal",
})
if window == 0 then
error "Failed to open window"
end
end

vim.notify("Launching broot in buffer " .. buffer .. " in window " .. window, vim.log.levels.INFO)

local cmd_path = M._mktemp()
local out_path = M._mktemp()
local cmd = vim.fn.shellescape(M.config.broot_binary)
Expand All @@ -127,7 +164,7 @@ function M.broot(opts)

local cmd_opts = {
on_exit = function(_job_id, exit_code, _event_type)
M._on_broot_exit(exit_code, window_id, buffer_id, cmd_path, out_path)
M._on_broot_exit(exit_code, window, buffer, cmd_path, out_path)
end,
}

Expand All @@ -145,17 +182,31 @@ function M.broot(opts)
end
vim.cmd ":startinsert"

vim.api.nvim_create_augroup("Broot", {})
local autocmd_group = vim.api.nvim_create_augroup("Broot", { clear = false })
vim.api.nvim_create_autocmd("VimResized", {
buffer = buffer_id,
group = "Broot",
buffer = buffer,
group = autocmd_group,
nested = true,
callback = function()
on_resize(window)
end,
})
vim.api.nvim_create_autocmd("WinResized", {
buffer = buffer,
group = autocmd_group,
nested = true,
callback = function()
local window_resize = M._window_size()
vim.api.nvim_win_set_width(window_id, window_resize.width)
vim.api.nvim_win_set_height(window_id, window_resize.height)
-- Moving the cursor back to {1, 0} puts the window where we expect.
vim.api.nvim_win_set_cursor(window_id, { 1, 0 })
for _, resized in ipairs(vim.v.event.windows) do
on_resize(resized)
end
end,
})
vim.api.nvim_create_autocmd("WinLeave", {
buffer = buffer,
group = autocmd_group,
nested = true,
callback = function()
reset_cursor(window)
end,
})
end
Expand All @@ -169,8 +220,14 @@ function M._on_broot_exit(exit_code, window_id, buffer_id, cmd_path, out_path)
if exit_code ~= 0 then
error("Broot failed with exit code " .. exit_code)
end
vim.api.nvim_win_close(window_id, true)

vim.api.nvim_buf_delete(buffer_id, { force = true })

-- Only close the window if it's not the last one.
if vim.fn.winnr "$" > 1 then
vim.api.nvim_win_close(window_id, true)
end

M._read_outcmd_path(cmd_path)
M._read_stdout_path(out_path)
end
Expand Down Expand Up @@ -211,4 +268,35 @@ function M._read_stdout_path(out_path)
end
end

---Like the 'minimal' window style but for non-floating windows.
function M._make_window_minimal(window)
vim.api.nvim_win_set_option(window, "number", false)
vim.api.nvim_win_set_option(window, "relativenumber", false)
vim.api.nvim_win_set_option(window, "cursorline", false)
vim.api.nvim_win_set_option(window, "cursorcolumn", false)
vim.api.nvim_win_set_option(window, "foldcolumn", "auto")
vim.api.nvim_win_set_option(window, "spell", false)
vim.api.nvim_win_set_option(window, "list", false)
vim.api.nvim_win_set_option(window, "signcolumn", "auto")
vim.api.nvim_win_set_option(window, "colorcolumn", "")
vim.api.nvim_win_set_option(window, "statuscolumn", "")

local fillchars = vim.api.nvim_win_get_option(window, "fillchars")
if #fillchars == 0 then
fillchars = "eob: "
else
fillchars = fillchars .. ",eob: "
end
vim.api.nvim_win_set_option(window, "fillchars", fillchars)

local winhighlight = vim.api.nvim_win_get_option(window, "winhighlight")
if #winhighlight == 0 then
winhighlight = "EndOfBuffer:NONE"
else
winhighlight = winhighlight .. ",EndOfBuffer:NONE"
end

vim.api.nvim_win_set_option(window, "winhighlight", winhighlight)
end

return M
104 changes: 104 additions & 0 deletions lua/broot/netrw.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
local M = {}

function M.replace_netrw()
local group_id = vim.api.nvim_create_augroup("BrootNetrw", {})
vim.api.nvim_create_autocmd("VimEnter", {
group = group_id,
desc = "Clear the FileExplorer group",
callback = function(_event)
vim.api.nvim_clear_autocmds {
group = "FileExplorer",
}
vim.api.nvim_create_augroup("FileExplorer", {})
M._replace_netrw_commands()
end,
})

vim.api.nvim_create_autocmd("BufEnter", {
group = group_id,
desc = "Use Broot to browse directories",
callback = function(event)
if vim.fn.isdirectory(event.file) == 1 then
require("broot").broot {
buffer = event.buf,
window = vim.fn.win_getid(),
directory = event.file,
}
end
end,
})

M._replace_netrw_commands()
end

function M._replace_netrw_commands()
local current_file = require("broot.default_directory").current_file()

vim.api.nvim_create_user_command("Explore", function(opts)
require("broot").broot {
directory = opts.args or current_file(),
window = vim.fn.win_getid(),
}
end, {
nargs = "?",
desc = "Explore the given directory with broot",
})

vim.api.nvim_create_user_command("Hexplore", function(opts)
vim.cmd [[split]]
require("broot").broot {
directory = opts.args or current_file(),
window = vim.fn.win_getid(),
}
end, {
nargs = "?",
desc = "Explore the given directory with broot",
})

vim.api.nvim_create_user_command("Vexplore", function(opts)
vim.cmd [[vsplit]]
require("broot").broot {
directory = opts.args or current_file(),
window = vim.fn.win_getid(),
}
end, {
nargs = "?",
desc = "Explore the given directory with broot",
})

vim.api.nvim_create_user_command("Lexplore", function(opts)
vim.cmd [[leftabove vsplit]]
require("broot").broot {
directory = opts.args or current_file(),
window = vim.fn.win_getid(),
}
end, {
nargs = "?",
desc = "Explore the given directory with broot",
})

vim.api.nvim_create_user_command("Sexplore", function(opts)
vim.cmd [[leftabove split]]
require("broot").broot {
directory = opts.args or current_file(),
window = vim.fn.win_getid(),
}
end, {
nargs = "?",
desc = "Explore the given directory with broot",
})

vim.api.nvim_create_user_command("Texplore", function(opts)
vim.cmd [[tabnew]]
require("broot").broot {
directory = opts.args or current_file(),
window = vim.fn.win_getid(),
buffer = vim.fn.bufnr(),
}
end, {
nargs = "?",
desc = "Explore the given directory with broot",
})
end

return M
1 change: 1 addition & 0 deletions nix/luarc.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
text = builtins.toJSON {
"workspace.library" = [
"./lua"
"./scripts/lua"
"${neodev}/types/stable"
"\${3rd}/luv/library"
"\${3rd}/luassert/library"
Expand Down
51 changes: 51 additions & 0 deletions tests/screenshots/tests-test_netrw.lua---can_explore_side_by_side
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
--|---------|---------|---------|---------|---------|---------|---------|---------|
01|. │.
02|├──conf.toml │├──conf.toml
03|├──doggy │├──doggy
04|├──flavors │├──flavors
05|│ ├──goldie ││ ├──goldie
06|│ ├──samoyed ││ ├──samoyed
07|│ └──weenie ││ └──weenie
08|├──nvim.toml │├──nvim.toml
09|└──puppy │└──puppy
10| │
11| │
12| │
13| │
14| │
15| │
16| │
17| │
18| │
19| │
20| │
21| Hit enter to go up, ? for help │ Hit e…r to go up, ? for h…,…l…s to s…h
22| h:n gi:y │ h:n gi:y
23|3' 1'
24|-- TERMINAL --

--|---------|---------|---------|---------|---------|---------|---------|---------|
01|01111111111111111111111111111111111111112011111111111111111111111111111111111111
02|33344444444444444444444444444444444444442333444444444444444444444444444444444444
03|33344444444444444444444444444444444444442333444444444444444444444444444444444444
04|33355555554444444444444444444444444444442333555555544444444444444444444444444444
05|33333344444444444444444444444444444444442333333444444444444444444444444444444444
06|33333344444444444444444444444444444444442333333444444444444444444444444444444444
07|33333344444444444444444444444444444444442333333444444444444444444444444444444444
08|33344444444444444444444444444444444444442333444444444444444444444444444444444444
09|33344444444444444444444444444444444444442333444444444444444444444444444444444444
10|44444444444444444444444444444444444444442444444444444444444444444444444444444444
11|44444444444444444444444444444444444444442444444444444444444444444444444444444444
12|44444444444444444444444444444444444444442444444444444444444444444444444444444444
13|44444444444444444444444444444444444444442444444444444444444444444444444444444444
14|44444444444444444444444444444444444444442444444444444444444444444444444444444444
15|44444444444444444444444444444444444444442444444444444444444444444444444444444444
16|44444444444444444444444444444444444444442444444444444444444444444444444444444444
17|44444444444444444444444444444444444444442444444444444444444444444444444444444444
18|44444444444444444444444444444444444444442444444444444444444444444444444444444444
19|44444444444444444444444444444444444444442444444444444444444444444444444444444444
20|44444444444444444444444444444444444444442444444444444444444444444444444444444444
21|66666777776666666666676666666666666666662666667876666666666676666668686866666686
22|9:::::::::::::::::::::::::::;;;<;;;;;<;429::::::::::::::::::::::::::;;;<;;;;;<;:
23|=========================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
24|??????????????222222222222222222222222222222222222222222222222222222222222222222
21 changes: 21 additions & 0 deletions tests/test_netrw.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
local MiniTest = require "mini.test"
local expect = MiniTest.expect

local T, child = require("test").new_set {
module = "broot.netrw",
pre_case = [[
require("broot").setup {
replace_netrw = true,
}
]],
}

T["can_explore_side_by_side"] = function()
child.cmd [[Explore]]
vim.loop.sleep(250)
child.cmd [[Lexplore]]
vim.loop.sleep(250)
expect.reference_screenshot(child.get_screenshot())
end

return T