Skip to content

Commit

Permalink
feat: experimental option to watch directory for changes (#292)
Browse files Browse the repository at this point in the history
  • Loading branch information
stevearc committed Feb 20, 2024
1 parent e27cc4e commit bcfe7d1
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 2 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ require("oil").setup({
-- Constrain the cursor to the editable parts of the oil buffer
-- Set to `false` to disable, or "name" to keep it on the file names
constrain_cursor = "editable",
-- Set to true to watch the filesystem for changes and reload oil
experimental_watch_for_changes = false,
-- Keymaps in oil buffer. Can be any value that `vim.keymap.set` accepts OR a table of keymap
-- options with a `callback` (e.g. { callback = function() ... end, desc = "", mode = "n" })
-- Additionally, if it is a string that matches "actions.<name>",
Expand Down
2 changes: 2 additions & 0 deletions doc/oil.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ CONFIG *oil-confi
-- Constrain the cursor to the editable parts of the oil buffer
-- Set to `false` to disable, or "name" to keep it on the file names
constrain_cursor = "editable",
-- Set to true to watch the filesystem for changes and reload oil
experimental_watch_for_changes = false,
-- Keymaps in oil buffer. Can be any value that `vim.keymap.set` accepts OR a table of keymap
-- options with a `callback` (e.g. { callback = function() ... end, desc = "", mode = "n" })
-- Additionally, if it is a string that matches "actions.<name>",
Expand Down
2 changes: 2 additions & 0 deletions lua/oil/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ local default_config = {
-- Constrain the cursor to the editable parts of the oil buffer
-- Set to `false` to disable, or "name" to keep it on the file names
constrain_cursor = "editable",
-- Set to true to watch the filesystem for changes and reload oil
experimental_watch_for_changes = false,
-- Keymaps in oil buffer. Can be any value that `vim.keymap.set` accepts OR a table of keymap
-- options with a `callback` (e.g. { callback = function() ... end, desc = "", mode = "n" })
-- Additionally, if it is a string that matches "actions.<name>",
Expand Down
5 changes: 5 additions & 0 deletions lua/oil/mutator/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,11 @@ end

local mutation_in_progress = false

---@return boolean
M.is_mutating = function()
return mutation_in_progress
end

---@param confirm nil|boolean
---@param cb? fun(err: nil|string)
M.try_write_changes = function(confirm, cb)
Expand Down
76 changes: 74 additions & 2 deletions lua/oil/view.lua
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,11 @@ M.set_sort = function(new_sort)
end
end

---@class oil.ViewData
---@field fs_event? any uv_fs_event_t

-- List of bufnrs
---@type table<integer, oil.ViewData>
local session = {}

---@return integer[]
Expand Down Expand Up @@ -320,7 +324,7 @@ M.initialize = function(bufnr)
vim.bo[bufnr].syntax = "oil"
vim.bo[bufnr].filetype = "oil"
vim.b[bufnr].EditorConfig_disable = 1
session[bufnr] = true
session[bufnr] = {}
for k, v in pairs(config.buf_options) do
vim.api.nvim_buf_set_option(bufnr, k, v)
end
Expand Down Expand Up @@ -356,7 +360,11 @@ M.initialize = function(bufnr)
once = true,
buffer = bufnr,
callback = function()
local view_data = session[bufnr]
session[bufnr] = nil
if view_data and view_data.fs_event then
view_data.fs_event:stop()
end
end,
})
vim.api.nvim_create_autocmd("BufEnter", {
Expand Down Expand Up @@ -426,8 +434,38 @@ M.initialize = function(bufnr)
end,
})

-- Watch for TextChanged and update the trash original path extmarks
local adapter = util.get_adapter(bufnr)

-- Set up a watcher that will refresh the directory
if adapter and adapter.name == "files" and config.experimental_watch_for_changes then
local fs_event = assert(uv.new_fs_event())
local bufname = vim.api.nvim_buf_get_name(bufnr)
local _, dir = util.parse_url(bufname)
fs_event:start(
assert(dir),
{},
vim.schedule_wrap(function(err, filename, events)
local mutator = require("oil.mutator")
if err or vim.bo[bufnr].modified or vim.b[bufnr].oil_dirty or mutator.is_mutating() then
return
end

-- If the buffer is currently visible, rerender
for _, winid in ipairs(vim.api.nvim_list_wins()) do
if vim.api.nvim_win_is_valid(winid) and vim.api.nvim_win_get_buf(winid) == bufnr then
M.render_buffer_async(bufnr)
return
end
end

-- If it is not currently visible, mark it as dirty
vim.b[bufnr].oil_dirty = {}
end)
)
session[bufnr].fs_event = fs_event
end

-- Watch for TextChanged and update the trash original path extmarks
if adapter and adapter.name == "trash" then
local debounce_timer = assert(uv.new_timer())
local pending = false
Expand Down Expand Up @@ -680,6 +718,8 @@ local function get_used_columns()
return cols
end

local pending_renders = {}

---@param bufnr integer
---@param opts nil|table
--- refetch nil|boolean Defaults to true
Expand All @@ -691,14 +731,33 @@ M.render_buffer_async = function(bufnr, opts, callback)
if bufnr == 0 then
bufnr = vim.api.nvim_get_current_buf()
end

-- If we're already rendering, queue up another rerender after it's complete
if vim.b[bufnr].oil_rendering then
if not pending_renders[bufnr] then
pending_renders[bufnr] = { callback }
elseif callback then
table.insert(pending_renders[bufnr], callback)
end
return
end

local bufname = vim.api.nvim_buf_get_name(bufnr)
vim.b[bufnr].oil_rendering = true
local _, dir = util.parse_url(bufname)
-- Undo should not return to a blank buffer
-- Method taken from :h clear-undo
vim.bo[bufnr].undolevels = -1
local handle_error = vim.schedule_wrap(function(message)
vim.b[bufnr].oil_rendering = false
vim.bo[bufnr].undolevels = vim.api.nvim_get_option_value("undolevels", { scope = "global" })
util.render_text(bufnr, { "Error: " .. message })
if pending_renders[bufnr] then
for _, cb in ipairs(pending_renders) do
cb(message)
end
pending_renders[bufnr] = nil
end
if callback then
callback(message)
else
Expand All @@ -725,13 +784,26 @@ M.render_buffer_async = function(bufnr, opts, callback)
if not vim.api.nvim_buf_is_valid(bufnr) then
return
end
vim.b[bufnr].oil_rendering = false
loading.set_loading(bufnr, false)
render_buffer(bufnr, { jump = true })
vim.bo[bufnr].undolevels = vim.api.nvim_get_option_value("undolevels", { scope = "global" })
vim.bo[bufnr].modifiable = not buffers_locked and adapter.is_modifiable(bufnr)
if callback then
callback()
end

-- If there were any concurrent calls to render this buffer, process them now
if pending_renders[bufnr] then
local all_cbs = pending_renders[bufnr]
pending_renders[bufnr] = nil
local new_cb = function(...)
for _, cb in ipairs(all_cbs) do
cb(...)
end
end
M.render_buffer_async(bufnr, {}, new_cb)
end
end)
if not opts.refetch then
finish()
Expand Down

0 comments on commit bcfe7d1

Please sign in to comment.