Merge branch 'stevearc:master' into feat-setup-view-options-add-is_excluded
This commit is contained in:
commit
c1ed4fad14
15 changed files with 295 additions and 78 deletions
17
README.md
17
README.md
|
|
@ -132,6 +132,8 @@ require("oil").setup({
|
||||||
conceallevel = 3,
|
conceallevel = 3,
|
||||||
concealcursor = "n",
|
concealcursor = "n",
|
||||||
},
|
},
|
||||||
|
-- Oil will take over directory buffers (e.g. `vim .` or `:e src/`
|
||||||
|
default_file_explorer = true,
|
||||||
-- Restore window options to previous values when leaving an oil buffer
|
-- Restore window options to previous values when leaving an oil buffer
|
||||||
restore_win_options = true,
|
restore_win_options = true,
|
||||||
-- Skip the confirmation popup for simple operations
|
-- Skip the confirmation popup for simple operations
|
||||||
|
|
@ -234,6 +236,21 @@ Change the display columns for oil
|
||||||
| ----- | ------------------ | ---- |
|
| ----- | ------------------ | ---- |
|
||||||
| cols | `oil.ColumnSpec[]` | |
|
| cols | `oil.ColumnSpec[]` | |
|
||||||
|
|
||||||
|
### set_is_hidden_file(is_hidden_file)
|
||||||
|
|
||||||
|
`set_is_hidden_file(is_hidden_file)` \
|
||||||
|
Change how oil determines if the file is hidden
|
||||||
|
|
||||||
|
| Param | Type | Desc |
|
||||||
|
| -------------- | ----------------------------------------------------- | -------------------------------------------- |
|
||||||
|
| is_hidden_file | `fun(filename: string, bufnr: nil\|integer): boolean` | Return true if the file/dir should be hidden |
|
||||||
|
|
||||||
|
### toggle_hidden()
|
||||||
|
|
||||||
|
`toggle_hidden()` \
|
||||||
|
Toggle hidden files and directories
|
||||||
|
|
||||||
|
|
||||||
### get_current_dir()
|
### get_current_dir()
|
||||||
|
|
||||||
`get_current_dir(): nil|string` \
|
`get_current_dir(): nil|string` \
|
||||||
|
|
|
||||||
13
doc/oil.txt
13
doc/oil.txt
|
|
@ -37,6 +37,8 @@ OPTIONS *oil-option
|
||||||
conceallevel = 3,
|
conceallevel = 3,
|
||||||
concealcursor = "n",
|
concealcursor = "n",
|
||||||
},
|
},
|
||||||
|
-- Oil will take over directory buffers (e.g. `vim .` or `:e src/`
|
||||||
|
default_file_explorer = true,
|
||||||
-- Restore window options to previous values when leaving an oil buffer
|
-- Restore window options to previous values when leaving an oil buffer
|
||||||
restore_win_options = true,
|
restore_win_options = true,
|
||||||
-- Skip the confirmation popup for simple operations
|
-- Skip the confirmation popup for simple operations
|
||||||
|
|
@ -110,6 +112,17 @@ set_columns({cols}) *oil.set_column
|
||||||
Parameters:
|
Parameters:
|
||||||
{cols} `oil.ColumnSpec[]`
|
{cols} `oil.ColumnSpec[]`
|
||||||
|
|
||||||
|
set_is_hidden_file({is_hidden_file}) *oil.set_is_hidden_file*
|
||||||
|
Change how oil determines if the file is hidden
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
{is_hidden_file} `fun(filename: string, bufnr: nil|integer): boolean` Retu
|
||||||
|
rn true if the file/dir should be hidden
|
||||||
|
|
||||||
|
toggle_hidden() *oil.toggle_hidden*
|
||||||
|
Toggle hidden files and directories
|
||||||
|
|
||||||
|
|
||||||
get_current_dir(): nil|string *oil.get_current_dir*
|
get_current_dir(): nil|string *oil.get_current_dir*
|
||||||
Get the current directory
|
Get the current directory
|
||||||
|
|
||||||
|
|
|
||||||
2
doc/tags
2
doc/tags
|
|
@ -53,5 +53,7 @@ oil.open_float oil.txt /*oil.open_float*
|
||||||
oil.save oil.txt /*oil.save*
|
oil.save oil.txt /*oil.save*
|
||||||
oil.select oil.txt /*oil.select*
|
oil.select oil.txt /*oil.select*
|
||||||
oil.set_columns oil.txt /*oil.set_columns*
|
oil.set_columns oil.txt /*oil.set_columns*
|
||||||
|
oil.set_is_hidden_file oil.txt /*oil.set_is_hidden_file*
|
||||||
oil.setup oil.txt /*oil.setup*
|
oil.setup oil.txt /*oil.setup*
|
||||||
|
oil.toggle_hidden oil.txt /*oil.toggle_hidden*
|
||||||
oil.txt oil.txt /*oil.txt*
|
oil.txt oil.txt /*oil.txt*
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,17 @@ M.normalize_url = function(url, callback)
|
||||||
vim.loop.fs_stat(
|
vim.loop.fs_stat(
|
||||||
realpath,
|
realpath,
|
||||||
vim.schedule_wrap(function(stat_err, stat)
|
vim.schedule_wrap(function(stat_err, stat)
|
||||||
if not stat or stat.type == "directory" then
|
local is_directory
|
||||||
|
if stat then
|
||||||
|
is_directory = stat.type == "directory"
|
||||||
|
elseif vim.endswith(realpath, "/") then
|
||||||
|
is_directory = true
|
||||||
|
else
|
||||||
|
local filetype = vim.filetype.match({ filename = vim.fs.basename(realpath) })
|
||||||
|
is_directory = filetype == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_directory then
|
||||||
local norm_path = util.addslash(fs.os_to_posix_path(realpath))
|
local norm_path = util.addslash(fs.os_to_posix_path(realpath))
|
||||||
callback(scheme .. norm_path)
|
callback(scheme .. norm_path)
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,16 @@ local function get_column(adapter, defn)
|
||||||
return all_columns[name] or adapter.get_column(name)
|
return all_columns[name] or adapter.get_column(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param scheme string
|
---@param adapter_or_scheme string|oil.Adapter
|
||||||
---@return oil.ColumnSpec[]
|
---@return oil.ColumnSpec[]
|
||||||
M.get_supported_columns = function(scheme)
|
M.get_supported_columns = function(adapter_or_scheme)
|
||||||
|
local adapter
|
||||||
|
if type(adapter_or_scheme) == "string" then
|
||||||
|
adapter = config.get_adapter_by_scheme(adapter_or_scheme)
|
||||||
|
else
|
||||||
|
adapter = adapter_or_scheme
|
||||||
|
end
|
||||||
local ret = {}
|
local ret = {}
|
||||||
local adapter = config.get_adapter_by_scheme(scheme)
|
|
||||||
for _, def in ipairs(config.columns) do
|
for _, def in ipairs(config.columns) do
|
||||||
if get_column(adapter, def) then
|
if get_column(adapter, def) then
|
||||||
table.insert(ret, def)
|
table.insert(ret, def)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ local default_config = {
|
||||||
conceallevel = 3,
|
conceallevel = 3,
|
||||||
concealcursor = "n",
|
concealcursor = "n",
|
||||||
},
|
},
|
||||||
|
-- Oil will take over directory buffers (e.g. `vim .` or `:e src/`
|
||||||
|
default_file_explorer = true,
|
||||||
-- Restore window options to previous values when leaving an oil buffer
|
-- Restore window options to previous values when leaving an oil buffer
|
||||||
restore_win_options = true,
|
restore_win_options = true,
|
||||||
-- Skip the confirmation popup for simple operations
|
-- Skip the confirmation popup for simple operations
|
||||||
|
|
@ -142,9 +144,12 @@ M.get_trash_url = function()
|
||||||
return M.adapter_to_scheme.files .. fs.os_to_posix_path(M.trash)
|
return M.adapter_to_scheme.files .. fs.os_to_posix_path(M.trash)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param scheme string
|
---@param scheme nil|string
|
||||||
---@return nil|oil.Adapter
|
---@return nil|oil.Adapter
|
||||||
M.get_adapter_by_scheme = function(scheme)
|
M.get_adapter_by_scheme = function(scheme)
|
||||||
|
if not scheme then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
if not vim.endswith(scheme, "://") then
|
if not vim.endswith(scheme, "://") then
|
||||||
local pieces = vim.split(scheme, "://", { plain = true })
|
local pieces = vim.split(scheme, "://", { plain = true })
|
||||||
if #pieces <= 2 then
|
if #pieces <= 2 then
|
||||||
|
|
|
||||||
138
lua/oil/init.lua
138
lua/oil/init.lua
|
|
@ -5,7 +5,7 @@ local M = {}
|
||||||
---@class oil.Entry
|
---@class oil.Entry
|
||||||
---@field name string
|
---@field name string
|
||||||
---@field type oil.EntryType
|
---@field type oil.EntryType
|
||||||
---@field id nil|string Will be nil if it hasn't been persisted to disk yet
|
---@field id nil|integer Will be nil if it hasn't been persisted to disk yet
|
||||||
|
|
||||||
---@alias oil.EntryType "file"|"directory"|"socket"|"link"
|
---@alias oil.EntryType "file"|"directory"|"socket"|"link"
|
||||||
---@alias oil.TextChunk string|string[]
|
---@alias oil.TextChunk string|string[]
|
||||||
|
|
@ -29,15 +29,12 @@ local M = {}
|
||||||
---@return nil|oil.Entry
|
---@return nil|oil.Entry
|
||||||
M.get_entry_on_line = function(bufnr, lnum)
|
M.get_entry_on_line = function(bufnr, lnum)
|
||||||
local columns = require("oil.columns")
|
local columns = require("oil.columns")
|
||||||
local config = require("oil.config")
|
|
||||||
local parser = require("oil.mutator.parser")
|
local parser = require("oil.mutator.parser")
|
||||||
local util = require("oil.util")
|
local util = require("oil.util")
|
||||||
if vim.bo[bufnr].filetype ~= "oil" then
|
if vim.bo[bufnr].filetype ~= "oil" then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
local bufname = vim.api.nvim_buf_get_name(0)
|
local adapter = util.get_adapter(bufnr)
|
||||||
local scheme = util.parse_url(bufname)
|
|
||||||
local adapter = config.get_adapter_by_scheme(scheme)
|
|
||||||
if not adapter then
|
if not adapter then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
@ -46,15 +43,15 @@ M.get_entry_on_line = function(bufnr, lnum)
|
||||||
if not line then
|
if not line then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
local column_defs = columns.get_supported_columns(scheme)
|
local column_defs = columns.get_supported_columns(adapter)
|
||||||
local parsed_entry, entry = parser.parse_line(adapter, line, column_defs)
|
local result = parser.parse_line(adapter, line, column_defs)
|
||||||
if parsed_entry then
|
if result then
|
||||||
if entry then
|
if result.entry then
|
||||||
return util.export_entry(entry)
|
return util.export_entry(result.entry)
|
||||||
else
|
else
|
||||||
return {
|
return {
|
||||||
name = parsed_entry.name,
|
name = result.data.name,
|
||||||
type = parsed_entry._type,
|
type = result.data._type,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -138,6 +135,17 @@ M.set_columns = function(cols)
|
||||||
require("oil.view").set_columns(cols)
|
require("oil.view").set_columns(cols)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Change how oil determines if the file is hidden
|
||||||
|
---@param is_hidden_file fun(filename: string, bufnr: nil|integer): boolean Return true if the file/dir should be hidden
|
||||||
|
M.set_is_hidden_file = function(is_hidden_file)
|
||||||
|
require("oil.view").set_is_hidden_file(is_hidden_file)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Toggle hidden files and directories
|
||||||
|
M.toggle_hidden = function()
|
||||||
|
require("oil.view").toggle_hidden()
|
||||||
|
end
|
||||||
|
|
||||||
---Get the current directory
|
---Get the current directory
|
||||||
---@return nil|string
|
---@return nil|string
|
||||||
M.get_current_dir = function()
|
M.get_current_dir = function()
|
||||||
|
|
@ -298,13 +306,19 @@ M.close = function()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local ok, bufnr = pcall(vim.api.nvim_win_get_var, 0, "oil_original_buffer")
|
local ok, bufnr = pcall(vim.api.nvim_win_get_var, 0, "oil_original_buffer")
|
||||||
if ok then
|
if ok and vim.api.nvim_buf_is_valid(bufnr) then
|
||||||
if vim.api.nvim_buf_is_valid(bufnr) then
|
vim.api.nvim_win_set_buf(0, bufnr)
|
||||||
vim.api.nvim_win_set_buf(0, bufnr)
|
if vim.w.oil_original_view then
|
||||||
return
|
vim.fn.winrestview(vim.w.oil_original_view)
|
||||||
end
|
end
|
||||||
|
return
|
||||||
end
|
end
|
||||||
vim.api.nvim_buf_delete(0, { force = true })
|
|
||||||
|
-- Deleting the buffer closes all windows with that buffer open, so navigate to a different
|
||||||
|
-- buffer first
|
||||||
|
local oilbuf = vim.api.nvim_get_current_buf()
|
||||||
|
vim.cmd.bprev()
|
||||||
|
vim.api.nvim_buf_delete(oilbuf, { force = true })
|
||||||
end
|
end
|
||||||
|
|
||||||
---Select the entry under the cursor
|
---Select the entry under the cursor
|
||||||
|
|
@ -368,11 +382,10 @@ M.select = function(opts)
|
||||||
vim.notify("Cannot preview multiple entries", vim.log.levels.WARN)
|
vim.notify("Cannot preview multiple entries", vim.log.levels.WARN)
|
||||||
entries = { entries[1] }
|
entries = { entries[1] }
|
||||||
end
|
end
|
||||||
-- Close the preview window
|
-- Close the preview window if we're not previewing the selection
|
||||||
for _, winid in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
|
local preview_win = util.get_preview_win()
|
||||||
if vim.api.nvim_win_is_valid(winid) and vim.api.nvim_win_get_option(winid, "previewwindow") then
|
if not opts.preview and preview_win then
|
||||||
vim.api.nvim_win_close(winid, true)
|
vim.api.nvim_win_close(preview_win, true)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
local bufname = vim.api.nvim_buf_get_name(0)
|
local bufname = vim.api.nvim_buf_get_name(0)
|
||||||
local prev_win = vim.api.nvim_get_current_win()
|
local prev_win = vim.api.nvim_get_current_win()
|
||||||
|
|
@ -380,15 +393,15 @@ M.select = function(opts)
|
||||||
local scheme, dir = util.parse_url(bufname)
|
local scheme, dir = util.parse_url(bufname)
|
||||||
local child = dir .. entry.name
|
local child = dir .. entry.name
|
||||||
local url = scheme .. child
|
local url = scheme .. child
|
||||||
if
|
local is_directory = entry.type == "directory"
|
||||||
entry.type == "directory"
|
|
||||||
or (
|
or (
|
||||||
entry.type == "link"
|
entry.type == "link"
|
||||||
and entry.meta
|
and entry.meta
|
||||||
and entry.meta.link_stat
|
and entry.meta.link_stat
|
||||||
and entry.meta.link_stat.type == "directory"
|
and entry.meta.link_stat.type == "directory"
|
||||||
)
|
)
|
||||||
then
|
if is_directory then
|
||||||
|
url = url .. "/"
|
||||||
-- If this is a new directory BUT we think we already have an entry with this name, disallow
|
-- If this is a new directory BUT we think we already have an entry with this name, disallow
|
||||||
-- entry. This prevents the case of MOVE /foo -> /bar + CREATE /foo.
|
-- entry. This prevents the case of MOVE /foo -> /bar + CREATE /foo.
|
||||||
-- If you enter the new /foo, it will show the contents of the old /foo.
|
-- If you enter the new /foo, it will show the contents of the old /foo.
|
||||||
|
|
@ -401,27 +414,34 @@ M.select = function(opts)
|
||||||
vim.api.nvim_win_close(0, false)
|
vim.api.nvim_win_close(0, false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local mods = {
|
local mods = {
|
||||||
vertical = opts.vertical,
|
vertical = opts.vertical,
|
||||||
horizontal = opts.horizontal,
|
horizontal = opts.horizontal,
|
||||||
split = opts.split,
|
split = opts.split,
|
||||||
|
keepalt = true,
|
||||||
}
|
}
|
||||||
if vim.tbl_isempty(mods) then
|
if opts.preview and preview_win then
|
||||||
mods = nil
|
vim.api.nvim_set_current_win(preview_win)
|
||||||
end
|
vim.cmd.edit({ args = { url }, mods = mods })
|
||||||
local cmd
|
|
||||||
if opts.tab then
|
|
||||||
cmd = "tabedit"
|
|
||||||
elseif opts.split then
|
|
||||||
cmd = "split"
|
|
||||||
else
|
else
|
||||||
cmd = "edit"
|
if vim.tbl_isempty(mods) then
|
||||||
|
mods = nil
|
||||||
|
end
|
||||||
|
local cmd
|
||||||
|
if opts.tab then
|
||||||
|
cmd = "tabedit"
|
||||||
|
elseif opts.split then
|
||||||
|
cmd = "split"
|
||||||
|
else
|
||||||
|
cmd = "edit"
|
||||||
|
end
|
||||||
|
vim.cmd({
|
||||||
|
cmd = cmd,
|
||||||
|
args = { url },
|
||||||
|
mods = mods,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
vim.cmd({
|
|
||||||
cmd = cmd,
|
|
||||||
args = { url },
|
|
||||||
mods = mods,
|
|
||||||
})
|
|
||||||
if opts.preview then
|
if opts.preview then
|
||||||
vim.api.nvim_win_set_option(0, "previewwindow", true)
|
vim.api.nvim_win_set_option(0, "previewwindow", true)
|
||||||
vim.api.nvim_win_set_var(0, "oil_entry_id", entry.id)
|
vim.api.nvim_win_set_var(0, "oil_entry_id", entry.id)
|
||||||
|
|
@ -439,6 +459,9 @@ end
|
||||||
local function maybe_hijack_directory_buffer(bufnr)
|
local function maybe_hijack_directory_buffer(bufnr)
|
||||||
local config = require("oil.config")
|
local config = require("oil.config")
|
||||||
local util = require("oil.util")
|
local util = require("oil.util")
|
||||||
|
if not config.default_file_explorer then
|
||||||
|
return
|
||||||
|
end
|
||||||
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
||||||
if bufname == "" then
|
if bufname == "" then
|
||||||
return
|
return
|
||||||
|
|
@ -599,6 +622,17 @@ local function load_oil_buffer(bufnr)
|
||||||
-- have BufReadCmd called for it
|
-- have BufReadCmd called for it
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- If the renamed buffer doesn't have a scheme anymore, this is a normal file.
|
||||||
|
-- Finish setting it up as a normal buffer.
|
||||||
|
local new_scheme = util.parse_url(new_url)
|
||||||
|
if not new_scheme then
|
||||||
|
loading.set_loading(bufnr, false)
|
||||||
|
vim.cmd.doautocmd({ args = { "BufReadPre", new_url }, mods = { emsg_silent = true } })
|
||||||
|
vim.cmd.doautocmd({ args = { "BufReadPost", new_url }, mods = { emsg_silent = true } })
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
bufname = new_url
|
bufname = new_url
|
||||||
end
|
end
|
||||||
if vim.endswith(bufname, "/") then
|
if vim.endswith(bufname, "/") then
|
||||||
|
|
@ -687,19 +721,20 @@ M.setup = function(opts)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
vim.api.nvim_create_autocmd("BufWinLeave", {
|
vim.api.nvim_create_autocmd("BufLeave", {
|
||||||
desc = "Save alternate buffer for later",
|
desc = "Save alternate buffer for later",
|
||||||
group = aug,
|
group = aug,
|
||||||
pattern = "*",
|
pattern = "*",
|
||||||
callback = function()
|
callback = function()
|
||||||
local util = require("oil.util")
|
local util = require("oil.util")
|
||||||
if not util.is_oil_bufnr(0) then
|
if not util.is_oil_bufnr(0) then
|
||||||
vim.api.nvim_win_set_var(0, "oil_original_buffer", vim.api.nvim_get_current_buf())
|
vim.w.oil_original_buffer = vim.api.nvim_get_current_buf()
|
||||||
vim.api.nvim_win_set_var(0, "oil_original_alternate", vim.fn.bufnr("#"))
|
vim.w.oil_original_view = vim.fn.winsaveview()
|
||||||
|
vim.w.oil_original_alternate = vim.fn.bufnr("#")
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
vim.api.nvim_create_autocmd("BufWinEnter", {
|
vim.api.nvim_create_autocmd("BufEnter", {
|
||||||
desc = "Set/unset oil window options and restore alternate buffer",
|
desc = "Set/unset oil window options and restore alternate buffer",
|
||||||
group = aug,
|
group = aug,
|
||||||
pattern = "*",
|
pattern = "*",
|
||||||
|
|
@ -715,6 +750,10 @@ M.setup = function(opts)
|
||||||
if has_orig and vim.api.nvim_buf_is_valid(orig_buffer) then
|
if has_orig and vim.api.nvim_buf_is_valid(orig_buffer) then
|
||||||
vim.fn.setreg("#", orig_buffer)
|
vim.fn.setreg("#", orig_buffer)
|
||||||
end
|
end
|
||||||
|
if not vim.w.oil_did_enter then
|
||||||
|
require("oil.view").set_win_options()
|
||||||
|
vim.w.oil_did_enter = true
|
||||||
|
end
|
||||||
elseif vim.fn.isdirectory(bufname) == 0 then
|
elseif vim.fn.isdirectory(bufname) == 0 then
|
||||||
-- Only run this logic if we are *not* in an oil buffer (and it's not a directory, which
|
-- Only run this logic if we are *not* in an oil buffer (and it's not a directory, which
|
||||||
-- will be replaced by an oil:// url)
|
-- will be replaced by an oil:// url)
|
||||||
|
|
@ -783,16 +822,9 @@ M.setup = function(opts)
|
||||||
end
|
end
|
||||||
-- Then transfer over the relevant window vars
|
-- Then transfer over the relevant window vars
|
||||||
vim.w.oil_did_enter = true
|
vim.w.oil_did_enter = true
|
||||||
local has_orig, orig_buffer =
|
vim.w.oil_original_buffer = vim.w[parent_win].oil_original_buffer
|
||||||
pcall(vim.api.nvim_win_get_var, parent_win, "oil_original_buffer")
|
vim.w.oil_original_view = vim.w[parent_win].oil_original_view
|
||||||
if has_orig and vim.api.nvim_buf_is_valid(orig_buffer) then
|
vim.w.oil_original_alternate = vim.w[parent_win].oil_original_alternate
|
||||||
vim.w.oil_original_buffer = orig_buffer
|
|
||||||
end
|
|
||||||
local has_orig_alt, alt_buffer =
|
|
||||||
pcall(vim.api.nvim_win_get_var, parent_win, "oil_original_alternate")
|
|
||||||
if has_orig_alt and vim.api.nvim_buf_is_valid(alt_buffer) then
|
|
||||||
vim.w.oil_original_alternate = alt_buffer
|
|
||||||
end
|
|
||||||
for k in pairs(config.win_options) do
|
for k in pairs(config.win_options) do
|
||||||
local varname = "_oil_" .. k
|
local varname = "_oil_" .. k
|
||||||
local has_opt, opt = pcall(vim.api.nvim_win_get_var, parent_win, varname)
|
local has_opt, opt = pcall(vim.api.nvim_win_get_var, parent_win, varname)
|
||||||
|
|
|
||||||
|
|
@ -38,27 +38,40 @@ local function parsedir(name)
|
||||||
return name, isdir
|
return name, isdir
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@class oil.ParseResult
|
||||||
|
---@field data table Parsed entry data
|
||||||
|
---@field ranges table<string, integer[]> Locations of the various columns
|
||||||
|
---@field entry nil|oil.InternalEntry If the entry already exists
|
||||||
|
|
||||||
---Parse a single line in a buffer
|
---Parse a single line in a buffer
|
||||||
---@param adapter oil.Adapter
|
---@param adapter oil.Adapter
|
||||||
---@param line string
|
---@param line string
|
||||||
---@param column_defs oil.ColumnSpec[]
|
---@param column_defs oil.ColumnSpec[]
|
||||||
---@return nil|table Parsed entry data
|
---@return nil|oil.ParseResult
|
||||||
---@return nil|oil.InternalEntry If the entry already exists
|
---@return nil|string Error
|
||||||
---@return nil|string Error message
|
|
||||||
M.parse_line = function(adapter, line, column_defs)
|
M.parse_line = function(adapter, line, column_defs)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
|
local ranges = {}
|
||||||
|
local start = 1
|
||||||
local value, rem = line:match("^/(%d+) (.+)$")
|
local value, rem = line:match("^/(%d+) (.+)$")
|
||||||
if not value then
|
if not value then
|
||||||
return nil, nil, "Malformed ID at start of line"
|
return nil, "Malformed ID at start of line"
|
||||||
end
|
end
|
||||||
|
ranges.id = { start, value:len() + 1 }
|
||||||
|
start = ranges.id[2] + 1
|
||||||
ret.id = tonumber(value)
|
ret.id = tonumber(value)
|
||||||
for _, def in ipairs(column_defs) do
|
for _, def in ipairs(column_defs) do
|
||||||
local name = util.split_config(def)
|
local name = util.split_config(def)
|
||||||
|
local range = { start }
|
||||||
|
local start_len = string.len(rem)
|
||||||
value, rem = columns.parse_col(adapter, rem, def)
|
value, rem = columns.parse_col(adapter, rem, def)
|
||||||
if not value then
|
if not value or not rem then
|
||||||
return nil, nil, string.format("Parsing %s failed", name)
|
return nil, string.format("Parsing %s failed", name)
|
||||||
end
|
end
|
||||||
ret[name] = value
|
ret[name] = value
|
||||||
|
range[2] = range[1] + start_len - string.len(rem) - 1
|
||||||
|
ranges[name] = range
|
||||||
|
start = range[2] + 1
|
||||||
end
|
end
|
||||||
local name = rem
|
local name = rem
|
||||||
if name then
|
if name then
|
||||||
|
|
@ -70,8 +83,9 @@ M.parse_line = function(adapter, line, column_defs)
|
||||||
ret._type = isdir and "directory" or "file"
|
ret._type = isdir and "directory" or "file"
|
||||||
end
|
end
|
||||||
local entry = cache.get_entry_by_id(ret.id)
|
local entry = cache.get_entry_by_id(ret.id)
|
||||||
|
ranges.name = { start, start + string.len(rem) - 1 }
|
||||||
if not entry then
|
if not entry then
|
||||||
return ret
|
return { data = ret, ranges = ranges }
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Parse the symlink syntax
|
-- Parse the symlink syntax
|
||||||
|
|
@ -81,8 +95,9 @@ M.parse_line = function(adapter, line, column_defs)
|
||||||
local name_pieces = vim.split(ret.name, " -> ", { plain = true })
|
local name_pieces = vim.split(ret.name, " -> ", { plain = true })
|
||||||
if #name_pieces ~= 2 then
|
if #name_pieces ~= 2 then
|
||||||
ret.name = ""
|
ret.name = ""
|
||||||
return ret
|
return { data = ret, ranges = ranges }
|
||||||
end
|
end
|
||||||
|
ranges.name = { start, start + string.len(name_pieces[1]) - 1 }
|
||||||
ret.name = parsedir(vim.trim(name_pieces[1]))
|
ret.name = parsedir(vim.trim(name_pieces[1]))
|
||||||
ret.link_target = name_pieces[2]
|
ret.link_target = name_pieces[2]
|
||||||
ret._type = "link"
|
ret._type = "link"
|
||||||
|
|
@ -93,7 +108,7 @@ M.parse_line = function(adapter, line, column_defs)
|
||||||
ret._type = entry[FIELD.type]
|
ret._type = entry[FIELD.type]
|
||||||
end
|
end
|
||||||
|
|
||||||
return ret, entry
|
return { data = ret, entry = entry, ranges = ranges }
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param bufnr integer
|
---@param bufnr integer
|
||||||
|
|
@ -113,8 +128,8 @@ M.parse = function(bufnr)
|
||||||
return diffs, errors
|
return diffs, errors
|
||||||
end
|
end
|
||||||
local scheme, path = util.parse_url(bufname)
|
local scheme, path = util.parse_url(bufname)
|
||||||
local column_defs = columns.get_supported_columns(scheme)
|
|
||||||
local parent_url = scheme .. path
|
local parent_url = scheme .. path
|
||||||
|
local column_defs = columns.get_supported_columns(adapter)
|
||||||
local children = cache.list_url(parent_url)
|
local children = cache.list_url(parent_url)
|
||||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
|
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
|
||||||
local original_entries = {}
|
local original_entries = {}
|
||||||
|
|
@ -137,8 +152,8 @@ M.parse = function(bufnr)
|
||||||
end
|
end
|
||||||
for i, line in ipairs(lines) do
|
for i, line in ipairs(lines) do
|
||||||
if line:match("^/%d+") then
|
if line:match("^/%d+") then
|
||||||
local parsed_entry, entry, err = M.parse_line(adapter, line, column_defs)
|
local result, err = M.parse_line(adapter, line, column_defs)
|
||||||
if not parsed_entry then
|
if not result or err then
|
||||||
table.insert(errors, {
|
table.insert(errors, {
|
||||||
message = err,
|
message = err,
|
||||||
lnum = i - 1,
|
lnum = i - 1,
|
||||||
|
|
@ -146,6 +161,8 @@ M.parse = function(bufnr)
|
||||||
})
|
})
|
||||||
goto continue
|
goto continue
|
||||||
end
|
end
|
||||||
|
local parsed_entry = result.data
|
||||||
|
local entry = result.entry
|
||||||
if not parsed_entry.name or parsed_entry.name:match("/") or not entry then
|
if not parsed_entry.name or parsed_entry.name:match("/") or not entry then
|
||||||
local message
|
local message
|
||||||
if not parsed_entry.name then
|
if not parsed_entry.name then
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb)
|
||||||
height = height,
|
height = height,
|
||||||
row = math.floor((vim.o.lines - vim.o.cmdheight - height) / 2),
|
row = math.floor((vim.o.lines - vim.o.cmdheight - height) / 2),
|
||||||
col = math.floor((vim.o.columns - width) / 2),
|
col = math.floor((vim.o.columns - width) / 2),
|
||||||
|
zindex = 152, -- render on top of the floating window title
|
||||||
style = "minimal",
|
style = "minimal",
|
||||||
border = "rounded",
|
border = "rounded",
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -102,11 +102,16 @@ M.rename_buffer = function(src_bufnr, dest_buf_name)
|
||||||
-- rename logic. The only reason we can't use nvim_buf_set_name on files is because vim will
|
-- rename logic. The only reason we can't use nvim_buf_set_name on files is because vim will
|
||||||
-- think that the new buffer conflicts with the file next time it tries to save.
|
-- think that the new buffer conflicts with the file next time it tries to save.
|
||||||
if not vim.loop.fs_stat(dest_buf_name) then
|
if not vim.loop.fs_stat(dest_buf_name) then
|
||||||
|
local altbuf = vim.fn.bufnr("#")
|
||||||
-- This will fail if the dest buf name already exists
|
-- This will fail if the dest buf name already exists
|
||||||
local ok = pcall(vim.api.nvim_buf_set_name, src_bufnr, dest_buf_name)
|
local ok = pcall(vim.api.nvim_buf_set_name, src_bufnr, dest_buf_name)
|
||||||
if ok then
|
if ok then
|
||||||
-- Renaming the buffer creates a new buffer with the old name. Find it and delete it.
|
-- Renaming the buffer creates a new buffer with the old name. Find it and delete it.
|
||||||
vim.api.nvim_buf_delete(vim.fn.bufadd(bufname), {})
|
vim.api.nvim_buf_delete(vim.fn.bufadd(bufname), {})
|
||||||
|
if altbuf and vim.api.nvim_buf_is_valid(altbuf) then
|
||||||
|
vim.fn.setreg("#", altbuf)
|
||||||
|
end
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,7 @@ M.get_last_cursor = function(bufname)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function are_any_modified()
|
local function are_any_modified()
|
||||||
local view = require("oil.view")
|
local buffers = M.get_all_buffers()
|
||||||
local buffers = view.get_all_buffers()
|
|
||||||
for _, bufnr in ipairs(buffers) do
|
for _, bufnr in ipairs(buffers) do
|
||||||
if vim.bo[bufnr].modified then
|
if vim.bo[bufnr].modified then
|
||||||
return true
|
return true
|
||||||
|
|
@ -65,25 +64,34 @@ local function are_any_modified()
|
||||||
end
|
end
|
||||||
|
|
||||||
M.toggle_hidden = function()
|
M.toggle_hidden = function()
|
||||||
local view = require("oil.view")
|
|
||||||
local any_modified = are_any_modified()
|
local any_modified = are_any_modified()
|
||||||
if any_modified then
|
if any_modified then
|
||||||
vim.notify("Cannot toggle hidden files when you have unsaved changes", vim.log.levels.WARN)
|
vim.notify("Cannot toggle hidden files when you have unsaved changes", vim.log.levels.WARN)
|
||||||
else
|
else
|
||||||
config.view_options.show_hidden = not config.view_options.show_hidden
|
config.view_options.show_hidden = not config.view_options.show_hidden
|
||||||
view.rerender_all_oil_buffers({ refetch = false })
|
M.rerender_all_oil_buffers({ refetch = false })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param is_hidden_file fun(filename: string, bufnr: nil|integer): boolean
|
||||||
|
M.set_is_hidden_file = function(is_hidden_file)
|
||||||
|
local any_modified = are_any_modified()
|
||||||
|
if any_modified then
|
||||||
|
vim.notify("Cannot change is_hidden_file when you have unsaved changes", vim.log.levels.WARN)
|
||||||
|
else
|
||||||
|
config.view_options.is_hidden_file = is_hidden_file
|
||||||
|
M.rerender_all_oil_buffers({ refetch = false })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
M.set_columns = function(cols)
|
M.set_columns = function(cols)
|
||||||
local view = require("oil.view")
|
|
||||||
local any_modified = are_any_modified()
|
local any_modified = are_any_modified()
|
||||||
if any_modified then
|
if any_modified then
|
||||||
vim.notify("Cannot change columns when you have unsaved changes", vim.log.levels.WARN)
|
vim.notify("Cannot change columns when you have unsaved changes", vim.log.levels.WARN)
|
||||||
else
|
else
|
||||||
config.columns = cols
|
config.columns = cols
|
||||||
-- TODO only refetch if we don't have all the necessary data for the columns
|
-- TODO only refetch if we don't have all the necessary data for the columns
|
||||||
view.rerender_all_oil_buffers({ refetch = true })
|
M.rerender_all_oil_buffers({ refetch = true })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -245,11 +253,36 @@ M.initialize = function(bufnr)
|
||||||
group = "Oil",
|
group = "Oil",
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
callback = function()
|
callback = function()
|
||||||
|
local oil = require("oil")
|
||||||
|
local parser = require("oil.mutator.parser")
|
||||||
|
if vim.wo.previewwindow then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Force the cursor to be after the (concealed) ID at the beginning of the line
|
||||||
|
local adapter = util.get_adapter(bufnr)
|
||||||
|
if adapter then
|
||||||
|
local cur = vim.api.nvim_win_get_cursor(0)
|
||||||
|
local line = vim.api.nvim_buf_get_lines(bufnr, cur[1] - 1, cur[1], true)[1]
|
||||||
|
local column_defs = columns.get_supported_columns(adapter)
|
||||||
|
local result = parser.parse_line(adapter, line, column_defs)
|
||||||
|
if result and result.data then
|
||||||
|
local min_col = result.ranges.id[2] + 1
|
||||||
|
if cur[2] < min_col then
|
||||||
|
vim.api.nvim_win_set_cursor(0, { cur[1], min_col })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debounce and update the preview window
|
||||||
if timer then
|
if timer then
|
||||||
timer:again()
|
timer:again()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
timer = vim.loop.new_timer()
|
timer = vim.loop.new_timer()
|
||||||
|
if not timer then
|
||||||
|
return
|
||||||
|
end
|
||||||
timer:start(10, 100, function()
|
timer:start(10, 100, function()
|
||||||
timer:stop()
|
timer:stop()
|
||||||
timer:close()
|
timer:close()
|
||||||
|
|
@ -258,7 +291,6 @@ M.initialize = function(bufnr)
|
||||||
if vim.api.nvim_get_current_buf() ~= bufnr then
|
if vim.api.nvim_get_current_buf() ~= bufnr then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local oil = require("oil")
|
|
||||||
local entry = oil.get_cursor_entry()
|
local entry = oil.get_cursor_entry()
|
||||||
if entry then
|
if entry then
|
||||||
local winid = util.get_preview_win()
|
local winid = util.get_preview_win()
|
||||||
|
|
@ -398,7 +430,6 @@ local function render_buffer(bufnr, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
---@param adapter oil.Adapter
|
|
||||||
---@param entry oil.InternalEntry
|
---@param entry oil.InternalEntry
|
||||||
---@param column_defs table[]
|
---@param column_defs table[]
|
||||||
---@param col_width integer[]
|
---@param col_width integer[]
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,34 @@ a.describe("Alternate buffer", function()
|
||||||
assert.equals("bar", vim.fn.expand("#"))
|
assert.equals("bar", vim.fn.expand("#"))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
a.it("preserves alternate when traversing oil dirs", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd("BufReadPost")
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
vim.wait(1000, function()
|
||||||
|
return oil.get_cursor_entry()
|
||||||
|
end, 10)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 1, 1 })
|
||||||
|
oil.select()
|
||||||
|
test_util.wait_for_autocmd("BufReadPost")
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("preserves alternate when opening preview", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd("BufReadPost")
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
vim.wait(1000, function()
|
||||||
|
return oil.get_cursor_entry()
|
||||||
|
end, 10)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 1, 1 })
|
||||||
|
oil.select({ preview = true })
|
||||||
|
test_util.wait_for_autocmd("BufReadPost")
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
a.describe("floating window", function()
|
a.describe("floating window", function()
|
||||||
a.it("sets previous buffer as alternate", function()
|
a.it("sets previous buffer as alternate", function()
|
||||||
vim.cmd.edit({ args = { "foo" } })
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ require("plenary.async").tests.add_to_env()
|
||||||
local fs = require("oil.fs")
|
local fs = require("oil.fs")
|
||||||
local files = require("oil.adapters.files")
|
local files = require("oil.adapters.files")
|
||||||
local cache = require("oil.cache")
|
local cache = require("oil.cache")
|
||||||
|
local test_util = require("tests.test_util")
|
||||||
|
|
||||||
local function throwiferr(err, ...)
|
local function throwiferr(err, ...)
|
||||||
if err then
|
if err then
|
||||||
|
|
@ -308,4 +309,27 @@ a.describe("files adapter", function()
|
||||||
["a/"] = true,
|
["a/"] = true,
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
a.it("Editing a new oil://path/ creates an oil buffer", function()
|
||||||
|
local tmpdir_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "/"
|
||||||
|
vim.cmd.edit({ args = { tmpdir_url } })
|
||||||
|
test_util.wait_for_autocmd("BufReadPost")
|
||||||
|
local new_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "newdir"
|
||||||
|
vim.cmd.edit({ args = { new_url } })
|
||||||
|
test_util.wait_for_autocmd("BufReadPost")
|
||||||
|
assert.equals("oil", vim.bo.filetype)
|
||||||
|
-- The normalization will add a '/'
|
||||||
|
assert.equals(new_url .. "/", vim.api.nvim_buf_get_name(0))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("Editing a new oil://file.rb creates a normal buffer", function()
|
||||||
|
local tmpdir_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "/"
|
||||||
|
vim.cmd.edit({ args = { tmpdir_url } })
|
||||||
|
test_util.wait_for_autocmd("BufReadPost")
|
||||||
|
local new_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "file.rb"
|
||||||
|
vim.cmd.edit({ args = { new_url } })
|
||||||
|
test_util.wait_for_autocmd("BufReadPost")
|
||||||
|
assert.equals("ruby", vim.bo.filetype)
|
||||||
|
assert.equals(vim.fn.fnamemodify(tmpdir.path, ":p") .. "file.rb", vim.api.nvim_buf_get_name(0))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -47,4 +47,19 @@ a.describe("regression tests", function()
|
||||||
a.util.sleep(10)
|
a.util.sleep(10)
|
||||||
assert.equals(winid, vim.api.nvim_get_current_win())
|
assert.equals(winid, vim.api.nvim_get_current_win())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- https://github.com/stevearc/oil.nvim/issues/64
|
||||||
|
a.it("doesn't close splits on oil.close", function()
|
||||||
|
vim.cmd.edit({ args = { "README.md" } })
|
||||||
|
vim.cmd.vsplit()
|
||||||
|
local winid = vim.api.nvim_get_current_win()
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
oil.open()
|
||||||
|
a.util.sleep(10)
|
||||||
|
oil.close()
|
||||||
|
a.util.sleep(10)
|
||||||
|
assert.equals(2, #vim.api.nvim_tabpage_list_wins(0))
|
||||||
|
assert.equals(winid, vim.api.nvim_get_current_win())
|
||||||
|
assert.equals(bufnr, vim.api.nvim_get_current_buf())
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -57,4 +57,16 @@ a.describe("window options", function()
|
||||||
vim.cmd.edit({ args = { "README.md" } })
|
vim.cmd.edit({ args = { "README.md" } })
|
||||||
assert.equals("auto", vim.o.signcolumn)
|
assert.equals("auto", vim.o.signcolumn)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
a.it("Sets the window options when re-entering oil buffer", function()
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd("BufReadPost")
|
||||||
|
assert.truthy(vim.w.oil_did_enter)
|
||||||
|
vim.cmd.edit({ args = { "README.md" } })
|
||||||
|
assert.falsy(vim.w.oil_did_enter)
|
||||||
|
oil.open()
|
||||||
|
assert.truthy(vim.w.oil_did_enter)
|
||||||
|
vim.cmd.vsplit()
|
||||||
|
assert.truthy(vim.w.oil_did_enter)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue