build: replace luacheck with selene, add nix devshell and pre-commit (#20)

* build: replace luacheck with selene

Problem: luacheck is unmaintained (last release 2018) and required
suppressing four warning classes to avoid false positives. It also
lacks first-class vim/neovim awareness.

Solution: switch to selene with std='vim' for vim-aware linting.
Replace the luacheck CI job with selene, update the Makefile lint
target, and delete .luacheckrc.

* build: add nix devshell and pre-commit hooks

Problem: oil.nvim had no reproducible dev environment. The .envrc
set up a Python venv for the now-removed docgen pipeline, and there
were no pre-commit hooks for local formatting checks.

Solution: add flake.nix with stylua, selene, and prettier in the
devshell. Replace the stale Python .envrc with 'use flake'. Add
.pre-commit-config.yaml with stylua and prettier hooks matching
other plugins in the repo collection.

* fix: format with stylua

* build(selene): configure lints and add inline suppressions

Problem: selene fails on 5 errors and 3 warnings from upstream code
patterns that are intentional (mixed tables in config API, unused
callback parameters, identical if branches for readability).

Solution: globally allow mixed_table and unused_variable (high volume,
inherent to the codebase design). Add inline selene:allow directives
for the 8 remaining issues: if_same_then_else (4), mismatched_arg_count
(1), empty_if (2), global_usage (1). Remove .envrc from tracking.

* build: switch typecheck action to mrcjkb/lua-typecheck-action

Problem: oil.nvim used stevearc/nvim-typecheck-action, which required
cloning the action repo locally for the Makefile lint target. All
other plugins in the collection use mrcjkb/lua-typecheck-action.

Solution: swap to mrcjkb/lua-typecheck-action@v0 for consistency.
Remove the nvim-typecheck-action git clone from the Makefile and
.gitignore. Drop LuaLS from the local lint target since it requires
a full language server install — CI handles it.
This commit is contained in:
Barrett Ruth 2026-02-21 23:52:27 -05:00 committed by GitHub
parent df53b172a9
commit 86f553cd0a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
72 changed files with 2762 additions and 2649 deletions

View file

@ -1,18 +1,18 @@
local oil = require("oil")
local util = require("oil.util")
local oil = require('oil')
local util = require('oil.util')
local M = {}
M.show_help = {
callback = function()
local config = require("oil.config")
require("oil.keymap_util").show_help(config.keymaps)
local config = require('oil.config')
require('oil.keymap_util').show_help(config.keymaps)
end,
desc = "Show default keymaps",
desc = 'Show default keymaps',
}
M.select = {
desc = "Open the entry under the cursor",
desc = 'Open the entry under the cursor',
callback = function(opts)
opts = opts or {}
local callback = opts.callback
@ -21,30 +21,30 @@ M.select = {
end,
parameters = {
vertical = {
type = "boolean",
desc = "Open the buffer in a vertical split",
type = 'boolean',
desc = 'Open the buffer in a vertical split',
},
horizontal = {
type = "boolean",
desc = "Open the buffer in a horizontal split",
type = 'boolean',
desc = 'Open the buffer in a horizontal split',
},
split = {
type = '"aboveleft"|"belowright"|"topleft"|"botright"',
desc = "Split modifier",
desc = 'Split modifier',
},
tab = {
type = "boolean",
desc = "Open the buffer in a new tab",
type = 'boolean',
desc = 'Open the buffer in a new tab',
},
close = {
type = "boolean",
desc = "Close the original oil buffer once selection is made",
type = 'boolean',
desc = 'Close the original oil buffer once selection is made',
},
},
}
M.select_vsplit = {
desc = "Open the entry under the cursor in a vertical split",
desc = 'Open the entry under the cursor in a vertical split',
deprecated = true,
callback = function()
oil.select({ vertical = true })
@ -52,7 +52,7 @@ M.select_vsplit = {
}
M.select_split = {
desc = "Open the entry under the cursor in a horizontal split",
desc = 'Open the entry under the cursor in a horizontal split',
deprecated = true,
callback = function()
oil.select({ horizontal = true })
@ -60,7 +60,7 @@ M.select_split = {
}
M.select_tab = {
desc = "Open the entry under the cursor in a new tab",
desc = 'Open the entry under the cursor in a new tab',
deprecated = true,
callback = function()
oil.select({ tab = true })
@ -68,25 +68,25 @@ M.select_tab = {
}
M.preview = {
desc = "Open the entry under the cursor in a preview window, or close the preview window if already open",
desc = 'Open the entry under the cursor in a preview window, or close the preview window if already open',
parameters = {
vertical = {
type = "boolean",
desc = "Open the buffer in a vertical split",
type = 'boolean',
desc = 'Open the buffer in a vertical split',
},
horizontal = {
type = "boolean",
desc = "Open the buffer in a horizontal split",
type = 'boolean',
desc = 'Open the buffer in a horizontal split',
},
split = {
type = '"aboveleft"|"belowright"|"topleft"|"botright"',
desc = "Split modifier",
desc = 'Split modifier',
},
},
callback = function(opts)
local entry = oil.get_cursor_entry()
if not entry then
vim.notify("Could not find entry under cursor", vim.log.levels.ERROR)
vim.notify('Could not find entry under cursor', vim.log.levels.ERROR)
return
end
local winid = util.get_preview_win()
@ -95,7 +95,7 @@ M.preview = {
if entry.id == cur_id then
vim.api.nvim_win_close(winid, true)
if util.is_floating_win() then
local layout = require("oil.layout")
local layout = require('oil.layout')
local win_opts = layout.get_fullscreen_win_opts()
vim.api.nvim_win_set_config(0, win_opts)
end
@ -107,13 +107,13 @@ M.preview = {
}
M.preview_scroll_down = {
desc = "Scroll down in the preview window",
desc = 'Scroll down in the preview window',
callback = function()
local winid = util.get_preview_win()
if winid then
vim.api.nvim_win_call(winid, function()
vim.cmd.normal({
args = { vim.api.nvim_replace_termcodes("<C-d>", true, true, true) },
args = { vim.api.nvim_replace_termcodes('<C-d>', true, true, true) },
bang = true,
})
end)
@ -122,13 +122,13 @@ M.preview_scroll_down = {
}
M.preview_scroll_up = {
desc = "Scroll up in the preview window",
desc = 'Scroll up in the preview window',
callback = function()
local winid = util.get_preview_win()
if winid then
vim.api.nvim_win_call(winid, function()
vim.cmd.normal({
args = { vim.api.nvim_replace_termcodes("<C-u>", true, true, true) },
args = { vim.api.nvim_replace_termcodes('<C-u>', true, true, true) },
bang = true,
})
end)
@ -137,50 +137,50 @@ M.preview_scroll_up = {
}
M.preview_scroll_left = {
desc = "Scroll left in the preview window",
desc = 'Scroll left in the preview window',
callback = function()
local winid = util.get_preview_win()
if winid then
vim.api.nvim_win_call(winid, function()
vim.cmd.normal({ "zH", bang = true })
vim.cmd.normal({ 'zH', bang = true })
end)
end
end,
}
M.preview_scroll_right = {
desc = "Scroll right in the preview window",
desc = 'Scroll right in the preview window',
callback = function()
local winid = util.get_preview_win()
if winid then
vim.api.nvim_win_call(winid, function()
vim.cmd.normal({ "zL", bang = true })
vim.cmd.normal({ 'zL', bang = true })
end)
end
end,
}
M.parent = {
desc = "Navigate to the parent path",
desc = 'Navigate to the parent path',
callback = oil.open,
}
M.close = {
desc = "Close oil and restore original buffer",
desc = 'Close oil and restore original buffer',
callback = function(opts)
opts = opts or {}
oil.close(opts)
end,
parameters = {
exit_if_last_buf = {
type = "boolean",
desc = "Exit vim if oil is closed as the last buffer",
type = 'boolean',
desc = 'Exit vim if oil is closed as the last buffer',
},
},
}
M.close_float = {
desc = "Close oil if the window is floating, otherwise do nothing",
desc = 'Close oil if the window is floating, otherwise do nothing',
callback = function(opts)
if vim.w.is_oil_win then
opts = opts or {}
@ -189,8 +189,8 @@ M.close_float = {
end,
parameters = {
exit_if_last_buf = {
type = "boolean",
desc = "Exit vim if oil is closed as the last buffer",
type = 'boolean',
desc = 'Exit vim if oil is closed as the last buffer',
},
},
}
@ -202,42 +202,42 @@ local function cd(cmd, silent)
if dir then
vim.cmd({ cmd = cmd, args = { dir } })
if not silent then
vim.notify(string.format("CWD: %s", dir), vim.log.levels.INFO)
vim.notify(string.format('CWD: %s', dir), vim.log.levels.INFO)
end
else
vim.notify("Cannot :cd; not in a directory", vim.log.levels.WARN)
vim.notify('Cannot :cd; not in a directory', vim.log.levels.WARN)
end
end
M.cd = {
desc = ":cd to the current oil directory",
desc = ':cd to the current oil directory',
callback = function(opts)
opts = opts or {}
local cmd = "cd"
if opts.scope == "tab" then
cmd = "tcd"
elseif opts.scope == "win" then
cmd = "lcd"
local cmd = 'cd'
if opts.scope == 'tab' then
cmd = 'tcd'
elseif opts.scope == 'win' then
cmd = 'lcd'
end
cd(cmd, opts.silent)
end,
parameters = {
scope = {
type = 'nil|"tab"|"win"',
desc = "Scope of the directory change (e.g. use |:tcd| or |:lcd|)",
desc = 'Scope of the directory change (e.g. use |:tcd| or |:lcd|)',
},
silent = {
type = "boolean",
desc = "Do not show a message when changing directories",
type = 'boolean',
desc = 'Do not show a message when changing directories',
},
},
}
M.tcd = {
desc = ":tcd to the current oil directory",
desc = ':tcd to the current oil directory',
deprecated = true,
callback = function()
cd("tcd")
cd('tcd')
end,
}
@ -249,46 +249,46 @@ M.open_cwd = {
}
M.toggle_hidden = {
desc = "Toggle hidden files and directories",
desc = 'Toggle hidden files and directories',
callback = function()
require("oil.view").toggle_hidden()
require('oil.view').toggle_hidden()
end,
}
M.open_terminal = {
desc = "Open a terminal in the current directory",
desc = 'Open a terminal in the current directory',
callback = function()
local config = require("oil.config")
local config = require('oil.config')
local bufname = vim.api.nvim_buf_get_name(0)
local adapter = config.get_adapter_by_scheme(bufname)
if not adapter then
return
end
if adapter.name == "files" then
if adapter.name == 'files' then
local dir = oil.get_current_dir()
assert(dir, "Oil buffer with files adapter must have current directory")
assert(dir, 'Oil buffer with files adapter must have current directory')
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_current_buf(bufnr)
if vim.fn.has("nvim-0.11") == 1 then
if vim.fn.has('nvim-0.11') == 1 then
vim.fn.jobstart(vim.o.shell, { cwd = dir, term = true })
else
---@diagnostic disable-next-line: deprecated
vim.fn.termopen(vim.o.shell, { cwd = dir })
end
elseif adapter.name == "ssh" then
elseif adapter.name == 'ssh' then
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_current_buf(bufnr)
local url = require("oil.adapters.ssh").parse_url(bufname)
local cmd = require("oil.adapters.ssh.connection").create_ssh_command(url)
local url = require('oil.adapters.ssh').parse_url(bufname)
local cmd = require('oil.adapters.ssh.connection').create_ssh_command(url)
local term_id
if vim.fn.has("nvim-0.11") == 1 then
if vim.fn.has('nvim-0.11') == 1 then
term_id = vim.fn.jobstart(cmd, { term = true })
else
---@diagnostic disable-next-line: deprecated
term_id = vim.fn.termopen(cmd)
end
if term_id then
vim.api.nvim_chan_send(term_id, string.format("cd %s\n", url.path))
vim.api.nvim_chan_send(term_id, string.format('cd %s\n', url.path))
end
else
vim.notify(
@ -304,25 +304,25 @@ M.open_terminal = {
---@return nil|string[] cmd
---@return nil|string error
local function get_open_cmd(path)
if vim.fn.has("mac") == 1 then
return { "open", path }
elseif vim.fn.has("win32") == 1 then
if vim.fn.executable("rundll32") == 1 then
return { "rundll32", "url.dll,FileProtocolHandler", path }
if vim.fn.has('mac') == 1 then
return { 'open', path }
elseif vim.fn.has('win32') == 1 then
if vim.fn.executable('rundll32') == 1 then
return { 'rundll32', 'url.dll,FileProtocolHandler', path }
else
return nil, "rundll32 not found"
return nil, 'rundll32 not found'
end
elseif vim.fn.executable("explorer.exe") == 1 then
return { "explorer.exe", path }
elseif vim.fn.executable("xdg-open") == 1 then
return { "xdg-open", path }
elseif vim.fn.executable('explorer.exe') == 1 then
return { 'explorer.exe', path }
elseif vim.fn.executable('xdg-open') == 1 then
return { 'xdg-open', path }
else
return nil, "no handler found"
return nil, 'no handler found'
end
end
M.open_external = {
desc = "Open the entry under the cursor in an external program",
desc = 'Open the entry under the cursor in an external program',
callback = function()
local entry = oil.get_cursor_entry()
local dir = oil.get_current_dir()
@ -338,20 +338,20 @@ M.open_external = {
local cmd, err = get_open_cmd(path)
if not cmd then
vim.notify(string.format("Could not open %s: %s", path, err), vim.log.levels.ERROR)
vim.notify(string.format('Could not open %s: %s', path, err), vim.log.levels.ERROR)
return
end
local jid = vim.fn.jobstart(cmd, { detach = true })
assert(jid > 0, "Failed to start job")
assert(jid > 0, 'Failed to start job')
end,
}
M.refresh = {
desc = "Refresh current directory list",
desc = 'Refresh current directory list',
callback = function(opts)
opts = opts or {}
if vim.bo.modified and not opts.force then
local ok, choice = pcall(vim.fn.confirm, "Discard changes?", "No\nYes")
local ok, choice = pcall(vim.fn.confirm, 'Discard changes?', 'No\nYes')
if not ok or choice ~= 2 then
return
end
@ -363,26 +363,26 @@ M.refresh = {
end,
parameters = {
force = {
desc = "When true, do not prompt user if they will be discarding changes",
type = "boolean",
desc = 'When true, do not prompt user if they will be discarding changes',
type = 'boolean',
},
},
}
local function open_cmdline_with_path(path)
local escaped =
vim.api.nvim_replace_termcodes(": " .. vim.fn.fnameescape(path) .. "<Home>", true, false, true)
vim.api.nvim_feedkeys(escaped, "n", false)
vim.api.nvim_replace_termcodes(': ' .. vim.fn.fnameescape(path) .. '<Home>', true, false, true)
vim.api.nvim_feedkeys(escaped, 'n', false)
end
M.open_cmdline = {
desc = "Open vim cmdline with current entry as an argument",
desc = 'Open vim cmdline with current entry as an argument',
callback = function(opts)
opts = vim.tbl_deep_extend("keep", opts or {}, {
opts = vim.tbl_deep_extend('keep', opts or {}, {
shorten_path = true,
})
local config = require("oil.config")
local fs = require("oil.fs")
local config = require('oil.config')
local fs = require('oil.fs')
local entry = oil.get_cursor_entry()
if not entry then
return
@ -393,7 +393,7 @@ M.open_cmdline = {
return
end
local adapter = config.get_adapter_by_scheme(scheme)
if not adapter or not path or adapter.name ~= "files" then
if not adapter or not path or adapter.name ~= 'files' then
return
end
local fullpath = fs.posix_to_os_path(path) .. entry.name
@ -407,18 +407,18 @@ M.open_cmdline = {
end,
parameters = {
modify = {
desc = "Modify the path with |fnamemodify()| using this as the mods argument",
type = "string",
desc = 'Modify the path with |fnamemodify()| using this as the mods argument',
type = 'string',
},
shorten_path = {
desc = "Use relative paths when possible",
type = "boolean",
desc = 'Use relative paths when possible',
type = 'boolean',
},
},
}
M.yank_entry = {
desc = "Yank the filepath of the entry under the cursor to a register",
desc = 'Yank the filepath of the entry under the cursor to a register',
callback = function(opts)
opts = opts or {}
local entry = oil.get_cursor_entry()
@ -427,8 +427,8 @@ M.yank_entry = {
return
end
local name = entry.name
if entry.type == "directory" then
name = name .. "/"
if entry.type == 'directory' then
name = name .. '/'
end
local path = dir .. name
if opts.modify then
@ -438,14 +438,14 @@ M.yank_entry = {
end,
parameters = {
modify = {
desc = "Modify the path with |fnamemodify()| using this as the mods argument",
type = "string",
desc = 'Modify the path with |fnamemodify()| using this as the mods argument',
type = 'string',
},
},
}
M.copy_entry_path = {
desc = "Yank the filepath of the entry under the cursor to a register",
desc = 'Yank the filepath of the entry under the cursor to a register',
deprecated = true,
callback = function()
local entry = oil.get_cursor_entry()
@ -458,7 +458,7 @@ M.copy_entry_path = {
}
M.copy_entry_filename = {
desc = "Yank the filename of the entry under the cursor to a register",
desc = 'Yank the filename of the entry under the cursor to a register',
deprecated = true,
callback = function()
local entry = oil.get_cursor_entry()
@ -470,30 +470,30 @@ M.copy_entry_filename = {
}
M.copy_to_system_clipboard = {
desc = "Copy the entry under the cursor to the system clipboard",
desc = 'Copy the entry under the cursor to the system clipboard',
callback = function()
require("oil.clipboard").copy_to_system_clipboard()
require('oil.clipboard').copy_to_system_clipboard()
end,
}
M.paste_from_system_clipboard = {
desc = "Paste the system clipboard into the current oil directory",
desc = 'Paste the system clipboard into the current oil directory',
callback = function(opts)
require("oil.clipboard").paste_from_system_clipboard(opts and opts.delete_original)
require('oil.clipboard').paste_from_system_clipboard(opts and opts.delete_original)
end,
parameters = {
delete_original = {
type = "boolean",
desc = "Delete the original file after copying",
type = 'boolean',
desc = 'Delete the original file after copying',
},
},
}
M.open_cmdline_dir = {
desc = "Open vim cmdline with current directory as an argument",
desc = 'Open vim cmdline with current directory as an argument',
deprecated = true,
callback = function()
local fs = require("oil.fs")
local fs = require('oil.fs')
local dir = oil.get_current_dir()
if dir then
open_cmdline_with_path(fs.shorten_path(dir))
@ -502,7 +502,7 @@ M.open_cmdline_dir = {
}
M.change_sort = {
desc = "Change the sort order",
desc = 'Change the sort order',
callback = function(opts)
opts = opts or {}
@ -511,21 +511,21 @@ M.change_sort = {
return
end
local sort_cols = { "name", "size", "atime", "mtime", "ctime", "birthtime" }
vim.ui.select(sort_cols, { prompt = "Sort by", kind = "oil_sort_col" }, function(col)
local sort_cols = { 'name', 'size', 'atime', 'mtime', 'ctime', 'birthtime' }
vim.ui.select(sort_cols, { prompt = 'Sort by', kind = 'oil_sort_col' }, function(col)
if not col then
return
end
vim.ui.select(
{ "ascending", "descending" },
{ prompt = "Sort order", kind = "oil_sort_order" },
{ 'ascending', 'descending' },
{ prompt = 'Sort order', kind = 'oil_sort_order' },
function(order)
if not order then
return
end
order = order == "ascending" and "asc" or "desc"
order = order == 'ascending' and 'asc' or 'desc'
oil.set_sort({
{ "type", "asc" },
{ 'type', 'asc' },
{ col, order },
})
end
@ -534,24 +534,24 @@ M.change_sort = {
end,
parameters = {
sort = {
type = "oil.SortSpec[]",
desc = "List of columns plus direction (see |oil.set_sort|) instead of interactive selection",
type = 'oil.SortSpec[]',
desc = 'List of columns plus direction (see |oil.set_sort|) instead of interactive selection',
},
},
}
M.toggle_trash = {
desc = "Jump to and from the trash for the current directory",
desc = 'Jump to and from the trash for the current directory',
callback = function()
local fs = require("oil.fs")
local fs = require('oil.fs')
local bufname = vim.api.nvim_buf_get_name(0)
local scheme, path = util.parse_url(bufname)
local bufnr = vim.api.nvim_get_current_buf()
local url
if scheme == "oil://" then
url = "oil-trash://" .. path
elseif scheme == "oil-trash://" then
url = "oil://" .. path
if scheme == 'oil://' then
url = 'oil-trash://' .. path
elseif scheme == 'oil-trash://' then
url = 'oil://' .. path
-- The non-linux trash implementations don't support per-directory trash,
-- so jump back to the stored source buffer.
if not fs.is_linux then
@ -561,7 +561,7 @@ M.toggle_trash = {
end
end
else
vim.notify("No trash found for buffer", vim.log.levels.WARN)
vim.notify('No trash found for buffer', vim.log.levels.WARN)
return
end
vim.cmd.edit({ args = { url } })
@ -570,11 +570,11 @@ M.toggle_trash = {
}
M.send_to_qflist = {
desc = "Sends files in the current oil directory to the quickfix list, replacing the previous entries.",
desc = 'Sends files in the current oil directory to the quickfix list, replacing the previous entries.',
callback = function(opts)
opts = vim.tbl_deep_extend("keep", opts or {}, {
target = "qflist",
action = "r",
opts = vim.tbl_deep_extend('keep', opts or {}, {
target = 'qflist',
action = 'r',
only_matching_search = false,
})
util.send_to_quickfix({
@ -586,48 +586,48 @@ M.send_to_qflist = {
parameters = {
target = {
type = '"qflist"|"loclist"',
desc = "The target list to send files to",
desc = 'The target list to send files to',
},
action = {
type = '"r"|"a"',
desc = "Replace or add to current quickfix list (see |setqflist-action|)",
desc = 'Replace or add to current quickfix list (see |setqflist-action|)',
},
only_matching_search = {
type = "boolean",
desc = "Whether to only add the files that matches the last search. This option only applies when search highlighting is active",
type = 'boolean',
desc = 'Whether to only add the files that matches the last search. This option only applies when search highlighting is active',
},
},
}
M.add_to_qflist = {
desc = "Adds files in the current oil directory to the quickfix list, keeping the previous entries.",
desc = 'Adds files in the current oil directory to the quickfix list, keeping the previous entries.',
deprecated = true,
callback = function()
util.send_to_quickfix({
target = "qflist",
mode = "a",
target = 'qflist',
mode = 'a',
})
end,
}
M.send_to_loclist = {
desc = "Sends files in the current oil directory to the location list, replacing the previous entries.",
desc = 'Sends files in the current oil directory to the location list, replacing the previous entries.',
deprecated = true,
callback = function()
util.send_to_quickfix({
target = "loclist",
mode = "r",
target = 'loclist',
mode = 'r',
})
end,
}
M.add_to_loclist = {
desc = "Adds files in the current oil directory to the location list, keeping the previous entries.",
desc = 'Adds files in the current oil directory to the location list, keeping the previous entries.',
deprecated = true,
callback = function()
util.send_to_quickfix({
target = "loclist",
mode = "a",
target = 'loclist',
mode = 'a',
})
end,
}
@ -637,7 +637,7 @@ M.add_to_loclist = {
M._get_actions = function()
local ret = {}
for name, action in pairs(M) do
if type(action) == "table" and action.desc then
if type(action) == 'table' and action.desc then
table.insert(ret, {
name = name,
desc = action.desc,

View file

@ -1,12 +1,12 @@
local cache = require("oil.cache")
local columns = require("oil.columns")
local config = require("oil.config")
local constants = require("oil.constants")
local fs = require("oil.fs")
local git = require("oil.git")
local log = require("oil.log")
local permissions = require("oil.adapters.files.permissions")
local util = require("oil.util")
local cache = require('oil.cache')
local columns = require('oil.columns')
local config = require('oil.config')
local constants = require('oil.constants')
local fs = require('oil.fs')
local git = require('oil.git')
local log = require('oil.log')
local permissions = require('oil.adapters.files.permissions')
local util = require('oil.util')
local uv = vim.uv or vim.loop
local M = {}
@ -25,7 +25,7 @@ local function read_link_data(path, cb)
assert(link)
local stat_path = link
if not fs.is_absolute(link) then
stat_path = fs.join(vim.fn.fnamemodify(path, ":h"), link)
stat_path = fs.join(vim.fn.fnamemodify(path, ':h'), link)
end
uv.fs_stat(stat_path, function(stat_err, stat)
cb(nil, link, stat)
@ -43,7 +43,7 @@ end
---@return string
M.to_short_os_path = function(path, entry_type)
local shortpath = fs.shorten_path(fs.posix_to_os_path(path))
if entry_type == "directory" then
if entry_type == 'directory' then
shortpath = util.addslash(shortpath, true)
end
return shortpath
@ -61,13 +61,13 @@ file_columns.size = {
return columns.EMPTY
end
if stat.size >= 1e9 then
return string.format("%.1fG", stat.size / 1e9)
return string.format('%.1fG', stat.size / 1e9)
elseif stat.size >= 1e6 then
return string.format("%.1fM", stat.size / 1e6)
return string.format('%.1fM', stat.size / 1e6)
elseif stat.size >= 1e3 then
return string.format("%.1fk", stat.size / 1e3)
return string.format('%.1fk', stat.size / 1e3)
else
return string.format("%d", stat.size)
return string.format('%d', stat.size)
end
end,
@ -82,7 +82,7 @@ file_columns.size = {
end,
parse = function(line, conf)
return line:match("^(%d+%S*)%s+(.*)$")
return line:match('^(%d+%S*)%s+(.*)$')
end,
}
@ -120,7 +120,7 @@ if not fs.is_windows then
local _, path = util.parse_url(action.url)
assert(path)
return string.format(
"CHMOD %s %s",
'CHMOD %s %s',
permissions.mode_to_octal_str(action.value),
M.to_short_os_path(path, action.entry_type)
)
@ -147,10 +147,10 @@ end
local current_year
-- Make sure we run this import-time effect in the main loop (mostly for tests)
vim.schedule(function()
current_year = vim.fn.strftime("%Y")
current_year = vim.fn.strftime('%Y')
end)
for _, time_key in ipairs({ "ctime", "mtime", "atime", "birthtime" }) do
for _, time_key in ipairs({ 'ctime', 'mtime', 'atime', 'birthtime' }) do
file_columns[time_key] = {
require_stat = true,
@ -165,11 +165,11 @@ for _, time_key in ipairs({ "ctime", "mtime", "atime", "birthtime" }) do
if fmt then
ret = vim.fn.strftime(fmt, stat[time_key].sec)
else
local year = vim.fn.strftime("%Y", stat[time_key].sec)
local year = vim.fn.strftime('%Y', stat[time_key].sec)
if year ~= current_year then
ret = vim.fn.strftime("%b %d %Y", stat[time_key].sec)
ret = vim.fn.strftime('%b %d %Y', stat[time_key].sec)
else
ret = vim.fn.strftime("%b %d %H:%M", stat[time_key].sec)
ret = vim.fn.strftime('%b %d %H:%M', stat[time_key].sec)
end
end
return ret
@ -183,20 +183,20 @@ for _, time_key in ipairs({ "ctime", "mtime", "atime", "birthtime" }) do
-- and whitespace with a pattern that matches any amount of whitespace
-- e.g. "%b %d %Y" -> "%S+%s+%S+%s+%S+"
pattern = fmt
:gsub("%%.", "%%S+")
:gsub("%s+", "%%s+")
:gsub('%%.', '%%S+')
:gsub('%s+', '%%s+')
-- escape `()[]` because those are special characters in Lua patterns
:gsub(
"%(",
"%%("
'%(',
'%%('
)
:gsub("%)", "%%)")
:gsub("%[", "%%[")
:gsub("%]", "%%]")
:gsub('%)', '%%)')
:gsub('%[', '%%[')
:gsub('%]', '%%]')
else
pattern = "%S+%s+%d+%s+%d%d:?%d%d"
pattern = '%S+%s+%d+%s+%d%d:?%d%d'
end
return line:match("^(" .. pattern .. ")%s+(.+)$")
return line:match('^(' .. pattern .. ')%s+(.+)$')
end,
get_sort_value = function(entry)
@ -238,17 +238,17 @@ M.normalize_url = function(url, callback)
assert(path)
if fs.is_windows then
if path == "/" then
if path == '/' then
return callback(url)
else
local is_root_drive = path:match("^/%u$")
local is_root_drive = path:match('^/%u$')
if is_root_drive then
return callback(url .. "/")
return callback(url .. '/')
end
end
end
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(path), ":p")
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(path), ':p')
uv.fs_realpath(os_path, function(err, new_os_path)
local realpath
if fs.is_windows then
@ -264,8 +264,8 @@ M.normalize_url = function(url, callback)
vim.schedule_wrap(function(stat_err, stat)
local is_directory
if stat then
is_directory = stat.type == "directory"
elseif vim.endswith(realpath, "/") or (fs.is_windows and vim.endswith(realpath, "\\")) then
is_directory = stat.type == 'directory'
elseif vim.endswith(realpath, '/') or (fs.is_windows and vim.endswith(realpath, '\\')) then
is_directory = true
else
local filetype = vim.filetype.match({ filename = vim.fs.basename(realpath) })
@ -276,7 +276,7 @@ M.normalize_url = function(url, callback)
local norm_path = util.addslash(fs.os_to_posix_path(realpath))
callback(scheme .. norm_path)
else
callback(vim.fn.fnamemodify(realpath, ":."))
callback(vim.fn.fnamemodify(realpath, ':.'))
end
end)
)
@ -292,11 +292,11 @@ M.get_entry_path = function(url, entry, cb)
local scheme, path = util.parse_url(parent_url)
M.normalize_url(scheme .. path .. entry.name, cb)
else
if entry.type == "directory" then
if entry.type == 'directory' then
cb(url)
else
local _, path = util.parse_url(url)
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(assert(path)), ":p")
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(assert(path)), ':p')
cb(os_path)
end
end
@ -321,10 +321,10 @@ local function fetch_entry_metadata(parent_dir, entry, require_stat, cb)
end
-- Make sure we always get fs_stat info for links
if entry[FIELD_TYPE] == "link" then
if entry[FIELD_TYPE] == 'link' then
read_link_data(entry_path, function(link_err, link, link_stat)
if link_err then
log.warn("Error reading link data %s: %s", entry_path, link_err)
log.warn('Error reading link data %s: %s', entry_path, link_err)
return cb()
end
meta.link = link
@ -336,7 +336,7 @@ local function fetch_entry_metadata(parent_dir, entry, require_stat, cb)
-- The link is broken, so let's use the stat of the link itself
uv.fs_lstat(entry_path, function(stat_err, stat)
if stat_err then
log.warn("Error lstat link file %s: %s", entry_path, stat_err)
log.warn('Error lstat link file %s: %s', entry_path, stat_err)
return cb()
end
meta.stat = stat
@ -350,7 +350,7 @@ local function fetch_entry_metadata(parent_dir, entry, require_stat, cb)
elseif require_stat then
uv.fs_stat(entry_path, function(stat_err, stat)
if stat_err then
log.warn("Error stat file %s: %s", entry_path, stat_err)
log.warn('Error stat file %s: %s', entry_path, stat_err)
return cb()
end
assert(stat)
@ -368,11 +368,11 @@ end
if fs.is_windows then
local old_fetch_metadata = fetch_entry_metadata
fetch_entry_metadata = function(parent_dir, entry, require_stat, cb)
if entry[FIELD_TYPE] == "link" then
if entry[FIELD_TYPE] == 'link' then
local entry_path = fs.posix_to_os_path(parent_dir .. entry[FIELD_NAME])
uv.fs_lstat(entry_path, function(stat_err, stat)
if stat_err then
log.warn("Error lstat link file %s: %s", entry_path, stat_err)
log.warn('Error lstat link file %s: %s', entry_path, stat_err)
return old_fetch_metadata(parent_dir, entry, require_stat, cb)
end
assert(stat)
@ -398,17 +398,17 @@ local function list_windows_drives(url, column_defs, cb)
local _, path = util.parse_url(url)
assert(path)
local require_stat = columns_require_stat(column_defs)
local stdout = ""
local jid = vim.fn.jobstart({ "wmic", "logicaldisk", "get", "name" }, {
local stdout = ''
local jid = vim.fn.jobstart({ 'wmic', 'logicaldisk', 'get', 'name' }, {
stdout_buffered = true,
on_stdout = function(_, data)
stdout = table.concat(data, "\n")
stdout = table.concat(data, '\n')
end,
on_exit = function(_, code)
if code ~= 0 then
return cb("Error listing windows devices")
return cb('Error listing windows devices')
end
local lines = vim.split(stdout, "\n", { plain = true, trimempty = true })
local lines = vim.split(stdout, '\n', { plain = true, trimempty = true })
-- Remove the "Name" header
table.remove(lines, 1)
local internal_entries = {}
@ -421,12 +421,12 @@ local function list_windows_drives(url, column_defs, cb)
end)
for _, disk in ipairs(lines) do
if disk:match("^%s*$") then
if disk:match('^%s*$') then
-- Skip empty line
complete_disk_cb()
else
disk = disk:gsub(":%s*$", "")
local cache_entry = cache.create_entry(url, disk, "directory")
disk = disk:gsub(':%s*$', '')
local cache_entry = cache.create_entry(url, disk, 'directory')
table.insert(internal_entries, cache_entry)
fetch_entry_metadata(path, cache_entry, require_stat, complete_disk_cb)
end
@ -434,7 +434,7 @@ local function list_windows_drives(url, column_defs, cb)
end,
})
if jid <= 0 then
cb("Could not list windows devices")
cb('Could not list windows devices')
end
end
@ -444,7 +444,7 @@ end
M.list = function(url, column_defs, cb)
local _, path = util.parse_url(url)
assert(path)
if fs.is_windows and path == "/" then
if fs.is_windows and path == '/' then
return list_windows_drives(url, column_defs, cb)
end
local dir = fs.posix_to_os_path(path)
@ -453,7 +453,7 @@ M.list = function(url, column_defs, cb)
---@diagnostic disable-next-line: param-type-mismatch, discard-returns
uv.fs_opendir(dir, function(open_err, fd)
if open_err then
if open_err:match("^ENOENT: no such file or directory") then
if open_err:match('^ENOENT: no such file or directory') then
-- If the directory doesn't exist, treat the list as a success. We will be able to traverse
-- and edit a not-yet-existing directory.
return cb()
@ -505,7 +505,7 @@ M.is_modifiable = function(bufnr)
local bufname = vim.api.nvim_buf_get_name(bufnr)
local _, path = util.parse_url(bufname)
assert(path)
if fs.is_windows and path == "/" then
if fs.is_windows and path == '/' then
return false
end
local dir = fs.posix_to_os_path(path)
@ -515,30 +515,30 @@ M.is_modifiable = function(bufnr)
end
-- fs_access can return nil, force boolean return
return uv.fs_access(dir, "W") == true
return uv.fs_access(dir, 'W') == true
end
---@param action oil.Action
---@return string
M.render_action = function(action)
if action.type == "create" then
if action.type == 'create' then
local _, path = util.parse_url(action.url)
assert(path)
local ret = string.format("CREATE %s", M.to_short_os_path(path, action.entry_type))
local ret = string.format('CREATE %s', M.to_short_os_path(path, action.entry_type))
if action.link then
ret = ret .. " -> " .. fs.posix_to_os_path(action.link)
ret = ret .. ' -> ' .. fs.posix_to_os_path(action.link)
end
return ret
elseif action.type == "delete" then
elseif action.type == 'delete' then
local _, path = util.parse_url(action.url)
assert(path)
local short_path = M.to_short_os_path(path, action.entry_type)
if config.delete_to_trash then
return string.format(" TRASH %s", short_path)
return string.format(' TRASH %s', short_path)
else
return string.format("DELETE %s", short_path)
return string.format('DELETE %s', short_path)
end
elseif action.type == "move" or action.type == "copy" then
elseif action.type == 'move' or action.type == 'copy' then
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if dest_adapter == M then
local _, src_path = util.parse_url(action.src_url)
@ -546,7 +546,7 @@ M.render_action = function(action)
local _, dest_path = util.parse_url(action.dest_url)
assert(dest_path)
return string.format(
" %s %s -> %s",
' %s %s -> %s',
action.type:upper(),
M.to_short_os_path(src_path, action.entry_type),
M.to_short_os_path(dest_path, action.entry_type)
@ -563,7 +563,7 @@ end
---@param action oil.Action
---@param cb fun(err: nil|string)
M.perform_action = function(action, cb)
if action.type == "create" then
if action.type == 'create' then
local _, path = util.parse_url(action.url)
assert(path)
path = fs.posix_to_os_path(path)
@ -579,16 +579,16 @@ M.perform_action = function(action, cb)
end)
end
if action.entry_type == "directory" then
if action.entry_type == 'directory' then
uv.fs_mkdir(path, config.new_dir_mode, function(err)
-- Ignore if the directory already exists
if not err or err:match("^EEXIST:") then
if not err or err:match('^EEXIST:') then
cb()
else
cb(err)
end
end)
elseif action.entry_type == "link" and action.link then
elseif action.entry_type == 'link' and action.link then
local flags = nil
local target = fs.posix_to_os_path(action.link)
if fs.is_windows then
@ -602,7 +602,7 @@ M.perform_action = function(action, cb)
else
fs.touch(path, config.new_file_mode, cb)
end
elseif action.type == "delete" then
elseif action.type == 'delete' then
local _, path = util.parse_url(action.url)
assert(path)
path = fs.posix_to_os_path(path)
@ -619,11 +619,11 @@ M.perform_action = function(action, cb)
end
if config.delete_to_trash then
require("oil.adapters.trash").delete_to_trash(path, cb)
require('oil.adapters.trash').delete_to_trash(path, cb)
else
fs.recursive_delete(action.entry_type, path, cb)
end
elseif action.type == "move" then
elseif action.type == 'move' then
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if dest_adapter == M then
local _, src_path = util.parse_url(action.src_url)
@ -641,7 +641,7 @@ M.perform_action = function(action, cb)
-- We should never hit this because we don't implement supported_cross_adapter_actions
cb("files adapter doesn't support cross-adapter move")
end
elseif action.type == "copy" then
elseif action.type == 'copy' then
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if dest_adapter == M then
local _, src_path = util.parse_url(action.src_url)
@ -656,7 +656,7 @@ M.perform_action = function(action, cb)
cb("files adapter doesn't support cross-adapter copy")
end
else
cb(string.format("Bad action type: %s", action.type))
cb(string.format('Bad action type: %s', action.type))
end
end

View file

@ -4,7 +4,7 @@ local M = {}
---@param num integer
---@return string
local function perm_to_str(exe_modifier, num)
local str = (bit.band(num, 4) ~= 0 and "r" or "-") .. (bit.band(num, 2) ~= 0 and "w" or "-")
local str = (bit.band(num, 4) ~= 0 and 'r' or '-') .. (bit.band(num, 2) ~= 0 and 'w' or '-')
if exe_modifier then
if bit.band(num, 1) ~= 0 then
return str .. exe_modifier
@ -12,7 +12,7 @@ local function perm_to_str(exe_modifier, num)
return str .. exe_modifier:upper()
end
else
return str .. (bit.band(num, 1) ~= 0 and "x" or "-")
return str .. (bit.band(num, 1) ~= 0 and 'x' or '-')
end
end
@ -20,9 +20,9 @@ end
---@return string
M.mode_to_str = function(mode)
local extra = bit.rshift(mode, 9)
return perm_to_str(bit.band(extra, 4) ~= 0 and "s", bit.rshift(mode, 6))
.. perm_to_str(bit.band(extra, 2) ~= 0 and "s", bit.rshift(mode, 3))
.. perm_to_str(bit.band(extra, 1) ~= 0 and "t", mode)
return perm_to_str(bit.band(extra, 4) ~= 0 and 's', bit.rshift(mode, 6))
.. perm_to_str(bit.band(extra, 2) ~= 0 and 's', bit.rshift(mode, 3))
.. perm_to_str(bit.band(extra, 1) ~= 0 and 't', mode)
end
---@param mode integer
@ -38,25 +38,25 @@ end
---@param str string String of 3 characters
---@return nil|integer
local function str_to_mode(str)
local r, w, x = unpack(vim.split(str, "", {}))
local r, w, x = unpack(vim.split(str, '', {}))
local mode = 0
if r == "r" then
if r == 'r' then
mode = bit.bor(mode, 4)
elseif r ~= "-" then
elseif r ~= '-' then
return nil
end
if w == "w" then
if w == 'w' then
mode = bit.bor(mode, 2)
elseif w ~= "-" then
elseif w ~= '-' then
return nil
end
-- t means sticky and executable
-- T means sticky, not executable
-- s means setuid/setgid and executable
-- S means setuid/setgid and not executable
if x == "x" or x == "t" or x == "s" then
if x == 'x' or x == 't' or x == 's' then
mode = bit.bor(mode, 1)
elseif x ~= "-" and x ~= "T" and x ~= "S" then
elseif x ~= '-' and x ~= 'T' and x ~= 'S' then
return nil
end
return mode
@ -67,13 +67,13 @@ end
local function parse_extra_bits(perm)
perm = perm:lower()
local mode = 0
if perm:sub(3, 3) == "s" then
if perm:sub(3, 3) == 's' then
mode = bit.bor(mode, 4)
end
if perm:sub(6, 6) == "s" then
if perm:sub(6, 6) == 's' then
mode = bit.bor(mode, 2)
end
if perm:sub(9, 9) == "t" then
if perm:sub(9, 9) == 't' then
mode = bit.bor(mode, 1)
end
return mode
@ -83,7 +83,7 @@ end
---@return nil|integer
---@return nil|string
M.parse = function(line)
local strval, rem = line:match("^([r%-][w%-][xsS%-][r%-][w%-][xsS%-][r%-][w%-][xtT%-])%s*(.*)$")
local strval, rem = line:match('^([r%-][w%-][xsS%-][r%-][w%-][xsS%-][r%-][w%-][xtT%-])%s*(.*)$')
if not strval then
return
end

View file

@ -1,11 +1,11 @@
local config = require("oil.config")
local constants = require("oil.constants")
local files = require("oil.adapters.files")
local fs = require("oil.fs")
local loading = require("oil.loading")
local pathutil = require("oil.pathutil")
local s3fs = require("oil.adapters.s3.s3fs")
local util = require("oil.util")
local config = require('oil.config')
local constants = require('oil.constants')
local files = require('oil.adapters.files')
local fs = require('oil.fs')
local loading = require('oil.loading')
local pathutil = require('oil.pathutil')
local s3fs = require('oil.adapters.s3.s3fs')
local util = require('oil.util')
local M = {}
local FIELD_META = constants.FIELD_META
@ -21,11 +21,11 @@ M.parse_url = function(oil_url)
local scheme, url = util.parse_url(oil_url)
assert(scheme and url, string.format("Malformed input url '%s'", oil_url))
local ret = { scheme = scheme }
local bucket, path = url:match("^([^/]+)/?(.*)$")
local bucket, path = url:match('^([^/]+)/?(.*)$')
ret.bucket = bucket
ret.path = path ~= "" and path or nil
ret.path = path ~= '' and path or nil
if not ret.bucket and ret.path then
error(string.format("Parsing error for s3 url: %s", oil_url))
error(string.format('Parsing error for s3 url: %s', oil_url))
end
---@cast ret oil.s3Url
return ret
@ -36,43 +36,43 @@ end
local function url_to_str(url)
local pieces = { url.scheme }
if url.bucket then
assert(url.bucket ~= "")
assert(url.bucket ~= '')
table.insert(pieces, url.bucket)
table.insert(pieces, "/")
table.insert(pieces, '/')
end
if url.path then
assert(url.path ~= "")
assert(url.path ~= '')
table.insert(pieces, url.path)
end
return table.concat(pieces, "")
return table.concat(pieces, '')
end
---@param url oil.s3Url
---@param is_folder boolean
---@return string
local function url_to_s3(url, is_folder)
local pieces = { "s3://" }
local pieces = { 's3://' }
if url.bucket then
assert(url.bucket ~= "")
assert(url.bucket ~= '')
table.insert(pieces, url.bucket)
table.insert(pieces, "/")
table.insert(pieces, '/')
end
if url.path then
assert(url.path ~= "")
assert(url.path ~= '')
table.insert(pieces, url.path)
if is_folder and not vim.endswith(url.path, "/") then
table.insert(pieces, "/")
if is_folder and not vim.endswith(url.path, '/') then
table.insert(pieces, '/')
end
end
return table.concat(pieces, "")
return table.concat(pieces, '')
end
---@param url oil.s3Url
---@return boolean
local function is_bucket(url)
assert(url.bucket and url.bucket ~= "")
assert(url.bucket and url.bucket ~= '')
if url.path then
assert(url.path ~= "")
assert(url.path ~= '')
return false
end
return true
@ -83,20 +83,20 @@ s3_columns.size = {
render = function(entry, conf)
local meta = entry[FIELD_META]
if not meta or not meta.size then
return ""
return ''
elseif meta.size >= 1e9 then
return string.format("%.1fG", meta.size / 1e9)
return string.format('%.1fG', meta.size / 1e9)
elseif meta.size >= 1e6 then
return string.format("%.1fM", meta.size / 1e6)
return string.format('%.1fM', meta.size / 1e6)
elseif meta.size >= 1e3 then
return string.format("%.1fk", meta.size / 1e3)
return string.format('%.1fk', meta.size / 1e3)
else
return string.format("%d", meta.size)
return string.format('%d', meta.size)
end
end,
parse = function(line, conf)
return line:match("^(%d+%S*)%s+(.*)$")
return line:match('^(%d+%S*)%s+(.*)$')
end,
get_sort_value = function(entry)
@ -113,21 +113,21 @@ s3_columns.birthtime = {
render = function(entry, conf)
local meta = entry[FIELD_META]
if not meta or not meta.date then
return ""
return ''
else
return meta.date
end
end,
parse = function(line, conf)
return line:match("^(%d+%-%d+%-%d+%s%d+:%d+:%d+)%s+(.*)$")
return line:match('^(%d+%-%d+%-%d+%s%d+:%d+:%d+)%s+(.*)$')
end,
get_sort_value = function(entry)
local meta = entry[FIELD_META]
if meta and meta.date then
local year, month, day, hour, min, sec =
meta.date:match("^(%d+)%-(%d+)%-(%d+)%s(%d+):(%d+):(%d+)$")
meta.date:match('^(%d+)%-(%d+)%-(%d+)%s(%d+):(%d+):(%d+)$')
local time =
os.time({ year = year, month = month, day = day, hour = hour, min = min, sec = sec })
return time
@ -148,9 +148,9 @@ end
M.get_parent = function(bufname)
local res = M.parse_url(bufname)
if res.path then
assert(res.path ~= "")
assert(res.path ~= '')
local path = pathutil.parent(res.path)
res.path = path ~= "" and path or nil
res.path = path ~= '' and path or nil
else
res.bucket = nil
end
@ -168,8 +168,8 @@ end
---@param column_defs string[]
---@param callback fun(err?: string, entries?: oil.InternalEntry[], fetch_more?: fun())
M.list = function(url, column_defs, callback)
if vim.fn.executable("aws") ~= 1 then
callback("`aws` is not executable. Can you run `aws s3 ls`?")
if vim.fn.executable('aws') ~= 1 then
callback('`aws` is not executable. Can you run `aws s3 ls`?')
return
end
@ -187,16 +187,16 @@ end
---@param action oil.Action
---@return string
M.render_action = function(action)
local is_folder = action.entry_type == "directory"
if action.type == "create" then
local is_folder = action.entry_type == 'directory'
if action.type == 'create' then
local res = M.parse_url(action.url)
local extra = is_bucket(res) and "BUCKET " or ""
return string.format("CREATE %s%s", extra, url_to_s3(res, is_folder))
elseif action.type == "delete" then
local extra = is_bucket(res) and 'BUCKET ' or ''
return string.format('CREATE %s%s', extra, url_to_s3(res, is_folder))
elseif action.type == 'delete' then
local res = M.parse_url(action.url)
local extra = is_bucket(res) and "BUCKET " or ""
return string.format("DELETE %s%s", extra, url_to_s3(res, is_folder))
elseif action.type == "move" or action.type == "copy" then
local extra = is_bucket(res) and 'BUCKET ' or ''
return string.format('DELETE %s%s', extra, url_to_s3(res, is_folder))
elseif action.type == 'move' or action.type == 'copy' then
local src = action.src_url
local dest = action.dest_url
if config.get_adapter_by_scheme(src) ~= M then
@ -210,7 +210,7 @@ M.render_action = function(action)
dest = files.to_short_os_path(path, action.entry_type)
src = url_to_s3(M.parse_url(src), is_folder)
end
return string.format(" %s %s -> %s", action.type:upper(), src, dest)
return string.format(' %s %s -> %s', action.type:upper(), src, dest)
else
error(string.format("Bad action type: '%s'", action.type))
end
@ -219,30 +219,30 @@ end
---@param action oil.Action
---@param cb fun(err: nil|string)
M.perform_action = function(action, cb)
local is_folder = action.entry_type == "directory"
if action.type == "create" then
local is_folder = action.entry_type == 'directory'
if action.type == 'create' then
local res = M.parse_url(action.url)
local bucket = is_bucket(res)
if action.entry_type == "directory" and bucket then
if action.entry_type == 'directory' and bucket then
s3fs.mb(url_to_s3(res, true), cb)
elseif action.entry_type == "directory" or action.entry_type == "file" then
elseif action.entry_type == 'directory' or action.entry_type == 'file' then
s3fs.touch(url_to_s3(res, is_folder), cb)
else
cb(string.format("Bad entry type on s3 create action: %s", action.entry_type))
cb(string.format('Bad entry type on s3 create action: %s', action.entry_type))
end
elseif action.type == "delete" then
elseif action.type == 'delete' then
local res = M.parse_url(action.url)
local bucket = is_bucket(res)
if action.entry_type == "directory" and bucket then
if action.entry_type == 'directory' and bucket then
s3fs.rb(url_to_s3(res, true), cb)
elseif action.entry_type == "directory" or action.entry_type == "file" then
elseif action.entry_type == 'directory' or action.entry_type == 'file' then
s3fs.rm(url_to_s3(res, is_folder), is_folder, cb)
else
cb(string.format("Bad entry type on s3 delete action: %s", action.entry_type))
cb(string.format('Bad entry type on s3 delete action: %s', action.entry_type))
end
elseif action.type == "move" then
elseif action.type == 'move' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if
@ -250,7 +250,7 @@ M.perform_action = function(action, cb)
then
cb(
string.format(
"We should never attempt to move from the %s adapter to the %s adapter.",
'We should never attempt to move from the %s adapter to the %s adapter.',
src_adapter.name,
dest_adapter.name
)
@ -276,7 +276,7 @@ M.perform_action = function(action, cb)
assert(dest)
s3fs.mv(src, dest, is_folder, cb)
elseif action.type == "copy" then
elseif action.type == 'copy' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if
@ -284,7 +284,7 @@ M.perform_action = function(action, cb)
then
cb(
string.format(
"We should never attempt to copy from the %s adapter to the %s adapter.",
'We should never attempt to copy from the %s adapter to the %s adapter.',
src_adapter.name,
dest_adapter.name
)
@ -311,11 +311,11 @@ M.perform_action = function(action, cb)
s3fs.cp(src, dest, is_folder, cb)
else
cb(string.format("Bad action type: %s", action.type))
cb(string.format('Bad action type: %s', action.type))
end
end
M.supported_cross_adapter_actions = { files = "move" }
M.supported_cross_adapter_actions = { files = 'move' }
---@param bufnr integer
M.read_file = function(bufnr)
@ -323,11 +323,11 @@ M.read_file = function(bufnr)
local bufname = vim.api.nvim_buf_get_name(bufnr)
local url = M.parse_url(bufname)
local basename = pathutil.basename(bufname)
local cache_dir = vim.fn.stdpath("cache")
assert(type(cache_dir) == "string")
local tmpdir = fs.join(cache_dir, "oil")
local cache_dir = vim.fn.stdpath('cache')
assert(type(cache_dir) == 'string')
local tmpdir = fs.join(cache_dir, 'oil')
fs.mkdirp(tmpdir)
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, "s3_XXXXXX"))
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, 's3_XXXXXX'))
if fd then
vim.loop.fs_close(fd)
end
@ -336,9 +336,9 @@ M.read_file = function(bufnr)
s3fs.cp(url_to_s3(url, false), tmpfile, false, function(err)
loading.set_loading(bufnr, false)
vim.bo[bufnr].modifiable = true
vim.cmd.doautocmd({ args = { "BufReadPre", bufname }, mods = { silent = true } })
vim.cmd.doautocmd({ args = { 'BufReadPre', bufname }, mods = { silent = true } })
if err then
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, vim.split(err, "\n"))
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, vim.split(err, '\n'))
else
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {})
vim.api.nvim_buf_call(bufnr, function()
@ -352,7 +352,7 @@ M.read_file = function(bufnr)
if filetype then
vim.bo[bufnr].filetype = filetype
end
vim.cmd.doautocmd({ args = { "BufReadPost", bufname }, mods = { silent = true } })
vim.cmd.doautocmd({ args = { 'BufReadPost', bufname }, mods = { silent = true } })
vim.api.nvim_buf_delete(tmp_bufnr, { force = true })
end)
end
@ -361,14 +361,14 @@ end
M.write_file = function(bufnr)
local bufname = vim.api.nvim_buf_get_name(bufnr)
local url = M.parse_url(bufname)
local cache_dir = vim.fn.stdpath("cache")
assert(type(cache_dir) == "string")
local tmpdir = fs.join(cache_dir, "oil")
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, "s3_XXXXXXXX"))
local cache_dir = vim.fn.stdpath('cache')
assert(type(cache_dir) == 'string')
local tmpdir = fs.join(cache_dir, 'oil')
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, 's3_XXXXXXXX'))
if fd then
vim.loop.fs_close(fd)
end
vim.cmd.doautocmd({ args = { "BufWritePre", bufname }, mods = { silent = true } })
vim.cmd.doautocmd({ args = { 'BufWritePre', bufname }, mods = { silent = true } })
vim.bo[bufnr].modifiable = false
vim.cmd.write({ args = { tmpfile }, bang = true, mods = { silent = true, noautocmd = true } })
local tmp_bufnr = vim.fn.bufadd(tmpfile)
@ -376,10 +376,10 @@ M.write_file = function(bufnr)
s3fs.cp(tmpfile, url_to_s3(url, false), false, function(err)
vim.bo[bufnr].modifiable = true
if err then
vim.notify(string.format("Error writing file: %s", err), vim.log.levels.ERROR)
vim.notify(string.format('Error writing file: %s', err), vim.log.levels.ERROR)
else
vim.bo[bufnr].modified = false
vim.cmd.doautocmd({ args = { "BufWritePost", bufname }, mods = { silent = true } })
vim.cmd.doautocmd({ args = { 'BufWritePost', bufname }, mods = { silent = true } })
end
vim.loop.fs_unlink(tmpfile)
vim.api.nvim_buf_delete(tmp_bufnr, { force = true })

View file

@ -1,8 +1,8 @@
local cache = require("oil.cache")
local config = require("oil.config")
local constants = require("oil.constants")
local shell = require("oil.shell")
local util = require("oil.util")
local cache = require('oil.cache')
local config = require('oil.config')
local constants = require('oil.constants')
local shell = require('oil.shell')
local util = require('oil.util')
local M = {}
@ -13,11 +13,11 @@ local FIELD_META = constants.FIELD_META
---@return oil.EntryType
---@return table Metadata for entry
local function parse_ls_line_bucket(line)
local date, name = line:match("^(%d+%-%d+%-%d+%s%d+:%d+:%d+)%s+(.*)$")
local date, name = line:match('^(%d+%-%d+%-%d+%s%d+:%d+:%d+)%s+(.*)$')
if not date or not name then
error(string.format("Could not parse '%s'", line))
end
local type = "directory"
local type = 'directory'
local meta = { date = date }
return name, type, meta
end
@ -27,18 +27,18 @@ end
---@return oil.EntryType
---@return table Metadata for entry
local function parse_ls_line_file(line)
local name = line:match("^%s+PRE%s+(.*)/$")
local type = "directory"
local name = line:match('^%s+PRE%s+(.*)/$')
local type = 'directory'
local meta = {}
if name then
return name, type, meta
end
local date, size
date, size, name = line:match("^(%d+%-%d+%-%d+%s%d+:%d+:%d+)%s+(%d+)%s+(.*)$")
date, size, name = line:match('^(%d+%-%d+%-%d+%s%d+:%d+:%d+)%s+(%d+)%s+(.*)$')
if not name then
error(string.format("Could not parse '%s'", line))
end
type = "file"
type = 'file'
meta = { date = date, size = tonumber(size) }
return name, type, meta
end
@ -46,7 +46,7 @@ end
---@param cmd string[] cmd and flags
---@return string[] Shell command to run
local function create_s3_command(cmd)
local full_cmd = vim.list_extend({ "aws", "s3" }, cmd)
local full_cmd = vim.list_extend({ 'aws', 's3' }, cmd)
return vim.list_extend(full_cmd, config.extra_s3_args)
end
@ -54,7 +54,7 @@ end
---@param path string
---@param callback fun(err?: string, entries?: oil.InternalEntry[], fetch_more?: fun())
function M.list_dir(url, path, callback)
local cmd = create_s3_command({ "ls", path, "--color=off", "--no-cli-pager" })
local cmd = create_s3_command({ 'ls', path, '--color=off', '--no-cli-pager' })
shell.run(cmd, function(err, lines)
if err then
return callback(err)
@ -63,13 +63,13 @@ function M.list_dir(url, path, callback)
local cache_entries = {}
local url_path, _
_, url_path = util.parse_url(url)
local is_top_level = url_path == nil or url_path:match("/") == nil
local is_top_level = url_path == nil or url_path:match('/') == nil
local parse_ls_line = is_top_level and parse_ls_line_bucket or parse_ls_line_file
for _, line in ipairs(lines) do
if line ~= "" then
if line ~= '' then
local name, type, meta = parse_ls_line(line)
-- in s3 '-' can be used to create an "empty folder"
if name ~= "-" then
if name ~= '-' then
local cache_entry = cache.create_entry(url, name, type)
table.insert(cache_entries, cache_entry)
cache_entry[FIELD_META] = meta
@ -85,8 +85,8 @@ end
---@param callback fun(err: nil|string)
function M.touch(path, callback)
-- here "-" means that we copy from stdin
local cmd = create_s3_command({ "cp", "-", path })
shell.run(cmd, { stdin = "null" }, callback)
local cmd = create_s3_command({ 'cp', '-', path })
shell.run(cmd, { stdin = 'null' }, callback)
end
--- Remove files
@ -94,9 +94,9 @@ end
---@param is_folder boolean
---@param callback fun(err: nil|string)
function M.rm(path, is_folder, callback)
local main_cmd = { "rm", path }
local main_cmd = { 'rm', path }
if is_folder then
table.insert(main_cmd, "--recursive")
table.insert(main_cmd, '--recursive')
end
local cmd = create_s3_command(main_cmd)
shell.run(cmd, callback)
@ -106,7 +106,7 @@ end
---@param bucket string
---@param callback fun(err: nil|string)
function M.rb(bucket, callback)
local cmd = create_s3_command({ "rb", bucket })
local cmd = create_s3_command({ 'rb', bucket })
shell.run(cmd, callback)
end
@ -114,7 +114,7 @@ end
---@param bucket string
---@param callback fun(err: nil|string)
function M.mb(bucket, callback)
local cmd = create_s3_command({ "mb", bucket })
local cmd = create_s3_command({ 'mb', bucket })
shell.run(cmd, callback)
end
@ -124,9 +124,9 @@ end
---@param is_folder boolean
---@param callback fun(err: nil|string)
function M.mv(src, dest, is_folder, callback)
local main_cmd = { "mv", src, dest }
local main_cmd = { 'mv', src, dest }
if is_folder then
table.insert(main_cmd, "--recursive")
table.insert(main_cmd, '--recursive')
end
local cmd = create_s3_command(main_cmd)
shell.run(cmd, callback)
@ -138,9 +138,9 @@ end
---@param is_folder boolean
---@param callback fun(err: nil|string)
function M.cp(src, dest, is_folder, callback)
local main_cmd = { "cp", src, dest }
local main_cmd = { 'cp', src, dest }
if is_folder then
table.insert(main_cmd, "--recursive")
table.insert(main_cmd, '--recursive')
end
local cmd = create_s3_command(main_cmd)
shell.run(cmd, callback)

View file

@ -1,13 +1,13 @@
local config = require("oil.config")
local constants = require("oil.constants")
local files = require("oil.adapters.files")
local fs = require("oil.fs")
local loading = require("oil.loading")
local pathutil = require("oil.pathutil")
local permissions = require("oil.adapters.files.permissions")
local shell = require("oil.shell")
local sshfs = require("oil.adapters.ssh.sshfs")
local util = require("oil.util")
local config = require('oil.config')
local constants = require('oil.constants')
local files = require('oil.adapters.files')
local fs = require('oil.fs')
local loading = require('oil.loading')
local pathutil = require('oil.pathutil')
local permissions = require('oil.adapters.files.permissions')
local shell = require('oil.shell')
local sshfs = require('oil.adapters.ssh.sshfs')
local util = require('oil.util')
local M = {}
local FIELD_NAME = constants.FIELD_NAME
@ -22,7 +22,7 @@ local FIELD_META = constants.FIELD_META
---@param args string[]
local function scp(args, ...)
local cmd = vim.list_extend({ "scp", "-C" }, config.extra_scp_args)
local cmd = vim.list_extend({ 'scp', '-C' }, config.extra_scp_args)
vim.list_extend(cmd, args)
shell.run(cmd, ...)
end
@ -33,21 +33,21 @@ M.parse_url = function(oil_url)
local scheme, url = util.parse_url(oil_url)
assert(scheme and url, string.format("Malformed input url '%s'", oil_url))
local ret = { scheme = scheme }
local username, rem = url:match("^([^@%s]+)@(.*)$")
local username, rem = url:match('^([^@%s]+)@(.*)$')
ret.user = username
url = rem or url
local host, port, path = url:match("^([^:]+):(%d+)/(.*)$")
local host, port, path = url:match('^([^:]+):(%d+)/(.*)$')
if host then
ret.host = host
ret.port = tonumber(port)
ret.path = path
else
host, path = url:match("^([^/]+)/(.*)$")
host, path = url:match('^([^/]+)/(.*)$')
ret.host = host
ret.path = path
end
if not ret.host or not ret.path then
error(string.format("Malformed SSH url: %s", oil_url))
error(string.format('Malformed SSH url: %s', oil_url))
end
---@cast ret oil.sshUrl
@ -60,33 +60,33 @@ local function url_to_str(url)
local pieces = { url.scheme }
if url.user then
table.insert(pieces, url.user)
table.insert(pieces, "@")
table.insert(pieces, '@')
end
table.insert(pieces, url.host)
if url.port then
table.insert(pieces, string.format(":%d", url.port))
table.insert(pieces, string.format(':%d', url.port))
end
table.insert(pieces, "/")
table.insert(pieces, '/')
table.insert(pieces, url.path)
return table.concat(pieces, "")
return table.concat(pieces, '')
end
---@param url oil.sshUrl
---@return string
local function url_to_scp(url)
local pieces = { "scp://" }
local pieces = { 'scp://' }
if url.user then
table.insert(pieces, url.user)
table.insert(pieces, "@")
table.insert(pieces, '@')
end
table.insert(pieces, url.host)
if url.port then
table.insert(pieces, string.format(":%d", url.port))
table.insert(pieces, string.format(':%d', url.port))
end
table.insert(pieces, "/")
table.insert(pieces, '/')
local escaped_path = util.url_escape(url.path)
table.insert(pieces, escaped_path)
return table.concat(pieces, "")
return table.concat(pieces, '')
end
---@param url1 oil.sshUrl
@ -103,7 +103,7 @@ local _connections = {}
local function get_connection(url, allow_retry)
local res = M.parse_url(url)
res.scheme = config.adapter_to_scheme.ssh
res.path = ""
res.path = ''
local key = url_to_str(res)
local conn = _connections[key]
if not conn or (allow_retry and conn:get_connection_error()) then
@ -137,7 +137,7 @@ ssh_columns.permissions = {
end,
render_action = function(action)
return string.format("CHMOD %s %s", permissions.mode_to_octal_str(action.value), action.url)
return string.format('CHMOD %s %s', permissions.mode_to_octal_str(action.value), action.url)
end,
perform_action = function(action, callback)
@ -151,20 +151,20 @@ ssh_columns.size = {
render = function(entry, conf)
local meta = entry[FIELD_META]
if not meta or not meta.size then
return ""
return ''
elseif meta.size >= 1e9 then
return string.format("%.1fG", meta.size / 1e9)
return string.format('%.1fG', meta.size / 1e9)
elseif meta.size >= 1e6 then
return string.format("%.1fM", meta.size / 1e6)
return string.format('%.1fM', meta.size / 1e6)
elseif meta.size >= 1e3 then
return string.format("%.1fk", meta.size / 1e3)
return string.format('%.1fk', meta.size / 1e3)
else
return string.format("%d", meta.size)
return string.format('%d', meta.size)
end
end,
parse = function(line, conf)
return line:match("^(%d+%S*)%s+(.*)$")
return line:match('^(%d+%S*)%s+(.*)$')
end,
get_sort_value = function(entry)
@ -206,13 +206,13 @@ M.normalize_url = function(url, callback)
local conn = get_connection(url, true)
local path = res.path
if path == "" then
path = "."
if path == '' then
path = '.'
end
conn:realpath(path, function(err, abspath)
if err then
vim.notify(string.format("Error normalizing url %s: %s", url, err), vim.log.levels.WARN)
vim.notify(string.format('Error normalizing url %s: %s', url, err), vim.log.levels.WARN)
callback(url)
else
res.path = abspath
@ -259,15 +259,15 @@ end
---@param action oil.Action
---@return string
M.render_action = function(action)
if action.type == "create" then
local ret = string.format("CREATE %s", action.url)
if action.type == 'create' then
local ret = string.format('CREATE %s', action.url)
if action.link then
ret = ret .. " -> " .. action.link
ret = ret .. ' -> ' .. action.link
end
return ret
elseif action.type == "delete" then
return string.format("DELETE %s", action.url)
elseif action.type == "move" or action.type == "copy" then
elseif action.type == 'delete' then
return string.format('DELETE %s', action.url)
elseif action.type == 'move' or action.type == 'copy' then
local src = action.src_url
local dest = action.dest_url
if config.get_adapter_by_scheme(src) == M then
@ -279,7 +279,7 @@ M.render_action = function(action)
assert(path)
src = files.to_short_os_path(path, action.entry_type)
end
return string.format(" %s %s -> %s", action.type:upper(), src, dest)
return string.format(' %s %s -> %s', action.type:upper(), src, dest)
else
error(string.format("Bad action type: '%s'", action.type))
end
@ -288,21 +288,21 @@ end
---@param action oil.Action
---@param cb fun(err: nil|string)
M.perform_action = function(action, cb)
if action.type == "create" then
if action.type == 'create' then
local res = M.parse_url(action.url)
local conn = get_connection(action.url)
if action.entry_type == "directory" then
if action.entry_type == 'directory' then
conn:mkdir(res.path, cb)
elseif action.entry_type == "link" and action.link then
elseif action.entry_type == 'link' and action.link then
conn:mklink(res.path, action.link, cb)
else
conn:touch(res.path, cb)
end
elseif action.type == "delete" then
elseif action.type == 'delete' then
local res = M.parse_url(action.url)
local conn = get_connection(action.url)
conn:rm(res.path, cb)
elseif action.type == "move" then
elseif action.type == 'move' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if src_adapter == M and dest_adapter == M then
@ -311,7 +311,7 @@ M.perform_action = function(action, cb)
local src_conn = get_connection(action.src_url)
local dest_conn = get_connection(action.dest_url)
if src_conn ~= dest_conn then
scp({ "-r", url_to_scp(src_res), url_to_scp(dest_res) }, function(err)
scp({ '-r', url_to_scp(src_res), url_to_scp(dest_res) }, function(err)
if err then
return cb(err)
end
@ -321,16 +321,16 @@ M.perform_action = function(action, cb)
src_conn:mv(src_res.path, dest_res.path, cb)
end
else
cb("We should never attempt to move across adapters")
cb('We should never attempt to move across adapters')
end
elseif action.type == "copy" then
elseif action.type == 'copy' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if src_adapter == M and dest_adapter == M then
local src_res = M.parse_url(action.src_url)
local dest_res = M.parse_url(action.dest_url)
if not url_hosts_equal(src_res, dest_res) then
scp({ "-r", url_to_scp(src_res), url_to_scp(dest_res) }, cb)
scp({ '-r', url_to_scp(src_res), url_to_scp(dest_res) }, cb)
else
local src_conn = get_connection(action.src_url)
src_conn:cp(src_res.path, dest_res.path, cb)
@ -349,14 +349,14 @@ M.perform_action = function(action, cb)
src_arg = fs.posix_to_os_path(path)
dest_arg = url_to_scp(M.parse_url(action.dest_url))
end
scp({ "-r", src_arg, dest_arg }, cb)
scp({ '-r', src_arg, dest_arg }, cb)
end
else
cb(string.format("Bad action type: %s", action.type))
cb(string.format('Bad action type: %s', action.type))
end
end
M.supported_cross_adapter_actions = { files = "copy" }
M.supported_cross_adapter_actions = { files = 'copy' }
---@param bufnr integer
M.read_file = function(bufnr)
@ -365,11 +365,11 @@ M.read_file = function(bufnr)
local url = M.parse_url(bufname)
local scp_url = url_to_scp(url)
local basename = pathutil.basename(bufname)
local cache_dir = vim.fn.stdpath("cache")
assert(type(cache_dir) == "string")
local tmpdir = fs.join(cache_dir, "oil")
local cache_dir = vim.fn.stdpath('cache')
assert(type(cache_dir) == 'string')
local tmpdir = fs.join(cache_dir, 'oil')
fs.mkdirp(tmpdir)
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, "ssh_XXXXXX"))
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, 'ssh_XXXXXX'))
if fd then
vim.loop.fs_close(fd)
end
@ -378,9 +378,9 @@ M.read_file = function(bufnr)
scp({ scp_url, tmpfile }, function(err)
loading.set_loading(bufnr, false)
vim.bo[bufnr].modifiable = true
vim.cmd.doautocmd({ args = { "BufReadPre", bufname }, mods = { silent = true } })
vim.cmd.doautocmd({ args = { 'BufReadPre', bufname }, mods = { silent = true } })
if err then
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, vim.split(err, "\n"))
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, vim.split(err, '\n'))
else
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {})
vim.api.nvim_buf_call(bufnr, function()
@ -394,9 +394,9 @@ M.read_file = function(bufnr)
if filetype then
vim.bo[bufnr].filetype = filetype
end
vim.cmd.doautocmd({ args = { "BufReadPost", bufname }, mods = { silent = true } })
vim.cmd.doautocmd({ args = { 'BufReadPost', bufname }, mods = { silent = true } })
vim.api.nvim_buf_delete(tmp_bufnr, { force = true })
vim.keymap.set("n", "gf", M.goto_file, { buffer = bufnr })
vim.keymap.set('n', 'gf', M.goto_file, { buffer = bufnr })
end)
end
@ -405,14 +405,14 @@ M.write_file = function(bufnr)
local bufname = vim.api.nvim_buf_get_name(bufnr)
local url = M.parse_url(bufname)
local scp_url = url_to_scp(url)
local cache_dir = vim.fn.stdpath("cache")
assert(type(cache_dir) == "string")
local tmpdir = fs.join(cache_dir, "oil")
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, "ssh_XXXXXXXX"))
local cache_dir = vim.fn.stdpath('cache')
assert(type(cache_dir) == 'string')
local tmpdir = fs.join(cache_dir, 'oil')
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, 'ssh_XXXXXXXX'))
if fd then
vim.loop.fs_close(fd)
end
vim.cmd.doautocmd({ args = { "BufWritePre", bufname }, mods = { silent = true } })
vim.cmd.doautocmd({ args = { 'BufWritePre', bufname }, mods = { silent = true } })
vim.bo[bufnr].modifiable = false
vim.cmd.write({ args = { tmpfile }, bang = true, mods = { silent = true, noautocmd = true } })
local tmp_bufnr = vim.fn.bufadd(tmpfile)
@ -420,10 +420,10 @@ M.write_file = function(bufnr)
scp({ tmpfile, scp_url }, function(err)
vim.bo[bufnr].modifiable = true
if err then
vim.notify(string.format("Error writing file: %s", err), vim.log.levels.ERROR)
vim.notify(string.format('Error writing file: %s', err), vim.log.levels.ERROR)
else
vim.bo[bufnr].modified = false
vim.cmd.doautocmd({ args = { "BufWritePost", bufname }, mods = { silent = true } })
vim.cmd.doautocmd({ args = { 'BufWritePost', bufname }, mods = { silent = true } })
end
vim.loop.fs_unlink(tmpfile)
vim.api.nvim_buf_delete(tmp_bufnr, { force = true })
@ -432,7 +432,7 @@ end
M.goto_file = function()
local url = M.parse_url(vim.api.nvim_buf_get_name(0))
local fname = vim.fn.expand("<cfile>")
local fname = vim.fn.expand('<cfile>')
local fullpath = fname
if not fs.is_absolute(fname) then
local pardir = vim.fs.dirname(url.path)
@ -459,7 +459,7 @@ M.goto_file = function()
vim.cmd.edit({ args = { url_to_str(url) } })
return
end
for suffix in vim.gsplit(vim.o.suffixesadd, ",", { plain = true, trimempty = true }) do
for suffix in vim.gsplit(vim.o.suffixesadd, ',', { plain = true, trimempty = true }) do
local suffixname = basename .. suffix
if name_map[suffixname] then
url.path = fullpath .. suffix

View file

@ -1,6 +1,6 @@
local config = require("oil.config")
local layout = require("oil.layout")
local util = require("oil.util")
local config = require('oil.config')
local layout = require('oil.layout')
local util = require('oil.util')
---@class (exact) oil.sshCommand
---@field cmd string|string[]
@ -24,12 +24,12 @@ local function output_extend(agg, output)
local start = #agg
if vim.tbl_isempty(agg) then
for _, line in ipairs(output) do
line = line:gsub("\r", "")
line = line:gsub('\r', '')
table.insert(agg, line)
end
else
for i, v in ipairs(output) do
v = v:gsub("\r", "")
v = v:gsub('\r', '')
if i == 1 then
agg[#agg] = agg[#agg] .. v
else
@ -53,7 +53,7 @@ local function get_last_lines(bufnr, num_lines)
vim.api.nvim_buf_get_lines(bufnr, end_line - need_lines, end_line, false),
lines
)
while not vim.tbl_isempty(lines) and lines[#lines]:match("^%s*$") do
while not vim.tbl_isempty(lines) and lines[#lines]:match('^%s*$') do
table.remove(lines)
end
end_line = end_line - need_lines
@ -66,14 +66,14 @@ end
function SSHConnection.create_ssh_command(url)
local host = url.host
if url.user then
host = url.user .. "@" .. host
host = url.user .. '@' .. host
end
local command = {
"ssh",
'ssh',
host,
}
if url.port then
table.insert(command, "-p")
table.insert(command, '-p')
table.insert(command, url.port)
end
return command
@ -84,8 +84,8 @@ end
function SSHConnection.new(url)
local command = SSHConnection.create_ssh_command(url)
vim.list_extend(command, {
"/bin/sh",
"-c",
'/bin/sh',
'-c',
-- HACK: For some reason in my testing if I just have "echo READY" it doesn't appear, but if I echo
-- anything prior to that, it *will* appear. The first line gets swallowed.
"echo '_make_newline_'; echo '===READY==='; exec /bin/sh",
@ -112,7 +112,7 @@ function SSHConnection.new(url)
})
end)
self.term_id = term_id
vim.api.nvim_chan_send(term_id, string.format("ssh %s\r\n", url.host))
vim.api.nvim_chan_send(term_id, string.format('ssh %s\r\n', url.host))
util.hack_around_termopen_autocmd(mode)
-- If it takes more than 2 seconds to connect, pop open the terminal
@ -125,7 +125,7 @@ function SSHConnection.new(url)
local jid = vim.fn.jobstart(command, {
pty = true, -- This is require for interactivity
on_stdout = function(j, output)
pcall(vim.api.nvim_chan_send, self.term_id, table.concat(output, "\r\n"))
pcall(vim.api.nvim_chan_send, self.term_id, table.concat(output, '\r\n'))
---@diagnostic disable-next-line: invisible
local new_i_start = output_extend(self._stdout, output)
self:_handle_output(new_i_start)
@ -134,12 +134,12 @@ function SSHConnection.new(url)
pcall(
vim.api.nvim_chan_send,
self.term_id,
string.format("\r\n[Process exited %d]\r\n", code)
string.format('\r\n[Process exited %d]\r\n', code)
)
-- Defer to allow the deferred terminal output handling to kick in first
vim.defer_fn(function()
if code == 0 then
self:_set_connection_error("SSH connection terminated gracefully")
self:_set_connection_error('SSH connection terminated gracefully')
else
self:_set_connection_error(
'Unknown SSH error\nTo see more, run :lua require("oil.adapters.ssh").open_terminal()'
@ -156,23 +156,23 @@ function SSHConnection.new(url)
else
self.jid = jid
end
self:run("id -u", function(err, lines)
self:run('id -u', function(err, lines)
if err then
vim.notify(string.format("Error fetching ssh connection user: %s", err), vim.log.levels.WARN)
vim.notify(string.format('Error fetching ssh connection user: %s', err), vim.log.levels.WARN)
else
assert(lines)
self.meta.user = vim.trim(table.concat(lines, ""))
self.meta.user = vim.trim(table.concat(lines, ''))
end
end)
self:run("id -G", function(err, lines)
self:run('id -G', function(err, lines)
if err then
vim.notify(
string.format("Error fetching ssh connection user groups: %s", err),
string.format('Error fetching ssh connection user groups: %s', err),
vim.log.levels.WARN
)
else
assert(lines)
self.meta.groups = vim.split(table.concat(lines, ""), "%s+", { trimempty = true })
self.meta.groups = vim.split(table.concat(lines, ''), '%s+', { trimempty = true })
end
end)
@ -197,7 +197,7 @@ function SSHConnection:_handle_output(start_i)
if not self.connected then
for i = start_i, #self._stdout - 1 do
local line = self._stdout[i]
if line == "===READY===" then
if line == '===READY===' then
if self.term_winid then
if vim.api.nvim_win_is_valid(self.term_winid) then
vim.api.nvim_win_close(self.term_winid, true)
@ -215,7 +215,7 @@ function SSHConnection:_handle_output(start_i)
for i = start_i, #self._stdout - 1 do
---@type string
local line = self._stdout[i]
if line:match("^===BEGIN===%s*$") then
if line:match('^===BEGIN===%s*$') then
self._stdout = util.tbl_slice(self._stdout, i + 1)
self:_handle_output(1)
return
@ -223,15 +223,15 @@ function SSHConnection:_handle_output(start_i)
-- We can't be as strict with the matching (^$) because since we're using a pty the stdout and
-- stderr can be interleaved. If the command had an error, the stderr may interfere with a
-- clean print of the done line.
local exit_code = line:match("===DONE%((%d+)%)===")
local exit_code = line:match('===DONE%((%d+)%)===')
if exit_code then
local output = util.tbl_slice(self._stdout, 1, i - 1)
local cb = self.commands[1].cb
self._stdout = util.tbl_slice(self._stdout, i + 1)
if exit_code == "0" then
if exit_code == '0' then
cb(nil, output)
else
cb(exit_code .. ": " .. table.concat(output, "\n"), output)
cb(exit_code .. ': ' .. table.concat(output, '\n'), output)
end
table.remove(self.commands, 1)
self:_handle_output(1)
@ -244,16 +244,17 @@ function SSHConnection:_handle_output(start_i)
local function check_last_line()
local last_lines = get_last_lines(self.term_bufnr, 1)
local last_line = last_lines[1]
if last_line:match("^Are you sure you want to continue connecting") then
if last_line:match('^Are you sure you want to continue connecting') then
self:open_terminal()
elseif last_line:match("Password:%s*$") then
-- selene: allow(if_same_then_else)
elseif last_line:match('Password:%s*$') then
self:open_terminal()
elseif last_line:match(": Permission denied %(.+%)%.") then
self:_set_connection_error(last_line:match(": (Permission denied %(.+%).)"))
elseif last_line:match("^ssh: .*Connection refused%s*$") then
self:_set_connection_error("Connection refused")
elseif last_line:match("^Connection to .+ closed by remote host.%s*$") then
self:_set_connection_error("Connection closed by remote host")
elseif last_line:match(': Permission denied %(.+%)%.') then
self:_set_connection_error(last_line:match(': (Permission denied %(.+%).)'))
elseif last_line:match('^ssh: .*Connection refused%s*$') then
self:_set_connection_error('Connection refused')
elseif last_line:match('^Connection to .+ closed by remote host.%s*$') then
self:_set_connection_error('Connection closed by remote host')
end
end
-- We have to defer this so the terminal buffer has time to update
@ -273,12 +274,12 @@ function SSHConnection:open_terminal()
local row = math.floor((total_height - height) / 2)
local col = math.floor((vim.o.columns - width) / 2)
self.term_winid = vim.api.nvim_open_win(self.term_bufnr, true, {
relative = "editor",
relative = 'editor',
width = width,
height = height,
row = row,
col = col,
style = "minimal",
style = 'minimal',
border = config.ssh.border,
})
vim.cmd.startinsert()

View file

@ -1,8 +1,8 @@
local SSHConnection = require("oil.adapters.ssh.connection")
local cache = require("oil.cache")
local constants = require("oil.constants")
local permissions = require("oil.adapters.files.permissions")
local util = require("oil.util")
local SSHConnection = require('oil.adapters.ssh.connection')
local cache = require('oil.cache')
local constants = require('oil.constants')
local permissions = require('oil.adapters.files.permissions')
local util = require('oil.util')
---@class (exact) oil.sshFs
---@field new fun(url: oil.sshUrl): oil.sshFs
@ -13,13 +13,13 @@ local FIELD_TYPE = constants.FIELD_TYPE
local FIELD_META = constants.FIELD_META
local typechar_map = {
l = "link",
d = "directory",
p = "fifo",
s = "socket",
["-"] = "file",
c = "file", -- character special file
b = "file", -- block special file
l = 'link',
d = 'directory',
p = 'fifo',
s = 'socket',
['-'] = 'file',
c = 'file', -- character special file
b = 'file', -- block special file
}
---@param line string
---@return string Name of entry
@ -27,11 +27,11 @@ local typechar_map = {
---@return table Metadata for entry
local function parse_ls_line(line)
local typechar, perms, refcount, user, group, rem =
line:match("^(.)(%S+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(.*)$")
line:match('^(.)(%S+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(.*)$')
if not typechar then
error(string.format("Could not parse '%s'", line))
end
local type = typechar_map[typechar] or "file"
local type = typechar_map[typechar] or 'file'
local meta = {
user = user,
@ -40,26 +40,26 @@ local function parse_ls_line(line)
refcount = tonumber(refcount),
}
local name, size, date, major, minor
if typechar == "c" or typechar == "b" then
major, minor, date, name = rem:match("^(%d+)%s*,%s*(%d+)%s+(%S+%s+%d+%s+%d%d:?%d%d)%s+(.*)")
if typechar == 'c' or typechar == 'b' then
major, minor, date, name = rem:match('^(%d+)%s*,%s*(%d+)%s+(%S+%s+%d+%s+%d%d:?%d%d)%s+(.*)')
if name == nil then
major, minor, date, name =
rem:match("^(%d+)%s*,%s*(%d+)%s+(%d+%-%d+%-%d+%s+%d%d:?%d%d)%s+(.*)")
rem:match('^(%d+)%s*,%s*(%d+)%s+(%d+%-%d+%-%d+%s+%d%d:?%d%d)%s+(.*)')
end
meta.major = tonumber(major)
meta.minor = tonumber(minor)
else
size, date, name = rem:match("^(%d+)%s+(%S+%s+%d+%s+%d%d:?%d%d)%s+(.*)")
size, date, name = rem:match('^(%d+)%s+(%S+%s+%d+%s+%d%d:?%d%d)%s+(.*)')
if name == nil then
size, date, name = rem:match("^(%d+)%s+(%d+%-%d+%-%d+%s+%d%d:?%d%d)%s+(.*)")
size, date, name = rem:match('^(%d+)%s+(%d+%-%d+%-%d+%s+%d%d:?%d%d)%s+(.*)')
end
meta.size = tonumber(size)
end
meta.iso_modified_date = date
if type == "link" then
if type == 'link' then
local link
name, link = unpack(vim.split(name, " -> ", { plain = true }))
if vim.endswith(link, "/") then
name, link = unpack(vim.split(name, ' -> ', { plain = true }))
if vim.endswith(link, '/') then
link = link:sub(1, #link - 1)
end
meta.link = link
@ -94,7 +94,7 @@ end
---@param callback fun(err: nil|string)
function SSHFS:chmod(value, path, callback)
local octal = permissions.mode_to_octal_str(value)
self.conn:run(string.format("chmod %s %s", octal, shellescape(path)), callback)
self.conn:run(string.format('chmod %s %s', octal, shellescape(path)), callback)
end
function SSHFS:open_terminal()
@ -114,24 +114,24 @@ function SSHFS:realpath(path, callback)
return callback(err)
end
assert(lines)
local abspath = table.concat(lines, "")
local abspath = table.concat(lines, '')
-- If the path was "." then the abspath might be /path/to/., so we need to trim that final '.'
if vim.endswith(abspath, ".") then
if vim.endswith(abspath, '.') then
abspath = abspath:sub(1, #abspath - 1)
end
self.conn:run(
string.format("LC_ALL=C ls -land --color=never %s", shellescape(abspath)),
string.format('LC_ALL=C ls -land --color=never %s', shellescape(abspath)),
function(ls_err, ls_lines)
local type
if ls_err then
-- If the file doesn't exist, treat it like a not-yet-existing directory
type = "directory"
type = 'directory'
else
assert(ls_lines)
local _
_, type = parse_ls_line(ls_lines[1])
end
if type == "directory" then
if type == 'directory' then
abspath = util.addslash(abspath)
end
callback(nil, abspath)
@ -146,13 +146,13 @@ local dir_meta = {}
---@param path string
---@param callback fun(err?: string, entries?: oil.InternalEntry[], fetch_more?: fun())
function SSHFS:list_dir(url, path, callback)
local path_postfix = ""
if path ~= "" then
path_postfix = string.format(" %s", shellescape(path))
local path_postfix = ''
if path ~= '' then
path_postfix = string.format(' %s', shellescape(path))
end
self.conn:run("LC_ALL=C ls -lan --color=never" .. path_postfix, function(err, lines)
self.conn:run('LC_ALL=C ls -lan --color=never' .. path_postfix, function(err, lines)
if err then
if err:match("No such file or directory%s*$") then
if err:match('No such file or directory%s*$') then
-- If the directory doesn't exist, treat the list as a success. We will be able to traverse
-- and edit a not-yet-existing directory.
return callback()
@ -165,12 +165,12 @@ function SSHFS:list_dir(url, path, callback)
local entries = {}
local cache_entries = {}
for _, line in ipairs(lines) do
if line ~= "" and not line:match("^total") then
if line ~= '' and not line:match('^total') then
local name, type, meta = parse_ls_line(line)
if name == "." then
if name == '.' then
dir_meta[url] = meta
elseif name ~= ".." then
if type == "link" then
elseif name ~= '..' then
if type == 'link' then
any_links = true
end
local cache_entry = cache.create_entry(url, name, type)
@ -184,19 +184,19 @@ function SSHFS:list_dir(url, path, callback)
-- If there were any soft links, then we need to run another ls command with -L so that we can
-- resolve the type of the link target
self.conn:run(
"LC_ALL=C ls -naLl --color=never" .. path_postfix .. " 2> /dev/null",
'LC_ALL=C ls -naLl --color=never' .. path_postfix .. ' 2> /dev/null',
function(link_err, link_lines)
-- Ignore exit code 1. That just means one of the links could not be resolved.
if link_err and not link_err:match("^1:") then
if link_err and not link_err:match('^1:') then
return callback(link_err)
end
assert(link_lines)
for _, line in ipairs(link_lines) do
if line ~= "" and not line:match("^total") then
if line ~= '' and not line:match('^total') then
local ok, name, type, meta = pcall(parse_ls_line, line)
if ok and name ~= "." and name ~= ".." then
if ok and name ~= '.' and name ~= '..' then
local cache_entry = entries[name]
if cache_entry[FIELD_TYPE] == "link" then
if cache_entry[FIELD_TYPE] == 'link' then
cache_entry[FIELD_META].link_stat = {
type = type,
size = meta.size,
@ -217,40 +217,40 @@ end
---@param path string
---@param callback fun(err: nil|string)
function SSHFS:mkdir(path, callback)
self.conn:run(string.format("mkdir -p %s", shellescape(path)), callback)
self.conn:run(string.format('mkdir -p %s', shellescape(path)), callback)
end
---@param path string
---@param callback fun(err: nil|string)
function SSHFS:touch(path, callback)
self.conn:run(string.format("touch %s", shellescape(path)), callback)
self.conn:run(string.format('touch %s', shellescape(path)), callback)
end
---@param path string
---@param link string
---@param callback fun(err: nil|string)
function SSHFS:mklink(path, link, callback)
self.conn:run(string.format("ln -s %s %s", shellescape(link), shellescape(path)), callback)
self.conn:run(string.format('ln -s %s %s', shellescape(link), shellescape(path)), callback)
end
---@param path string
---@param callback fun(err: nil|string)
function SSHFS:rm(path, callback)
self.conn:run(string.format("rm -rf %s", shellescape(path)), callback)
self.conn:run(string.format('rm -rf %s', shellescape(path)), callback)
end
---@param src string
---@param dest string
---@param callback fun(err: nil|string)
function SSHFS:mv(src, dest, callback)
self.conn:run(string.format("mv %s %s", shellescape(src), shellescape(dest)), callback)
self.conn:run(string.format('mv %s %s', shellescape(src), shellescape(dest)), callback)
end
---@param src string
---@param dest string
---@param callback fun(err: nil|string)
function SSHFS:cp(src, dest, callback)
self.conn:run(string.format("cp -r %s %s", shellescape(src), shellescape(dest)), callback)
self.conn:run(string.format('cp -r %s %s', shellescape(src), shellescape(dest)), callback)
end
function SSHFS:get_dir_meta(url)

View file

@ -1,5 +1,5 @@
local cache = require("oil.cache")
local util = require("oil.util")
local cache = require('oil.cache')
local util = require('oil.util')
local M = {}
---@param url string
@ -32,24 +32,24 @@ end
---@param entry_type oil.EntryType
---@return oil.InternalEntry
M.test_set = function(path, entry_type)
if path == "/" then
if path == '/' then
return {}
end
local parent = vim.fn.fnamemodify(path, ":h")
local parent = vim.fn.fnamemodify(path, ':h')
if parent ~= path then
M.test_set(parent, "directory")
M.test_set(parent, 'directory')
end
parent = util.addslash(parent)
if not dir_listing[parent] then
dir_listing[parent] = {}
end
local name = vim.fn.fnamemodify(path, ":t")
local name = vim.fn.fnamemodify(path, ':t')
local entry = {
name = name,
entry_type = entry_type,
}
table.insert(dir_listing[parent], entry)
local parent_url = "oil-test://" .. parent
local parent_url = 'oil-test://' .. parent
return cache.create_and_store_entry(parent_url, entry.name, entry.entry_type)
end
@ -68,12 +68,12 @@ end
---@param action oil.Action
---@return string
M.render_action = function(action)
if action.type == "create" or action.type == "delete" then
return string.format("%s %s", action.type:upper(), action.url)
elseif action.type == "move" or action.type == "copy" then
return string.format(" %s %s -> %s", action.type:upper(), action.src_url, action.dest_url)
if action.type == 'create' or action.type == 'delete' then
return string.format('%s %s', action.type:upper(), action.url)
elseif action.type == 'move' or action.type == 'copy' then
return string.format(' %s %s -> %s', action.type:upper(), action.src_url, action.dest_url)
else
error("Bad action type")
error('Bad action type')
end
end

View file

@ -1,9 +1,9 @@
local fs = require("oil.fs")
local fs = require('oil.fs')
if fs.is_mac then
return require("oil.adapters.trash.mac")
return require('oil.adapters.trash.mac')
elseif fs.is_windows then
return require("oil.adapters.trash.windows")
return require('oil.adapters.trash.windows')
else
return require("oil.adapters.trash.freedesktop")
return require('oil.adapters.trash.freedesktop')
end

View file

@ -1,11 +1,11 @@
-- Based on the FreeDesktop.org trash specification
-- https://specifications.freedesktop.org/trash/1.0/
local cache = require("oil.cache")
local config = require("oil.config")
local constants = require("oil.constants")
local files = require("oil.adapters.files")
local fs = require("oil.fs")
local util = require("oil.util")
local cache = require('oil.cache')
local config = require('oil.config')
local constants = require('oil.constants')
local files = require('oil.adapters.files')
local fs = require('oil.fs')
local util = require('oil.util')
local uv = vim.uv or vim.loop
local FIELD_META = constants.FIELD_META
@ -14,8 +14,8 @@ local M = {}
local function ensure_trash_dir(path)
local mode = 448 -- 0700
fs.mkdirp(fs.join(path, "info"), mode)
fs.mkdirp(fs.join(path, "files"), mode)
fs.mkdirp(fs.join(path, 'info'), mode)
fs.mkdirp(fs.join(path, 'files'), mode)
end
---Gets the location of the home trash dir, creating it if necessary
@ -23,9 +23,9 @@ end
local function get_home_trash_dir()
local xdg_home = vim.env.XDG_DATA_HOME
if not xdg_home then
xdg_home = fs.join(assert(uv.os_homedir()), ".local", "share")
xdg_home = fs.join(assert(uv.os_homedir()), '.local', 'share')
end
local trash_dir = fs.join(xdg_home, "Trash")
local trash_dir = fs.join(xdg_home, 'Trash')
ensure_trash_dir(trash_dir)
return trash_dir
end
@ -43,13 +43,13 @@ end
local function get_top_trash_dirs(path)
local dirs = {}
local dev = (uv.fs_lstat(path) or {}).dev
local top_trash_dirs = vim.fs.find(".Trash", { upward = true, path = path, limit = math.huge })
local top_trash_dirs = vim.fs.find('.Trash', { upward = true, path = path, limit = math.huge })
for _, top_trash_dir in ipairs(top_trash_dirs) do
local stat = uv.fs_lstat(top_trash_dir)
if stat and not dev then
dev = stat.dev
end
if stat and stat.dev == dev and stat.type == "directory" and is_sticky(stat.mode) then
if stat and stat.dev == dev and stat.type == 'directory' and is_sticky(stat.mode) then
local trash_dir = fs.join(top_trash_dir, tostring(uv.getuid()))
ensure_trash_dir(trash_dir)
table.insert(dirs, trash_dir)
@ -58,7 +58,7 @@ local function get_top_trash_dirs(path)
-- Also search for the .Trash-$uid
top_trash_dirs = vim.fs.find(
string.format(".Trash-%d", uv.getuid()),
string.format('.Trash-%d', uv.getuid()),
{ upward = true, path = path, limit = math.huge }
)
for _, top_trash_dir in ipairs(top_trash_dirs) do
@ -91,14 +91,14 @@ local function get_write_trash_dir(path)
return top_trash_dirs[1]
end
local parent = vim.fn.fnamemodify(path, ":h")
local next_parent = vim.fn.fnamemodify(parent, ":h")
local parent = vim.fn.fnamemodify(path, ':h')
local next_parent = vim.fn.fnamemodify(parent, ':h')
while parent ~= next_parent and uv.fs_lstat(next_parent).dev == dev do
parent = next_parent
next_parent = vim.fn.fnamemodify(parent, ":h")
next_parent = vim.fn.fnamemodify(parent, ':h')
end
local top_trash = fs.join(parent, string.format(".Trash-%d", uv.getuid()))
local top_trash = fs.join(parent, string.format('.Trash-%d', uv.getuid()))
ensure_trash_dir(top_trash)
return top_trash
end
@ -116,7 +116,7 @@ end
M.normalize_url = function(url, callback)
local scheme, path = util.parse_url(url)
assert(path)
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(path), ":p")
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(path), ':p')
uv.fs_realpath(
os_path,
vim.schedule_wrap(function(err, new_os_path)
@ -140,10 +140,10 @@ M.get_entry_path = function(url, entry, cb)
return
end
local path = fs.os_to_posix_path(trash_info.trash_file)
if meta.stat.type == "directory" then
if meta.stat.type == 'directory' then
path = util.addslash(path)
end
cb("oil://" .. path)
cb('oil://' .. path)
end
---@class oil.TrashInfo
@ -156,10 +156,10 @@ end
---@param info_file string
---@param cb fun(err?: string, info?: oil.TrashInfo)
local function read_trash_info(info_file, cb)
if not vim.endswith(info_file, ".trashinfo") then
return cb("File is not .trashinfo")
if not vim.endswith(info_file, '.trashinfo') then
return cb('File is not .trashinfo')
end
uv.fs_open(info_file, "r", 448, function(err, fd)
uv.fs_open(info_file, 'r', 448, function(err, fd)
if err then
return cb(err)
end
@ -182,32 +182,32 @@ local function read_trash_info(info_file, cb)
local trash_info = {
info_file = info_file,
}
local lines = vim.split(content, "\r?\n")
if lines[1] ~= "[Trash Info]" then
return cb("File missing [Trash Info] header")
local lines = vim.split(content, '\r?\n')
if lines[1] ~= '[Trash Info]' then
return cb('File missing [Trash Info] header')
end
local trash_base = vim.fn.fnamemodify(info_file, ":h:h")
local trash_base = vim.fn.fnamemodify(info_file, ':h:h')
for _, line in ipairs(lines) do
local key, value = unpack(vim.split(line, "=", { plain = true, trimempty = true }))
if key == "Path" and not trash_info.original_path then
if not vim.startswith(value, "/") then
local key, value = unpack(vim.split(line, '=', { plain = true, trimempty = true }))
if key == 'Path' and not trash_info.original_path then
if not vim.startswith(value, '/') then
value = fs.join(trash_base, value)
end
trash_info.original_path = value
elseif key == "DeletionDate" and not trash_info.deletion_date then
trash_info.deletion_date = vim.fn.strptime("%Y-%m-%dT%H:%M:%S", value)
elseif key == 'DeletionDate' and not trash_info.deletion_date then
trash_info.deletion_date = vim.fn.strptime('%Y-%m-%dT%H:%M:%S', value)
end
end
if not trash_info.original_path or not trash_info.deletion_date then
return cb("File missing required fields")
return cb('File missing required fields')
end
local basename = vim.fn.fnamemodify(info_file, ":t:r")
trash_info.trash_file = fs.join(trash_base, "files", basename)
local basename = vim.fn.fnamemodify(info_file, ':t:r')
trash_info.trash_file = fs.join(trash_base, 'files', basename)
uv.fs_lstat(trash_info.trash_file, function(trash_stat_err, trash_stat)
if trash_stat_err then
cb(".trashinfo file points to non-existant file")
cb('.trashinfo file points to non-existant file')
else
trash_info.stat = trash_stat
---@cast trash_info oil.TrashInfo
@ -244,14 +244,14 @@ M.list = function(url, column_defs, cb)
-- The first trash dir is a special case; it is in the home directory and we should only show
-- all entries if we are in the top root path "/"
if trash_idx == 1 then
show_all_files = path == "/"
show_all_files = path == '/'
end
local info_dir = fs.join(trash_dir, "info")
local info_dir = fs.join(trash_dir, 'info')
---@diagnostic disable-next-line: param-type-mismatch, discard-returns
uv.fs_opendir(info_dir, function(open_err, fd)
if open_err then
if open_err:match("^ENOENT: no such file or directory") then
if open_err:match('^ENOENT: no such file or directory') then
-- If the directory doesn't exist, treat the list as a success. We will be able to traverse
-- and edit a not-yet-existing directory.
return read_next_trash_dir()
@ -286,12 +286,12 @@ M.list = function(url, column_defs, cb)
-- files.
poll()
else
local parent = util.addslash(vim.fn.fnamemodify(info.original_path, ":h"))
local parent = util.addslash(vim.fn.fnamemodify(info.original_path, ':h'))
if path == parent or show_all_files then
local name = vim.fn.fnamemodify(info.trash_file, ":t")
local name = vim.fn.fnamemodify(info.trash_file, ':t')
---@diagnostic disable-next-line: undefined-field
local cache_entry = cache.create_entry(url, name, info.stat.type)
local display_name = vim.fn.fnamemodify(info.original_path, ":t")
local display_name = vim.fn.fnamemodify(info.original_path, ':t')
cache_entry[FIELD_META] = {
stat = info.stat,
trash_info = info,
@ -302,12 +302,12 @@ M.list = function(url, column_defs, cb)
if path ~= parent and (show_all_files or fs.is_subpath(path, parent)) then
local name = parent:sub(path:len() + 1)
local next_par = vim.fs.dirname(name)
while next_par ~= "." do
while next_par ~= '.' do
name = next_par
next_par = vim.fs.dirname(name)
end
---@diagnostic disable-next-line: undefined-field
local cache_entry = cache.create_entry(url, name, "directory")
local cache_entry = cache.create_entry(url, name, 'directory')
cache_entry[FIELD_META] = {
stat = info.stat,
@ -348,7 +348,7 @@ local file_columns = {}
local current_year
-- Make sure we run this import-time effect in the main loop (mostly for tests)
vim.schedule(function()
current_year = vim.fn.strftime("%Y")
current_year = vim.fn.strftime('%Y')
end)
file_columns.mtime = {
@ -368,11 +368,11 @@ file_columns.mtime = {
if fmt then
ret = vim.fn.strftime(fmt, time)
else
local year = vim.fn.strftime("%Y", time)
local year = vim.fn.strftime('%Y', time)
if year ~= current_year then
ret = vim.fn.strftime("%b %d %Y", time)
ret = vim.fn.strftime('%b %d %Y', time)
else
ret = vim.fn.strftime("%b %d %H:%M", time)
ret = vim.fn.strftime('%b %d %H:%M', time)
end
end
return ret
@ -393,11 +393,11 @@ file_columns.mtime = {
local fmt = conf and conf.format
local pattern
if fmt then
pattern = fmt:gsub("%%.", "%%S+")
pattern = fmt:gsub('%%.', '%%S+')
else
pattern = "%S+%s+%d+%s+%d%d:?%d%d"
pattern = '%S+%s+%d+%s+%d%d:?%d%d'
end
return line:match("^(" .. pattern .. ")%s+(.+)$")
return line:match('^(' .. pattern .. ')%s+(.+)$')
end,
}
@ -407,25 +407,26 @@ M.get_column = function(name)
return file_columns[name]
end
M.supported_cross_adapter_actions = { files = "move" }
M.supported_cross_adapter_actions = { files = 'move' }
---@param action oil.Action
---@return boolean
M.filter_action = function(action)
if action.type == "create" then
if action.type == 'create' then
return false
elseif action.type == "delete" then
elseif action.type == 'delete' then
local entry = assert(cache.get_entry_by_url(action.url))
local meta = entry[FIELD_META]
return meta ~= nil and meta.trash_info ~= nil
elseif action.type == "move" then
elseif action.type == 'move' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
return src_adapter.name == "files" or dest_adapter.name == "files"
elseif action.type == "copy" then
return src_adapter.name == 'files' or dest_adapter.name == 'files'
-- selene: allow(if_same_then_else)
elseif action.type == 'copy' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
return src_adapter.name == "files" or dest_adapter.name == "files"
return src_adapter.name == 'files' or dest_adapter.name == 'files'
else
error(string.format("Bad action type '%s'", action.type))
end
@ -434,7 +435,7 @@ end
---@param err oil.ParseError
---@return boolean
M.filter_error = function(err)
if err.message == "Duplicate filename" then
if err.message == 'Duplicate filename' then
return false
end
return true
@ -443,44 +444,44 @@ end
---@param action oil.Action
---@return string
M.render_action = function(action)
if action.type == "delete" then
if action.type == 'delete' then
local entry = assert(cache.get_entry_by_url(action.url))
local meta = entry[FIELD_META]
---@type oil.TrashInfo
local trash_info = assert(meta).trash_info
local short_path = fs.shorten_path(trash_info.original_path)
return string.format(" PURGE %s", short_path)
elseif action.type == "move" then
return string.format(' PURGE %s', short_path)
elseif action.type == 'move' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if src_adapter.name == "files" then
if src_adapter.name == 'files' then
local _, path = util.parse_url(action.src_url)
assert(path)
local short_path = files.to_short_os_path(path, action.entry_type)
return string.format(" TRASH %s", short_path)
elseif dest_adapter.name == "files" then
return string.format(' TRASH %s', short_path)
elseif dest_adapter.name == 'files' then
local _, path = util.parse_url(action.dest_url)
assert(path)
local short_path = files.to_short_os_path(path, action.entry_type)
return string.format("RESTORE %s", short_path)
return string.format('RESTORE %s', short_path)
else
error("Must be moving files into or out of trash")
error('Must be moving files into or out of trash')
end
elseif action.type == "copy" then
elseif action.type == 'copy' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if src_adapter.name == "files" then
if src_adapter.name == 'files' then
local _, path = util.parse_url(action.src_url)
assert(path)
local short_path = files.to_short_os_path(path, action.entry_type)
return string.format(" COPY %s -> TRASH", short_path)
elseif dest_adapter.name == "files" then
return string.format(' COPY %s -> TRASH', short_path)
elseif dest_adapter.name == 'files' then
local _, path = util.parse_url(action.dest_url)
assert(path)
local short_path = files.to_short_os_path(path, action.entry_type)
return string.format("RESTORE %s", short_path)
return string.format('RESTORE %s', short_path)
else
error("Must be copying files into or out of trash")
error('Must be copying files into or out of trash')
end
else
error(string.format("Bad action type '%s'", action.type))
@ -490,7 +491,7 @@ end
---@param trash_info oil.TrashInfo
---@param cb fun(err?: string)
local function purge(trash_info, cb)
fs.recursive_delete("file", trash_info.info_file, function(err)
fs.recursive_delete('file', trash_info.info_file, function(err)
if err then
return cb(err)
end
@ -505,15 +506,15 @@ end
local function write_info_file(path, info_path, cb)
uv.fs_open(
info_path,
"w",
'w',
448,
vim.schedule_wrap(function(err, fd)
if err then
return cb(err)
end
assert(fd)
local deletion_date = vim.fn.strftime("%Y-%m-%dT%H:%M:%S")
local contents = string.format("[Trash Info]\nPath=%s\nDeletionDate=%s", path, deletion_date)
local deletion_date = vim.fn.strftime('%Y-%m-%dT%H:%M:%S')
local contents = string.format('[Trash Info]\nPath=%s\nDeletionDate=%s', path, deletion_date)
uv.fs_write(fd, contents, function(write_err)
uv.fs_close(fd, function(close_err)
cb(write_err or close_err)
@ -529,9 +530,9 @@ local function create_trash_info(path, cb)
local trash_dir = get_write_trash_dir(path)
local basename = vim.fs.basename(path)
local now = os.time()
local name = string.format("%s-%d.%d", basename, now, math.random(100000, 999999))
local dest_path = fs.join(trash_dir, "files", name)
local dest_info = fs.join(trash_dir, "info", name .. ".trashinfo")
local name = string.format('%s-%d.%d', basename, now, math.random(100000, 999999))
local dest_path = fs.join(trash_dir, 'files', name)
local dest_info = fs.join(trash_dir, 'info', name .. '.trashinfo')
uv.fs_lstat(path, function(err, stat)
if err then
return cb(err)
@ -557,19 +558,19 @@ end
---@param action oil.Action
---@param cb fun(err: nil|string)
M.perform_action = function(action, cb)
if action.type == "delete" then
if action.type == 'delete' then
local entry = assert(cache.get_entry_by_url(action.url))
local meta = entry[FIELD_META]
---@type oil.TrashInfo
local trash_info = assert(meta).trash_info
purge(trash_info, cb)
elseif action.type == "move" then
elseif action.type == 'move' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if src_adapter.name == "files" then
if src_adapter.name == 'files' then
local _, path = util.parse_url(action.src_url)
M.delete_to_trash(assert(path), cb)
elseif dest_adapter.name == "files" then
elseif dest_adapter.name == 'files' then
-- Restore
local _, dest_path = util.parse_url(action.dest_url)
assert(dest_path)
@ -584,23 +585,23 @@ M.perform_action = function(action, cb)
uv.fs_unlink(trash_info.info_file, cb)
end)
else
error("Must be moving files into or out of trash")
error('Must be moving files into or out of trash')
end
elseif action.type == "copy" then
elseif action.type == 'copy' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if src_adapter.name == "files" then
if src_adapter.name == 'files' then
local _, path = util.parse_url(action.src_url)
assert(path)
create_trash_info(path, function(err, trash_info)
if err then
cb(err)
else
local stat_type = trash_info.stat.type or "unknown"
local stat_type = trash_info.stat.type or 'unknown'
fs.recursive_copy(stat_type, path, trash_info.trash_file, vim.schedule_wrap(cb))
end
end)
elseif dest_adapter.name == "files" then
elseif dest_adapter.name == 'files' then
-- Restore
local _, dest_path = util.parse_url(action.dest_url)
assert(dest_path)
@ -610,10 +611,10 @@ M.perform_action = function(action, cb)
local trash_info = assert(meta).trash_info
fs.recursive_copy(action.entry_type, trash_info.trash_file, dest_path, cb)
else
error("Must be moving files into or out of trash")
error('Must be moving files into or out of trash')
end
else
cb(string.format("Bad action type: %s", action.type))
cb(string.format('Bad action type: %s', action.type))
end
end
@ -624,7 +625,7 @@ M.delete_to_trash = function(path, cb)
if err then
cb(err)
else
local stat_type = trash_info.stat.type or "unknown"
local stat_type = trash_info.stat.type or 'unknown'
fs.recursive_move(stat_type, path, trash_info.trash_file, vim.schedule_wrap(cb))
end
end)

View file

@ -1,8 +1,8 @@
local cache = require("oil.cache")
local config = require("oil.config")
local files = require("oil.adapters.files")
local fs = require("oil.fs")
local util = require("oil.util")
local cache = require('oil.cache')
local config = require('oil.config')
local files = require('oil.adapters.files')
local fs = require('oil.fs')
local util = require('oil.util')
local uv = vim.uv or vim.loop
@ -15,7 +15,7 @@ end
---Gets the location of the home trash dir, creating it if necessary
---@return string
local function get_trash_dir()
local trash_dir = fs.join(assert(uv.os_homedir()), ".Trash")
local trash_dir = fs.join(assert(uv.os_homedir()), '.Trash')
touch_dir(trash_dir)
return trash_dir
end
@ -25,7 +25,7 @@ end
M.normalize_url = function(url, callback)
local scheme, path = util.parse_url(url)
assert(path)
callback(scheme .. "/")
callback(scheme .. '/')
end
---@param url string
@ -34,8 +34,8 @@ end
M.get_entry_path = function(url, entry, cb)
local trash_dir = get_trash_dir()
local path = fs.join(trash_dir, entry.name)
if entry.type == "directory" then
path = "oil://" .. path
if entry.type == 'directory' then
path = 'oil://' .. path
end
cb(path)
end
@ -51,7 +51,7 @@ M.list = function(url, column_defs, cb)
---@diagnostic disable-next-line: param-type-mismatch, discard-returns
uv.fs_opendir(trash_dir, function(open_err, fd)
if open_err then
if open_err:match("^ENOENT: no such file or directory") then
if open_err:match('^ENOENT: no such file or directory') then
-- If the directory doesn't exist, treat the list as a success. We will be able to traverse
-- and edit a not-yet-existing directory.
return cb()
@ -111,35 +111,35 @@ M.get_column = function(name)
return nil
end
M.supported_cross_adapter_actions = { files = "move" }
M.supported_cross_adapter_actions = { files = 'move' }
---@param action oil.Action
---@return string
M.render_action = function(action)
if action.type == "create" then
return string.format("CREATE %s", action.url)
elseif action.type == "delete" then
return string.format(" PURGE %s", action.url)
elseif action.type == "move" then
if action.type == 'create' then
return string.format('CREATE %s', action.url)
elseif action.type == 'delete' then
return string.format(' PURGE %s', action.url)
elseif action.type == 'move' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if src_adapter.name == "files" then
if src_adapter.name == 'files' then
local _, path = util.parse_url(action.src_url)
assert(path)
local short_path = files.to_short_os_path(path, action.entry_type)
return string.format(" TRASH %s", short_path)
elseif dest_adapter.name == "files" then
return string.format(' TRASH %s', short_path)
elseif dest_adapter.name == 'files' then
local _, path = util.parse_url(action.dest_url)
assert(path)
local short_path = files.to_short_os_path(path, action.entry_type)
return string.format("RESTORE %s", short_path)
return string.format('RESTORE %s', short_path)
else
return string.format(" %s %s -> %s", action.type:upper(), action.src_url, action.dest_url)
return string.format(' %s %s -> %s', action.type:upper(), action.src_url, action.dest_url)
end
elseif action.type == "copy" then
return string.format(" %s %s -> %s", action.type:upper(), action.src_url, action.dest_url)
elseif action.type == 'copy' then
return string.format(' %s %s -> %s', action.type:upper(), action.src_url, action.dest_url)
else
error("Bad action type")
error('Bad action type')
end
end
@ -147,20 +147,20 @@ end
---@param cb fun(err: nil|string)
M.perform_action = function(action, cb)
local trash_dir = get_trash_dir()
if action.type == "create" then
if action.type == 'create' then
local _, path = util.parse_url(action.url)
assert(path)
path = trash_dir .. path
if action.entry_type == "directory" then
if action.entry_type == 'directory' then
uv.fs_mkdir(path, 493, function(err)
-- Ignore if the directory already exists
if not err or err:match("^EEXIST:") then
if not err or err:match('^EEXIST:') then
cb()
else
cb(err)
end
end) -- 0755
elseif action.entry_type == "link" and action.link then
elseif action.entry_type == 'link' and action.link then
local flags = nil
local target = fs.posix_to_os_path(action.link)
---@diagnostic disable-next-line: param-type-mismatch
@ -168,33 +168,33 @@ M.perform_action = function(action, cb)
else
fs.touch(path, config.new_file_mode, cb)
end
elseif action.type == "delete" then
elseif action.type == 'delete' then
local _, path = util.parse_url(action.url)
assert(path)
local fullpath = trash_dir .. path
fs.recursive_delete(action.entry_type, fullpath, cb)
elseif action.type == "move" or action.type == "copy" then
elseif action.type == 'move' or action.type == 'copy' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
local _, src_path = util.parse_url(action.src_url)
local _, dest_path = util.parse_url(action.dest_url)
assert(src_path and dest_path)
if src_adapter.name == "files" then
if src_adapter.name == 'files' then
dest_path = trash_dir .. dest_path
elseif dest_adapter.name == "files" then
elseif dest_adapter.name == 'files' then
src_path = trash_dir .. src_path
else
dest_path = trash_dir .. dest_path
src_path = trash_dir .. src_path
end
if action.type == "move" then
if action.type == 'move' then
fs.recursive_move(action.entry_type, src_path, dest_path, cb)
else
fs.recursive_copy(action.entry_type, src_path, dest_path, cb)
end
else
cb(string.format("Bad action type: %s", action.type))
cb(string.format('Bad action type: %s', action.type))
end
end
@ -212,8 +212,8 @@ M.delete_to_trash = function(path, cb)
end
assert(src_stat)
if uv.fs_lstat(dest) then
local date_str = vim.fn.strftime(" %Y-%m-%dT%H:%M:%S")
local name_pieces = vim.split(basename, ".", { plain = true })
local date_str = vim.fn.strftime(' %Y-%m-%dT%H:%M:%S')
local name_pieces = vim.split(basename, '.', { plain = true })
if #name_pieces > 1 then
table.insert(name_pieces, #name_pieces - 1, date_str)
basename = table.concat(name_pieces)

View file

@ -1,11 +1,11 @@
local util = require("oil.util")
local util = require('oil.util')
local uv = vim.uv or vim.loop
local cache = require("oil.cache")
local config = require("oil.config")
local constants = require("oil.constants")
local files = require("oil.adapters.files")
local fs = require("oil.fs")
local powershell_trash = require("oil.adapters.trash.windows.powershell-trash")
local cache = require('oil.cache')
local config = require('oil.config')
local constants = require('oil.constants')
local files = require('oil.adapters.files')
local fs = require('oil.fs')
local powershell_trash = require('oil.adapters.trash.windows.powershell-trash')
local FIELD_META = constants.FIELD_META
local FIELD_TYPE = constants.FIELD_TYPE
@ -15,22 +15,22 @@ local M = {}
---@return string
local function get_trash_dir()
local cwd = assert(vim.fn.getcwd())
local trash_dir = cwd:sub(1, 3) .. "$Recycle.Bin"
local trash_dir = cwd:sub(1, 3) .. '$Recycle.Bin'
if vim.fn.isdirectory(trash_dir) == 1 then
return trash_dir
end
trash_dir = "C:\\$Recycle.Bin"
trash_dir = 'C:\\$Recycle.Bin'
if vim.fn.isdirectory(trash_dir) == 1 then
return trash_dir
end
error("No trash found")
error('No trash found')
end
---@param path string
---@return string
local win_addslash = function(path)
if not vim.endswith(path, "\\") then
return path .. "\\"
if not vim.endswith(path, '\\') then
return path .. '\\'
else
return path
end
@ -61,7 +61,7 @@ M.list = function(url, column_defs, cb)
local raw_displayed_entries = vim.tbl_filter(
---@param entry {IsFolder: boolean, DeletionDate: integer, Name: string, Path: string, OriginalPath: string}
function(entry)
local parent = win_addslash(assert(vim.fn.fnamemodify(entry.OriginalPath, ":h")))
local parent = win_addslash(assert(vim.fn.fnamemodify(entry.OriginalPath, ':h')))
local is_in_path = path == parent
local is_subpath = fs.is_subpath(path, parent)
return is_in_path or is_subpath or show_all_files
@ -72,27 +72,27 @@ M.list = function(url, column_defs, cb)
---@param entry {IsFolder: boolean, DeletionDate: integer, Name: string, Path: string, OriginalPath: string}
---@return {[1]:nil, [2]:string, [3]:string, [4]:{stat: uv_fs_t, trash_info: oil.WindowsTrashInfo, display_name: string}}
function(entry)
local parent = win_addslash(assert(vim.fn.fnamemodify(entry.OriginalPath, ":h")))
local parent = win_addslash(assert(vim.fn.fnamemodify(entry.OriginalPath, ':h')))
--- @type oil.InternalEntry
local cache_entry
if path == parent or show_all_files then
local deleted_file_tail = assert(vim.fn.fnamemodify(entry.Path, ":t"))
local deleted_file_head = assert(vim.fn.fnamemodify(entry.Path, ":h"))
local deleted_file_tail = assert(vim.fn.fnamemodify(entry.Path, ':t'))
local deleted_file_head = assert(vim.fn.fnamemodify(entry.Path, ':h'))
local info_file_head = deleted_file_head
--- @type string?
local info_file
cache_entry =
cache.create_entry(url, deleted_file_tail, entry.IsFolder and "directory" or "file")
cache.create_entry(url, deleted_file_tail, entry.IsFolder and 'directory' or 'file')
-- info_file on windows has the following format: $I<6 char hash>.<extension>
-- the hash is the same for the deleted file and the info file
-- so, we take the hash (and extension) from the deleted file
--
-- see https://superuser.com/questions/368890/how-does-the-recycle-bin-in-windows-work/1736690#1736690
local info_file_tail = deleted_file_tail:match("^%$R(.*)$") --[[@as string?]]
local info_file_tail = deleted_file_tail:match('^%$R(.*)$') --[[@as string?]]
if info_file_tail then
info_file_tail = "$I" .. info_file_tail
info_file = info_file_head .. "\\" .. info_file_tail
info_file_tail = '$I' .. info_file_tail
info_file = info_file_head .. '\\' .. info_file_tail
end
cache_entry[FIELD_META] = {
stat = nil,
@ -109,10 +109,10 @@ M.list = function(url, column_defs, cb)
if path ~= parent and (show_all_files or fs.is_subpath(path, parent)) then
local name = parent:sub(path:len() + 1)
local next_par = vim.fs.dirname(name)
while next_par ~= "." do
while next_par ~= '.' do
name = next_par
next_par = vim.fs.dirname(name)
cache_entry = cache.create_entry(url, name, "directory")
cache_entry = cache.create_entry(url, name, 'directory')
cache_entry[FIELD_META] = {}
end
@ -132,7 +132,7 @@ end
local current_year
-- Make sure we run this import-time effect in the main loop (mostly for tests)
vim.schedule(function()
current_year = vim.fn.strftime("%Y")
current_year = vim.fn.strftime('%Y')
end)
local file_columns = {}
@ -153,11 +153,11 @@ file_columns.mtime = {
if fmt then
ret = vim.fn.strftime(fmt, time)
else
local year = vim.fn.strftime("%Y", time)
local year = vim.fn.strftime('%Y', time)
if year ~= current_year then
ret = vim.fn.strftime("%b %d %Y", time)
ret = vim.fn.strftime('%b %d %Y', time)
else
ret = vim.fn.strftime("%b %d %H:%M", time)
ret = vim.fn.strftime('%b %d %H:%M', time)
end
end
return ret
@ -178,11 +178,11 @@ file_columns.mtime = {
local fmt = conf and conf.format
local pattern
if fmt then
pattern = fmt:gsub("%%.", "%%S+")
pattern = fmt:gsub('%%.', '%%S+')
else
pattern = "%S+%s+%d+%s+%d%d:?%d%d"
pattern = '%S+%s+%d+%s+%d%d:?%d%d'
end
return line:match("^(" .. pattern .. ")%s+(.+)$")
return line:match('^(' .. pattern .. ')%s+(.+)$')
end,
}
@ -195,20 +195,21 @@ end
---@param action oil.Action
---@return boolean
M.filter_action = function(action)
if action.type == "create" then
if action.type == 'create' then
return false
elseif action.type == "delete" then
elseif action.type == 'delete' then
local entry = assert(cache.get_entry_by_url(action.url))
local meta = entry[FIELD_META]
return meta ~= nil and meta.trash_info ~= nil
elseif action.type == "move" then
elseif action.type == 'move' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
return src_adapter.name == "files" or dest_adapter.name == "files"
elseif action.type == "copy" then
return src_adapter.name == 'files' or dest_adapter.name == 'files'
-- selene: allow(if_same_then_else)
elseif action.type == 'copy' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
return src_adapter.name == "files" or dest_adapter.name == "files"
return src_adapter.name == 'files' or dest_adapter.name == 'files'
else
error(string.format("Bad action type '%s'", action.type))
end
@ -219,7 +220,7 @@ end
M.normalize_url = function(url, callback)
local scheme, path = util.parse_url(url)
assert(path)
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(path), ":p")
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(path), ':p')
assert(os_path)
uv.fs_realpath(
os_path,
@ -244,16 +245,16 @@ M.get_entry_path = function(url, entry, cb)
end
local path = fs.os_to_posix_path(trash_info.trash_file)
if entry.type == "directory" then
if entry.type == 'directory' then
path = win_addslash(path)
end
cb("oil://" .. path)
cb('oil://' .. path)
end
---@param err oil.ParseError
---@return boolean
M.filter_error = function(err)
if err.message == "Duplicate filename" then
if err.message == 'Duplicate filename' then
return false
end
return true
@ -262,44 +263,44 @@ end
---@param action oil.Action
---@return string
M.render_action = function(action)
if action.type == "delete" then
if action.type == 'delete' then
local entry = assert(cache.get_entry_by_url(action.url))
local meta = entry[FIELD_META]
---@type oil.WindowsTrashInfo
local trash_info = assert(meta).trash_info
local short_path = fs.shorten_path(trash_info.original_path)
return string.format(" PURGE %s", short_path)
elseif action.type == "move" then
return string.format(' PURGE %s', short_path)
elseif action.type == 'move' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if src_adapter.name == "files" then
if src_adapter.name == 'files' then
local _, path = util.parse_url(action.src_url)
assert(path)
local short_path = files.to_short_os_path(path, action.entry_type)
return string.format(" TRASH %s", short_path)
elseif dest_adapter.name == "files" then
return string.format(' TRASH %s', short_path)
elseif dest_adapter.name == 'files' then
local _, path = util.parse_url(action.dest_url)
assert(path)
local short_path = files.to_short_os_path(path, action.entry_type)
return string.format("RESTORE %s", short_path)
return string.format('RESTORE %s', short_path)
else
error("Must be moving files into or out of trash")
error('Must be moving files into or out of trash')
end
elseif action.type == "copy" then
elseif action.type == 'copy' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if src_adapter.name == "files" then
if src_adapter.name == 'files' then
local _, path = util.parse_url(action.src_url)
assert(path)
local short_path = files.to_short_os_path(path, action.entry_type)
return string.format(" COPY %s -> TRASH", short_path)
elseif dest_adapter.name == "files" then
return string.format(' COPY %s -> TRASH', short_path)
elseif dest_adapter.name == 'files' then
local _, path = util.parse_url(action.dest_url)
assert(path)
local short_path = files.to_short_os_path(path, action.entry_type)
return string.format("RESTORE %s", short_path)
return string.format('RESTORE %s', short_path)
else
error("Must be copying files into or out of trash")
error('Must be copying files into or out of trash')
end
else
error(string.format("Bad action type '%s'", action.type))
@ -309,11 +310,11 @@ end
---@param trash_info oil.WindowsTrashInfo
---@param cb fun(err?: string, raw_entries: oil.WindowsRawEntry[]?)
local purge = function(trash_info, cb)
fs.recursive_delete("file", trash_info.info_file, function(err)
fs.recursive_delete('file', trash_info.info_file, function(err)
if err then
return cb(err)
end
fs.recursive_delete("file", trash_info.trash_file, cb)
fs.recursive_delete('file', trash_info.trash_file, cb)
end)
end
@ -321,7 +322,7 @@ end
---@param type string
---@param cb fun(err?: string, trash_info?: oil.TrashInfo)
local function create_trash_info_and_copy(path, type, cb)
local temp_path = path .. "temp"
local temp_path = path .. 'temp'
-- create a temporary copy on the same location
fs.recursive_copy(
type,
@ -346,19 +347,19 @@ end
---@param action oil.Action
---@param cb fun(err: nil|string)
M.perform_action = function(action, cb)
if action.type == "delete" then
if action.type == 'delete' then
local entry = assert(cache.get_entry_by_url(action.url))
local meta = entry[FIELD_META] --[[@as {stat: uv_fs_t, trash_info: oil.WindowsTrashInfo, display_name: string}]]
local trash_info = meta and meta.trash_info
purge(trash_info, cb)
elseif action.type == "move" then
elseif action.type == 'move' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if src_adapter.name == "files" then
if src_adapter.name == 'files' then
local _, path = util.parse_url(action.src_url)
M.delete_to_trash(assert(path), cb)
elseif dest_adapter.name == "files" then
elseif dest_adapter.name == 'files' then
-- Restore
local _, dest_path = util.parse_url(action.dest_url)
assert(dest_path)
@ -373,16 +374,16 @@ M.perform_action = function(action, cb)
uv.fs_unlink(trash_info.info_file, cb)
end)
end
elseif action.type == "copy" then
elseif action.type == 'copy' then
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
if src_adapter.name == "files" then
if src_adapter.name == 'files' then
local _, path = util.parse_url(action.src_url)
assert(path)
path = fs.posix_to_os_path(path)
local entry = assert(cache.get_entry_by_url(action.src_url))
create_trash_info_and_copy(path, entry[FIELD_TYPE], cb)
elseif dest_adapter.name == "files" then
elseif dest_adapter.name == 'files' then
-- Restore
local _, dest_path = util.parse_url(action.dest_url)
assert(dest_path)
@ -392,14 +393,14 @@ M.perform_action = function(action, cb)
local trash_info = meta and meta.trash_info
fs.recursive_copy(action.entry_type, trash_info.trash_file, dest_path, cb)
else
error("Must be moving files into or out of trash")
error('Must be moving files into or out of trash')
end
else
cb(string.format("Bad action type: %s", action.type))
cb(string.format('Bad action type: %s', action.type))
end
end
M.supported_cross_adapter_actions = { files = "move" }
M.supported_cross_adapter_actions = { files = 'move' }
---@param path string
---@param cb fun(err?: string)

View file

@ -41,23 +41,23 @@ function PowershellConnection:_init(init_command)
-- 65001 is the UTF-8 codepage
-- powershell needs to be launched with the UTF-8 codepage to use it for both stdin and stdout
local jid = vim.fn.jobstart({
"cmd",
"/c",
'cmd',
'/c',
'"chcp 65001 && powershell -NoProfile -NoLogo -ExecutionPolicy Bypass -NoExit -Command -"',
}, {
---@param data string[]
on_stdout = function(_, data)
for _, fragment in ipairs(data) do
if fragment:find("===DONE%((%a+)%)===") then
if fragment:find('===DONE%((%a+)%)===') then
self.is_reading_data = false
local output = table.concat(self.stdout, "")
local output = table.concat(self.stdout, '')
local cb = self.commands[1].cb
table.remove(self.commands, 1)
local success = fragment:match("===DONE%((%a+)%)===")
if success == "True" then
local success = fragment:match('===DONE%((%a+)%)===')
if success == 'True' then
cb(nil, output)
elseif success == "False" then
cb(success .. ": " .. output, output)
elseif success == 'False' then
cb(success .. ': ' .. output, output)
end
self.stdout = {}
self:_consume()

View file

@ -1,5 +1,5 @@
-- A wrapper around trash operations using windows powershell
local Powershell = require("oil.adapters.trash.windows.powershell-connection")
local Powershell = require('oil.adapters.trash.windows.powershell-connection')
---@class oil.WindowsRawEntry
---@field IsFolder boolean

View file

@ -1,5 +1,5 @@
local constants = require("oil.constants")
local util = require("oil.util")
local constants = require('oil.constants')
local util = require('oil.util')
local M = {}
local FIELD_ID = constants.FIELD_ID
@ -28,7 +28,7 @@ local _cached_id_fmt
M.format_id = function(id)
if not _cached_id_fmt then
local id_str_length = math.max(3, 1 + math.floor(math.log10(next_id)))
_cached_id_fmt = "/%0" .. string.format("%d", id_str_length) .. "d"
_cached_id_fmt = '/%0' .. string.format('%d', id_str_length) .. 'd'
end
return _cached_id_fmt:format(id)
end
@ -125,8 +125,8 @@ end
M.get_entry_by_url = function(url)
local scheme, path = util.parse_url(url)
assert(path)
local parent_url = scheme .. vim.fn.fnamemodify(path, ":h")
local basename = vim.fn.fnamemodify(path, ":t")
local parent_url = scheme .. vim.fn.fnamemodify(path, ':h')
local basename = vim.fn.fnamemodify(path, ':t')
return M.list_url(parent_url)[basename]
end
@ -135,7 +135,7 @@ end
M.get_parent_url = function(id)
local url = parent_url_by_id[id]
if not url then
error(string.format("Entry %d missing parent url", id))
error(string.format('Entry %d missing parent url', id))
end
return url
end
@ -149,32 +149,32 @@ end
---@param action oil.Action
M.perform_action = function(action)
if action.type == "create" then
if action.type == 'create' then
local scheme, path = util.parse_url(action.url)
assert(path)
local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ":h"))
local name = vim.fn.fnamemodify(path, ":t")
local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ':h'))
local name = vim.fn.fnamemodify(path, ':t')
M.create_and_store_entry(parent_url, name, action.entry_type)
elseif action.type == "delete" then
elseif action.type == 'delete' then
local scheme, path = util.parse_url(action.url)
assert(path)
local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ":h"))
local name = vim.fn.fnamemodify(path, ":t")
local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ':h'))
local name = vim.fn.fnamemodify(path, ':t')
local entry = url_directory[parent_url][name]
url_directory[parent_url][name] = nil
entries_by_id[entry[FIELD_ID]] = nil
parent_url_by_id[entry[FIELD_ID]] = nil
elseif action.type == "move" then
elseif action.type == 'move' then
local src_scheme, src_path = util.parse_url(action.src_url)
assert(src_path)
local src_parent_url = util.addslash(src_scheme .. vim.fn.fnamemodify(src_path, ":h"))
local src_name = vim.fn.fnamemodify(src_path, ":t")
local src_parent_url = util.addslash(src_scheme .. vim.fn.fnamemodify(src_path, ':h'))
local src_name = vim.fn.fnamemodify(src_path, ':t')
local entry = url_directory[src_parent_url][src_name]
local dest_scheme, dest_path = util.parse_url(action.dest_url)
assert(dest_path)
local dest_parent_url = util.addslash(dest_scheme .. vim.fn.fnamemodify(dest_path, ":h"))
local dest_name = vim.fn.fnamemodify(dest_path, ":t")
local dest_parent_url = util.addslash(dest_scheme .. vim.fn.fnamemodify(dest_path, ':h'))
local dest_name = vim.fn.fnamemodify(dest_path, ':t')
url_directory[src_parent_url][src_name] = nil
local dest_parent = url_directory[dest_parent_url]
@ -188,13 +188,14 @@ M.perform_action = function(action)
parent_url_by_id[entry[FIELD_ID]] = dest_parent_url
entry[FIELD_NAME] = dest_name
util.update_moved_buffers(action.entry_type, action.src_url, action.dest_url)
elseif action.type == "copy" then
elseif action.type == 'copy' then
local scheme, path = util.parse_url(action.dest_url)
assert(path)
local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ":h"))
local name = vim.fn.fnamemodify(path, ":t")
local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ':h'))
local name = vim.fn.fnamemodify(path, ':t')
M.create_and_store_entry(parent_url, name, action.entry_type)
elseif action.type == "change" then
-- selene: allow(empty_if)
elseif action.type == 'change' then
-- Cache doesn't need to update
else
---@diagnostic disable-next-line: undefined-field

View file

@ -1,11 +1,11 @@
local cache = require("oil.cache")
local columns = require("oil.columns")
local config = require("oil.config")
local fs = require("oil.fs")
local oil = require("oil")
local parser = require("oil.mutator.parser")
local util = require("oil.util")
local view = require("oil.view")
local cache = require('oil.cache')
local columns = require('oil.columns')
local config = require('oil.config')
local fs = require('oil.fs')
local oil = require('oil')
local parser = require('oil.mutator.parser')
local util = require('oil.util')
local view = require('oil.view')
local M = {}
@ -16,10 +16,10 @@ local function get_linux_session_type()
return
end
xdg_session_type = xdg_session_type:lower()
if xdg_session_type:find("x11") then
return "x11"
elseif xdg_session_type:find("wayland") then
return "wayland"
if xdg_session_type:find('x11') then
return 'x11'
elseif xdg_session_type:find('wayland') then
return 'wayland'
else
return nil
end
@ -29,9 +29,9 @@ end
local function is_linux_desktop_gnome()
local cur_desktop = vim.env.XDG_CURRENT_DESKTOP
local session_desktop = vim.env.XDG_SESSION_DESKTOP
local idx = session_desktop and session_desktop:lower():find("gnome")
or cur_desktop and cur_desktop:lower():find("gnome")
return idx ~= nil or cur_desktop == "X-Cinnamon" or cur_desktop == "XFCE"
local idx = session_desktop and session_desktop:lower():find('gnome')
or cur_desktop and cur_desktop:lower():find('gnome')
return idx ~= nil or cur_desktop == 'X-Cinnamon' or cur_desktop == 'XFCE'
end
---@param winid integer
@ -55,7 +55,7 @@ end
---@param entry oil.InternalEntry
local function remove_entry_from_parent_buffer(parent_url, entry)
local bufnr = vim.fn.bufadd(parent_url)
assert(vim.api.nvim_buf_is_loaded(bufnr), "Expected parent buffer to be loaded during paste")
assert(vim.api.nvim_buf_is_loaded(bufnr), 'Expected parent buffer to be loaded during paste')
local adapter = assert(util.get_adapter(bufnr))
local column_defs = columns.get_supported_columns(adapter)
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
@ -77,7 +77,7 @@ end
---@param delete_original? boolean
local function paste_paths(paths, delete_original)
local bufnr = vim.api.nvim_get_current_buf()
local scheme = "oil://"
local scheme = 'oil://'
local adapter = assert(config.get_adapter_by_scheme(scheme))
local column_defs = columns.get_supported_columns(scheme)
local winid = vim.api.nvim_get_current_win()
@ -88,7 +88,7 @@ local function paste_paths(paths, delete_original)
-- Handle as many paths synchronously as possible
for _, path in ipairs(paths) do
-- Trim the trailing slash off directories
if vim.endswith(path, "/") then
if vim.endswith(path, '/') then
path = path:sub(1, -2)
end
@ -114,7 +114,7 @@ local function paste_paths(paths, delete_original)
local cursor = vim.api.nvim_win_get_cursor(winid)
local complete_loading = util.cb_collect(#vim.tbl_keys(parent_urls), function(err)
if err then
vim.notify(string.format("Error loading parent directory: %s", err), vim.log.levels.ERROR)
vim.notify(string.format('Error loading parent directory: %s', err), vim.log.levels.ERROR)
else
-- Something in this process moves the cursor to the top of the window, so have to restore it
vim.api.nvim_win_set_cursor(winid, cursor)
@ -149,8 +149,8 @@ end
---@return integer end
local function range_from_selection()
-- [bufnum, lnum, col, off]; both row and column 1-indexed
local start = vim.fn.getpos("v")
local end_ = vim.fn.getpos(".")
local start = vim.fn.getpos('v')
local end_ = vim.fn.getpos('.')
local start_row = start[2]
local end_row = end_[2]
@ -164,16 +164,16 @@ end
M.copy_to_system_clipboard = function()
local dir = oil.get_current_dir()
if not dir then
vim.notify("System clipboard only works for local files", vim.log.levels.ERROR)
vim.notify('System clipboard only works for local files', vim.log.levels.ERROR)
return
end
local entries = {}
local mode = vim.api.nvim_get_mode().mode
if mode == "v" or mode == "V" then
if mode == 'v' or mode == 'V' then
if fs.is_mac then
vim.notify(
"Copying multiple paths to clipboard is not supported on mac",
'Copying multiple paths to clipboard is not supported on mac',
vim.log.levels.ERROR
)
return
@ -184,7 +184,7 @@ M.copy_to_system_clipboard = function()
end
-- leave visual mode
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<Esc>", true, false, true), "n", true)
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Esc>', true, false, true), 'n', true)
else
table.insert(entries, oil.get_cursor_entry())
end
@ -193,7 +193,7 @@ M.copy_to_system_clipboard = function()
entries = vim.tbl_values(entries)
if #entries == 0 then
vim.notify("Could not find local file under cursor", vim.log.levels.WARN)
vim.notify('Could not find local file under cursor', vim.log.levels.WARN)
return
end
local paths = {}
@ -204,38 +204,38 @@ M.copy_to_system_clipboard = function()
local stdin
if fs.is_mac then
cmd = {
"osascript",
"-e",
"on run args",
"-e",
"set the clipboard to POSIX file (first item of args)",
"-e",
"end run",
'osascript',
'-e',
'on run args',
'-e',
'set the clipboard to POSIX file (first item of args)',
'-e',
'end run',
paths[1],
}
elseif fs.is_linux then
local xdg_session_type = get_linux_session_type()
if xdg_session_type == "x11" then
vim.list_extend(cmd, { "xclip", "-i", "-selection", "clipboard" })
elseif xdg_session_type == "wayland" then
table.insert(cmd, "wl-copy")
if xdg_session_type == 'x11' then
vim.list_extend(cmd, { 'xclip', '-i', '-selection', 'clipboard' })
elseif xdg_session_type == 'wayland' then
table.insert(cmd, 'wl-copy')
else
vim.notify("System clipboard not supported, check $XDG_SESSION_TYPE", vim.log.levels.ERROR)
vim.notify('System clipboard not supported, check $XDG_SESSION_TYPE', vim.log.levels.ERROR)
return
end
local urls = {}
for _, path in ipairs(paths) do
table.insert(urls, "file://" .. path)
table.insert(urls, 'file://' .. path)
end
if is_linux_desktop_gnome() then
stdin = string.format("copy\n%s\0", table.concat(urls, "\n"))
vim.list_extend(cmd, { "-t", "x-special/gnome-copied-files" })
stdin = string.format('copy\n%s\0', table.concat(urls, '\n'))
vim.list_extend(cmd, { '-t', 'x-special/gnome-copied-files' })
else
stdin = table.concat(urls, "\n") .. "\n"
vim.list_extend(cmd, { "-t", "text/uri-list" })
stdin = table.concat(urls, '\n') .. '\n'
vim.list_extend(cmd, { '-t', 'text/uri-list' })
end
else
vim.notify("System clipboard not supported on Windows", vim.log.levels.ERROR)
vim.notify('System clipboard not supported on Windows', vim.log.levels.ERROR)
return
end
@ -243,11 +243,11 @@ M.copy_to_system_clipboard = function()
vim.notify(string.format("Could not find executable '%s'", cmd[1]), vim.log.levels.ERROR)
return
end
local stderr = ""
local stderr = ''
local jid = vim.fn.jobstart(cmd, {
stderr_buffered = true,
on_stderr = function(_, data)
stderr = table.concat(data, "\n")
stderr = table.concat(data, '\n')
end,
on_exit = function(j, exit_code)
if exit_code ~= 0 then
@ -259,15 +259,15 @@ M.copy_to_system_clipboard = function()
if #paths == 1 then
vim.notify(string.format("Copied '%s' to system clipboard", paths[1]))
else
vim.notify(string.format("Copied %d files to system clipboard", #paths))
vim.notify(string.format('Copied %d files to system clipboard', #paths))
end
end
end,
})
assert(jid > 0, "Failed to start job")
assert(jid > 0, 'Failed to start job')
if stdin then
vim.api.nvim_chan_send(jid, stdin)
vim.fn.chanclose(jid, "stdin")
vim.fn.chanclose(jid, 'stdin')
end
end
@ -276,7 +276,7 @@ end
local function handle_paste_output_mac(lines)
local ret = {}
for _, line in ipairs(lines) do
if not line:match("^%s*$") then
if not line:match('^%s*$') then
table.insert(ret, line)
end
end
@ -288,7 +288,7 @@ end
local function handle_paste_output_linux(lines)
local ret = {}
for _, line in ipairs(lines) do
local path = line:match("^file://(.+)$")
local path = line:match('^file://(.+)$')
if path then
table.insert(ret, util.url_unescape(path))
end
@ -306,37 +306,37 @@ M.paste_from_system_clipboard = function(delete_original)
local handle_paste_output
if fs.is_mac then
cmd = {
"osascript",
"-e",
"on run",
"-e",
"POSIX path of (the clipboard as «class furl»)",
"-e",
"end run",
'osascript',
'-e',
'on run',
'-e',
'POSIX path of (the clipboard as «class furl»)',
'-e',
'end run',
}
handle_paste_output = handle_paste_output_mac
elseif fs.is_linux then
local xdg_session_type = get_linux_session_type()
if xdg_session_type == "x11" then
vim.list_extend(cmd, { "xclip", "-o", "-selection", "clipboard" })
elseif xdg_session_type == "wayland" then
table.insert(cmd, "wl-paste")
if xdg_session_type == 'x11' then
vim.list_extend(cmd, { 'xclip', '-o', '-selection', 'clipboard' })
elseif xdg_session_type == 'wayland' then
table.insert(cmd, 'wl-paste')
else
vim.notify("System clipboard not supported, check $XDG_SESSION_TYPE", vim.log.levels.ERROR)
vim.notify('System clipboard not supported, check $XDG_SESSION_TYPE', vim.log.levels.ERROR)
return
end
if is_linux_desktop_gnome() then
vim.list_extend(cmd, { "-t", "x-special/gnome-copied-files" })
vim.list_extend(cmd, { '-t', 'x-special/gnome-copied-files' })
else
vim.list_extend(cmd, { "-t", "text/uri-list" })
vim.list_extend(cmd, { '-t', 'text/uri-list' })
end
handle_paste_output = handle_paste_output_linux
else
vim.notify("System clipboard not supported on Windows", vim.log.levels.ERROR)
vim.notify('System clipboard not supported on Windows', vim.log.levels.ERROR)
return
end
local paths
local stderr = ""
local stderr = ''
if vim.fn.executable(cmd[1]) == 0 then
vim.notify(string.format("Could not find executable '%s'", cmd[1]), vim.log.levels.ERROR)
return
@ -345,26 +345,26 @@ M.paste_from_system_clipboard = function(delete_original)
stdout_buffered = true,
stderr_buffered = true,
on_stdout = function(j, data)
local lines = vim.split(table.concat(data, "\n"), "\r?\n")
local lines = vim.split(table.concat(data, '\n'), '\r?\n')
paths = handle_paste_output(lines)
end,
on_stderr = function(_, data)
stderr = table.concat(data, "\n")
stderr = table.concat(data, '\n')
end,
on_exit = function(j, exit_code)
if exit_code ~= 0 or not paths then
vim.notify(
string.format("Error pasting from system clipboard: %s", stderr),
string.format('Error pasting from system clipboard: %s', stderr),
vim.log.levels.ERROR
)
elseif #paths == 0 then
vim.notify("No valid files found in system clipboard", vim.log.levels.WARN)
vim.notify('No valid files found in system clipboard', vim.log.levels.WARN)
else
paste_paths(paths, delete_original)
end
end,
})
assert(jid > 0, "Failed to start job")
assert(jid > 0, 'Failed to start job')
end
return M

View file

@ -1,6 +1,6 @@
local config = require("oil.config")
local constants = require("oil.constants")
local util = require("oil.util")
local config = require('oil.config')
local constants = require('oil.constants')
local util = require('oil.util')
local M = {}
local FIELD_NAME = constants.FIELD_NAME
@ -38,7 +38,7 @@ end
---@return oil.ColumnSpec[]
M.get_supported_columns = function(adapter_or_scheme)
local adapter
if type(adapter_or_scheme) == "string" then
if type(adapter_or_scheme) == 'string' then
adapter = config.get_adapter_by_scheme(adapter_or_scheme)
else
adapter = adapter_or_scheme
@ -53,7 +53,7 @@ M.get_supported_columns = function(adapter_or_scheme)
return ret
end
local EMPTY = { "-", "OilEmpty" }
local EMPTY = { '-', 'OilEmpty' }
M.EMPTY = EMPTY
@ -71,17 +71,17 @@ M.render_col = function(adapter, col_def, entry, bufnr)
end
local chunk = column.render(entry, conf, bufnr)
if type(chunk) == "table" then
if chunk[1]:match("^%s*$") then
if type(chunk) == 'table' then
if chunk[1]:match('^%s*$') then
return EMPTY
end
else
if not chunk or chunk:match("^%s*$") then
if not chunk or chunk:match('^%s*$') then
return EMPTY
end
if conf and conf.highlight then
local highlight = conf.highlight
if type(highlight) == "function" then
if type(highlight) == 'function' then
highlight = conf.highlight(chunk)
end
return { chunk, highlight }
@ -98,13 +98,13 @@ end
M.parse_col = function(adapter, line, col_def)
local name, conf = util.split_config(col_def)
-- If rendering failed, there will just be a "-"
local empty_col, rem = line:match("^%s*(-%s+)(.*)$")
local empty_col, rem = line:match('^%s*(-%s+)(.*)$')
if empty_col then
return nil, rem
end
local column = M.get_column(adapter, name)
if column then
return column.parse(line:gsub("^%s+", ""), conf)
return column.parse(line:gsub('^%s+', ''), conf)
end
end
@ -128,12 +128,12 @@ end
M.render_change_action = function(adapter, action)
local column = M.get_column(adapter, action.column)
if not column then
error(string.format("Received change action for nonexistant column %s", action.column))
error(string.format('Received change action for nonexistant column %s', action.column))
end
if column.render_action then
return column.render_action(action)
else
return string.format("CHANGE %s %s = %s", action.url, action.column, action.value)
return string.format('CHANGE %s %s = %s', action.url, action.column, action.value)
end
end
@ -144,7 +144,7 @@ M.perform_change_action = function(adapter, action, callback)
local column = M.get_column(adapter, action.column)
if not column then
return callback(
string.format("Received change action for nonexistant column %s", action.column)
string.format('Received change action for nonexistant column %s', action.column)
)
end
column.perform_action(action, callback)
@ -152,12 +152,12 @@ end
local icon_provider = util.get_icon_provider()
if icon_provider then
M.register("icon", {
M.register('icon', {
render = function(entry, conf, bufnr)
local field_type = entry[FIELD_TYPE]
local name = entry[FIELD_NAME]
local meta = entry[FIELD_META]
if field_type == "link" and meta then
if field_type == 'link' and meta then
if meta.link then
name = meta.link
end
@ -170,11 +170,11 @@ if icon_provider then
end
local ft = nil
if conf and conf.use_slow_filetype_detection and field_type == "file" then
if conf and conf.use_slow_filetype_detection and field_type == 'file' then
local bufname = vim.api.nvim_buf_get_name(bufnr)
local _, path = util.parse_url(bufname)
if path then
local lines = vim.fn.readfile(path .. name, "", 16)
local lines = vim.fn.readfile(path .. name, '', 16)
if lines and #lines > 0 then
ft = vim.filetype.match({ filename = name, contents = lines })
end
@ -183,10 +183,10 @@ if icon_provider then
local icon, hl = icon_provider(field_type, name, conf, ft)
if not conf or conf.add_padding ~= false then
icon = icon .. " "
icon = icon .. ' '
end
if conf and conf.highlight then
if type(conf.highlight) == "function" then
if type(conf.highlight) == 'function' then
hl = conf.highlight(icon)
else
hl = conf.highlight
@ -196,29 +196,29 @@ if icon_provider then
end,
parse = function(line, conf)
return line:match("^(%S+)%s+(.*)$")
return line:match('^(%S+)%s+(.*)$')
end,
})
end
local default_type_icons = {
directory = "dir",
socket = "sock",
directory = 'dir',
socket = 'sock',
}
---@param entry oil.InternalEntry
---@return boolean
local function is_entry_directory(entry)
local type = entry[FIELD_TYPE]
if type == "directory" then
if type == 'directory' then
return true
elseif type == "link" then
elseif type == 'link' then
local meta = entry[FIELD_META]
return (meta and meta.link_stat and meta.link_stat.type == "directory") == true
return (meta and meta.link_stat and meta.link_stat.type == 'directory') == true
else
return false
end
end
M.register("type", {
M.register('type', {
render = function(entry, conf)
local entry_type = entry[FIELD_TYPE]
if conf and conf.icons then
@ -229,7 +229,7 @@ M.register("type", {
end,
parse = function(line, conf)
return line:match("^(%S+)%s+(.*)$")
return line:match('^(%S+)%s+(.*)$')
end,
get_sort_value = function(entry)
@ -242,22 +242,22 @@ M.register("type", {
})
local function adjust_number(int)
return string.format("%03d%s", #int, int)
return string.format('%03d%s', #int, int)
end
M.register("name", {
M.register('name', {
render = function(entry, conf)
error("Do not use the name column. It is for sorting only")
error('Do not use the name column. It is for sorting only')
end,
parse = function(line, conf)
error("Do not use the name column. It is for sorting only")
error('Do not use the name column. It is for sorting only')
end,
create_sort_value_factory = function(num_entries)
if
config.view_options.natural_order == false
or (config.view_options.natural_order == "fast" and num_entries > 5000)
or (config.view_options.natural_order == 'fast' and num_entries > 5000)
then
if config.view_options.case_insensitive then
return function(entry)
@ -272,7 +272,7 @@ M.register("name", {
local memo = {}
return function(entry)
if memo[entry] == nil then
local name = entry[FIELD_NAME]:gsub("0*(%d+)", adjust_number)
local name = entry[FIELD_NAME]:gsub('0*(%d+)', adjust_number)
if config.view_options.case_insensitive then
name = name:lower()
end

View file

@ -5,7 +5,7 @@ local default_config = {
-- Id is automatically added at the beginning, and name at the end
-- See :help oil-columns
columns = {
"icon",
'icon',
-- "permissions",
-- "size",
-- "mtime",
@ -13,18 +13,18 @@ local default_config = {
-- Buffer-local options to use for oil buffers
buf_options = {
buflisted = false,
bufhidden = "hide",
bufhidden = 'hide',
},
-- Window-local options to use for oil buffers
win_options = {
wrap = false,
signcolumn = "no",
signcolumn = 'no',
cursorcolumn = false,
foldcolumn = "0",
foldcolumn = '0',
spell = false,
list = false,
conceallevel = 3,
concealcursor = "nvic",
concealcursor = 'nvic',
},
-- Send deleted files to the trash instead of permanently deleting them (:help oil-trash)
delete_to_trash = false,
@ -48,7 +48,7 @@ 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",
constrain_cursor = 'editable',
-- Set to true to watch the filesystem for changes and reload oil
watch_for_changes = false,
-- Keymaps in oil buffer. Can be any value that `vim.keymap.set` accepts OR a table of keymap
@ -58,22 +58,22 @@ local default_config = {
-- Set to `false` to remove a keymap
-- See :help oil-actions for a list of all available actions
keymaps = {
["g?"] = { "actions.show_help", mode = "n" },
["<CR>"] = "actions.select",
["<C-s>"] = { "actions.select", opts = { vertical = true } },
["<C-h>"] = { "actions.select", opts = { horizontal = true } },
["<C-t>"] = { "actions.select", opts = { tab = true } },
["<C-p>"] = "actions.preview",
["<C-c>"] = { "actions.close", mode = "n" },
["<C-l>"] = "actions.refresh",
["-"] = { "actions.parent", mode = "n" },
["_"] = { "actions.open_cwd", mode = "n" },
["`"] = { "actions.cd", mode = "n" },
["g~"] = { "actions.cd", opts = { scope = "tab" }, mode = "n" },
["gs"] = { "actions.change_sort", mode = "n" },
["gx"] = "actions.open_external",
["g."] = { "actions.toggle_hidden", mode = "n" },
["g\\"] = { "actions.toggle_trash", mode = "n" },
['g?'] = { 'actions.show_help', mode = 'n' },
['<CR>'] = 'actions.select',
['<C-s>'] = { 'actions.select', opts = { vertical = true } },
['<C-h>'] = { 'actions.select', opts = { horizontal = true } },
['<C-t>'] = { 'actions.select', opts = { tab = true } },
['<C-p>'] = 'actions.preview',
['<C-c>'] = { 'actions.close', mode = 'n' },
['<C-l>'] = 'actions.refresh',
['-'] = { 'actions.parent', mode = 'n' },
['_'] = { 'actions.open_cwd', mode = 'n' },
['`'] = { 'actions.cd', mode = 'n' },
['g~'] = { 'actions.cd', opts = { scope = 'tab' }, mode = 'n' },
['gs'] = { 'actions.change_sort', mode = 'n' },
['gx'] = 'actions.open_external',
['g.'] = { 'actions.toggle_hidden', mode = 'n' },
['g\\'] = { 'actions.toggle_trash', mode = 'n' },
},
-- Set to false to disable all of the above keymaps
use_default_keymaps = true,
@ -82,7 +82,7 @@ local default_config = {
show_hidden = false,
-- This function defines what is considered a "hidden" file
is_hidden_file = function(name, bufnr)
local m = name:match("^%.")
local m = name:match('^%.')
return m ~= nil
end,
-- This function defines what will never be shown, even when `show_hidden` is set
@ -91,14 +91,14 @@ local default_config = {
end,
-- Sort file names with numbers in a more intuitive order for humans.
-- Can be "fast", true, or false. "fast" will turn it off for large directories.
natural_order = "fast",
natural_order = 'fast',
-- Sort file and directory names case insensitive
case_insensitive = false,
sort = {
-- sort order can be "asc" or "desc"
-- see :help oil-columns to see which columns are sortable
{ "type", "asc" },
{ "name", "asc" },
{ 'type', 'asc' },
{ 'name', 'asc' },
},
-- Customize the highlight group for the file name
highlight_filename = function(entry, is_hidden, is_link_target, is_link_orphan)
@ -138,7 +138,7 @@ local default_config = {
-- optionally override the oil buffers window title with custom function: fun(winid: integer): string
get_win_title = nil,
-- preview_split: Split direction: "auto", "left", "right", "above", "below".
preview_split = "auto",
preview_split = 'auto',
-- This is the config that will be passed to nvim_open_win.
-- Change values here to customize the layout
override = function(conf)
@ -150,7 +150,7 @@ local default_config = {
-- Whether the preview window is automatically updated when the cursor is moved
update_on_cursor_moved = true,
-- How to open the preview window "load"|"scratch"|"fast_scratch"
preview_method = "fast_scratch",
preview_method = 'fast_scratch',
-- A function that returns true to disable preview on a file e.g. to avoid lag
disable_preview = function(filename)
return false
@ -190,7 +190,7 @@ local default_config = {
min_height = { 5, 0.1 },
height = nil,
border = nil,
minimized_border = "none",
minimized_border = 'none',
win_options = {
winblend = 0,
},
@ -211,12 +211,12 @@ local default_config = {
-- not "oil-s3://" on older neovim versions, since it doesn't open buffers correctly with a number
-- in the name
local oil_s3_string = vim.fn.has("nvim-0.12") == 1 and "oil-s3://" or "oil-sss://"
local oil_s3_string = vim.fn.has('nvim-0.12') == 1 and 'oil-s3://' or 'oil-sss://'
default_config.adapters = {
["oil://"] = "files",
["oil-ssh://"] = "ssh",
[oil_s3_string] = "s3",
["oil-trash://"] = "trash",
['oil://'] = 'files',
['oil-ssh://'] = 'ssh',
[oil_s3_string] = 's3',
['oil-trash://'] = 'trash',
}
default_config.adapter_aliases = {}
-- We want the function in the default config for documentation generation, but if we nil it out
@ -408,7 +408,7 @@ local M = {}
M.setup = function(opts)
opts = opts or vim.g.oil or {}
local new_conf = vim.tbl_deep_extend("keep", opts, default_config)
local new_conf = vim.tbl_deep_extend('keep', opts, default_config)
if not new_conf.use_default_keymaps then
new_conf.keymaps = opts.keymaps or {}
elseif opts.keymaps then
@ -429,19 +429,19 @@ M.setup = function(opts)
end
-- Backwards compatibility for old versions that don't support winborder
if vim.fn.has("nvim-0.11") == 0 then
new_conf = vim.tbl_deep_extend("keep", new_conf, {
float = { border = "rounded" },
confirmation = { border = "rounded" },
progress = { border = "rounded" },
ssh = { border = "rounded" },
keymaps_help = { border = "rounded" },
if vim.fn.has('nvim-0.11') == 0 then
new_conf = vim.tbl_deep_extend('keep', new_conf, {
float = { border = 'rounded' },
confirmation = { border = 'rounded' },
progress = { border = 'rounded' },
ssh = { border = 'rounded' },
keymaps_help = { border = 'rounded' },
})
end
-- Backwards compatibility. We renamed the 'preview' window config to be called 'confirmation'.
if opts.preview and not opts.confirmation then
new_conf.confirmation = vim.tbl_deep_extend("keep", opts.preview, default_config.confirmation)
new_conf.confirmation = vim.tbl_deep_extend('keep', opts.preview, default_config.confirmation)
end
-- Backwards compatibility. We renamed the 'preview' config to 'preview_win'
if opts.preview and opts.preview.update_on_cursor_moved ~= nil then
@ -452,7 +452,7 @@ M.setup = function(opts)
new_conf.lsp_file_methods.autosave_changes = new_conf.lsp_rename_autosave
new_conf.lsp_rename_autosave = nil
vim.notify_once(
"oil config value lsp_rename_autosave has moved to lsp_file_methods.autosave_changes.\nCompatibility will be removed on 2024-09-01.",
'oil config value lsp_rename_autosave has moved to lsp_file_methods.autosave_changes.\nCompatibility will be removed on 2024-09-01.',
vim.log.levels.WARN
)
end
@ -479,10 +479,10 @@ M.get_adapter_by_scheme = function(scheme)
if not scheme then
return nil
end
if not vim.endswith(scheme, "://") then
local pieces = vim.split(scheme, "://", { plain = true })
if not vim.endswith(scheme, '://') then
local pieces = vim.split(scheme, '://', { plain = true })
if #pieces <= 2 then
scheme = pieces[1] .. "://"
scheme = pieces[1] .. '://'
else
error(string.format("Malformed url: '%s'", scheme))
end
@ -494,7 +494,7 @@ M.get_adapter_by_scheme = function(scheme)
return nil
end
local ok
ok, adapter = pcall(require, string.format("oil.adapters.%s", name))
ok, adapter = pcall(require, string.format('oil.adapters.%s', name))
if ok then
adapter.name = name
M._adapter_by_scheme[scheme] = adapter

View file

@ -1,17 +1,17 @@
local log = require("oil.log")
local log = require('oil.log')
local M = {}
local uv = vim.uv or vim.loop
---@type boolean
M.is_windows = uv.os_uname().version:match("Windows")
M.is_windows = uv.os_uname().version:match('Windows')
M.is_mac = uv.os_uname().sysname == "Darwin"
M.is_mac = uv.os_uname().sysname == 'Darwin'
M.is_linux = not M.is_windows and not M.is_mac
---@type string
M.sep = M.is_windows and "\\" or "/"
M.sep = M.is_windows and '\\' or '/'
---@param ... string
M.join = function(...)
@ -23,15 +23,15 @@ end
---@return boolean
M.is_absolute = function(dir)
if M.is_windows then
return dir:match("^%a:\\")
return dir:match('^%a:\\')
else
return vim.startswith(dir, "/")
return vim.startswith(dir, '/')
end
end
M.abspath = function(path)
if not M.is_absolute(path) then
path = vim.fn.fnamemodify(path, ":p")
path = vim.fn.fnamemodify(path, ':p')
end
return path
end
@ -40,11 +40,11 @@ end
---@param mode? integer File mode in decimal (default 420 = 0644)
---@param cb fun(err: nil|string)
M.touch = function(path, mode, cb)
if type(mode) == "function" then
if type(mode) == 'function' then
cb = mode
mode = 420
end
uv.fs_open(path, "a", mode or 420, function(err, fd)
uv.fs_open(path, 'a', mode or 420, function(err, fd)
if err then
cb(err)
else
@ -59,12 +59,12 @@ end
---@param candidate string
---@return boolean
M.is_subpath = function(root, candidate)
if candidate == "" then
if candidate == '' then
return false
end
root = vim.fs.normalize(M.abspath(root))
-- Trim trailing "/" from the root
if root:find("/", -1) then
if root:find('/', -1) then
root = root:sub(1, -2)
end
candidate = vim.fs.normalize(M.abspath(candidate))
@ -80,8 +80,8 @@ M.is_subpath = function(root, candidate)
return false
end
local candidate_starts_with_sep = candidate:find("/", root:len() + 1, true) == root:len() + 1
local root_ends_with_sep = root:find("/", root:len(), true) == root:len()
local candidate_starts_with_sep = candidate:find('/', root:len() + 1, true) == root:len() + 1
local root_ends_with_sep = root:find('/', root:len(), true) == root:len()
return candidate_starts_with_sep or root_ends_with_sep
end
@ -90,15 +90,15 @@ end
---@return string
M.posix_to_os_path = function(path)
if M.is_windows then
if vim.startswith(path, "/") then
local drive = path:match("^/(%a+)")
if vim.startswith(path, '/') then
local drive = path:match('^/(%a+)')
if not drive then
return path
end
local rem = path:sub(drive:len() + 2)
return string.format("%s:%s", drive, rem:gsub("/", "\\"))
return string.format('%s:%s', drive, rem:gsub('/', '\\'))
else
local newpath = path:gsub("/", "\\")
local newpath = path:gsub('/', '\\')
return newpath
end
else
@ -111,10 +111,10 @@ end
M.os_to_posix_path = function(path)
if M.is_windows then
if M.is_absolute(path) then
local drive, rem = path:match("^([^:]+):\\(.*)$")
return string.format("/%s/%s", drive:upper(), rem:gsub("\\", "/"))
local drive, rem = path:match('^([^:]+):\\(.*)$')
return string.format('/%s/%s', drive:upper(), rem:gsub('\\', '/'))
else
local newpath = path:gsub("\\", "/")
local newpath = path:gsub('\\', '/')
return newpath
end
else
@ -135,16 +135,16 @@ M.shorten_path = function(path, relative_to)
if M.is_subpath(relative_to, path) then
local idx = relative_to:len() + 1
-- Trim the dividing slash if it's not included in relative_to
if not vim.endswith(relative_to, "/") and not vim.endswith(relative_to, "\\") then
if not vim.endswith(relative_to, '/') and not vim.endswith(relative_to, '\\') then
idx = idx + 1
end
relpath = path:sub(idx)
if relpath == "" then
relpath = "."
if relpath == '' then
relpath = '.'
end
end
if M.is_subpath(home_dir, path) then
local homepath = "~" .. path:sub(home_dir:len() + 1)
local homepath = '~' .. path:sub(home_dir:len() + 1)
if not relpath or homepath:len() < relpath:len() then
return homepath
end
@ -156,13 +156,13 @@ end
---@param mode? integer
M.mkdirp = function(dir, mode)
mode = mode or 493
local mod = ""
local mod = ''
local path = dir
while vim.fn.isdirectory(path) == 0 do
mod = mod .. ":h"
mod = mod .. ':h'
path = vim.fn.fnamemodify(dir, mod)
end
while mod ~= "" do
while mod ~= '' do
mod = mod:sub(3)
path = vim.fn.fnamemodify(dir, mod)
uv.fs_mkdir(path, mode)
@ -209,7 +209,7 @@ end
---@param path string
---@param cb fun(err: nil|string)
M.recursive_delete = function(entry_type, path, cb)
if entry_type ~= "directory" then
if entry_type ~= 'directory' then
return uv.fs_unlink(path, cb)
end
---@diagnostic disable-next-line: param-type-mismatch, discard-returns
@ -271,13 +271,13 @@ local move_undofile = vim.schedule_wrap(function(src_path, dest_path, copy)
if copy then
uv.fs_copyfile(src_path, dest_path, function(err)
if err then
log.warn("Error copying undofile %s: %s", undofile, err)
log.warn('Error copying undofile %s: %s', undofile, err)
end
end)
else
uv.fs_rename(undofile, dest_undofile, function(err)
if err then
log.warn("Error moving undofile %s: %s", undofile, err)
log.warn('Error moving undofile %s: %s', undofile, err)
end
end)
end
@ -290,7 +290,7 @@ end)
---@param dest_path string
---@param cb fun(err: nil|string)
M.recursive_copy = function(entry_type, src_path, dest_path, cb)
if entry_type == "link" then
if entry_type == 'link' then
uv.fs_readlink(src_path, function(link_err, link)
if link_err then
return cb(link_err)
@ -300,7 +300,7 @@ M.recursive_copy = function(entry_type, src_path, dest_path, cb)
end)
return
end
if entry_type ~= "directory" then
if entry_type ~= 'directory' then
uv.fs_copyfile(src_path, dest_path, { excl = true }, cb)
move_undofile(src_path, dest_path, true)
return
@ -374,7 +374,7 @@ M.recursive_move = function(entry_type, src_path, dest_path, cb)
end
end)
else
if entry_type ~= "directory" then
if entry_type ~= 'directory' then
move_undofile(src_path, dest_path, false)
end
cb()

View file

@ -1,12 +1,12 @@
-- integration with git operations
local fs = require("oil.fs")
local fs = require('oil.fs')
local M = {}
---@param path string
---@return string|nil
M.get_root = function(path)
local git_dir = vim.fs.find(".git", { upward = true, path = path })[1]
local git_dir = vim.fs.find('.git', { upward = true, path = path })[1]
if git_dir then
return vim.fs.dirname(git_dir)
else
@ -22,16 +22,16 @@ M.add = function(path, cb)
return cb()
end
local stderr = ""
local jid = vim.fn.jobstart({ "git", "add", path }, {
local stderr = ''
local jid = vim.fn.jobstart({ 'git', 'add', path }, {
cwd = root,
stderr_buffered = true,
on_stderr = function(_, data)
stderr = table.concat(data, "\n")
stderr = table.concat(data, '\n')
end,
on_exit = function(_, code)
if code ~= 0 then
cb("Error in git add: " .. stderr)
cb('Error in git add: ' .. stderr)
else
cb()
end
@ -50,12 +50,12 @@ M.rm = function(path, cb)
return cb()
end
local stderr = ""
local jid = vim.fn.jobstart({ "git", "rm", "-r", path }, {
local stderr = ''
local jid = vim.fn.jobstart({ 'git', 'rm', '-r', path }, {
cwd = root,
stderr_buffered = true,
on_stderr = function(_, data)
stderr = table.concat(data, "\n")
stderr = table.concat(data, '\n')
end,
on_exit = function(_, code)
if code ~= 0 then
@ -63,7 +63,7 @@ M.rm = function(path, cb)
if stderr:match("^fatal: pathspec '.*' did not match any files$") then
cb()
else
cb("Error in git rm: " .. stderr)
cb('Error in git rm: ' .. stderr)
end
else
cb()
@ -86,23 +86,23 @@ M.mv = function(entry_type, src_path, dest_path, cb)
return
end
local stderr = ""
local jid = vim.fn.jobstart({ "git", "mv", src_path, dest_path }, {
local stderr = ''
local jid = vim.fn.jobstart({ 'git', 'mv', src_path, dest_path }, {
cwd = src_git,
stderr_buffered = true,
on_stderr = function(_, data)
stderr = table.concat(data, "\n")
stderr = table.concat(data, '\n')
end,
on_exit = function(_, code)
if code ~= 0 then
stderr = vim.trim(stderr)
if
stderr:match("^fatal: not under version control")
or stderr:match("^fatal: source directory is empty")
stderr:match('^fatal: not under version control')
or stderr:match('^fatal: source directory is empty')
then
fs.recursive_move(entry_type, src_path, dest_path, cb)
else
cb("Error in git mv: " .. stderr)
cb('Error in git mv: ' .. stderr)
end
else
cb()

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
local actions = require("oil.actions")
local config = require("oil.config")
local layout = require("oil.layout")
local util = require("oil.util")
local actions = require('oil.actions')
local config = require('oil.config')
local layout = require('oil.layout')
local util = require('oil.util')
local M = {}
---@param rhs string|table|fun()
@ -9,14 +9,14 @@ local M = {}
---@return table opts
---@return string|nil mode
local function resolve(rhs)
if type(rhs) == "string" and vim.startswith(rhs, "actions.") then
local action_name = vim.split(rhs, ".", { plain = true })[2]
if type(rhs) == 'string' and vim.startswith(rhs, 'actions.') then
local action_name = vim.split(rhs, '.', { plain = true })[2]
local action = actions[action_name]
if not action then
vim.notify("[oil.nvim] Unknown action name: " .. action_name, vim.log.levels.ERROR)
vim.notify('[oil.nvim] Unknown action name: ' .. action_name, vim.log.levels.ERROR)
end
return resolve(action)
elseif type(rhs) == "table" then
elseif type(rhs) == 'table' then
local opts = vim.deepcopy(rhs)
-- We support passing in a `callback` key, or using the 1 index as the rhs of the keymap
local callback, parent_opts = resolve(opts.callback or opts[1])
@ -25,17 +25,17 @@ local function resolve(rhs)
if parent_opts.desc and not opts.desc then
if opts.opts then
opts.desc =
string.format("%s %s", parent_opts.desc, vim.inspect(opts.opts):gsub("%s+", " "))
string.format('%s %s', parent_opts.desc, vim.inspect(opts.opts):gsub('%s+', ' '))
else
opts.desc = parent_opts.desc
end
end
local mode = opts.mode
if type(rhs.callback) == "string" then
if type(rhs.callback) == 'string' then
local action_opts, action_mode
callback, action_opts, action_mode = resolve(rhs.callback)
opts = vim.tbl_extend("keep", opts, action_opts)
opts = vim.tbl_extend('keep', opts, action_opts)
mode = mode or action_mode
end
@ -46,7 +46,7 @@ local function resolve(rhs)
opts.deprecated = nil
opts.parameters = nil
if opts.opts and type(callback) == "function" then
if opts.opts and type(callback) == 'function' then
local callback_args = opts.opts
opts.opts = nil
local orig_callback = callback
@ -68,7 +68,7 @@ M.set_keymaps = function(keymaps, bufnr)
for k, v in pairs(keymaps) do
local rhs, opts, mode = resolve(v)
if rhs then
vim.keymap.set(mode or "", k, rhs, vim.tbl_extend("keep", { buffer = bufnr }, opts))
vim.keymap.set(mode or '', k, rhs, vim.tbl_extend('keep', { buffer = bufnr }, opts))
end
end
end
@ -95,9 +95,9 @@ M.show_help = function(keymaps)
local all_lhs = lhs_to_all_lhs[k]
if all_lhs then
local _, opts = resolve(rhs)
local keystr = table.concat(all_lhs, "/")
local keystr = table.concat(all_lhs, '/')
max_lhs = math.max(max_lhs, vim.api.nvim_strwidth(keystr))
table.insert(keymap_entries, { str = keystr, all_lhs = all_lhs, desc = opts.desc or "" })
table.insert(keymap_entries, { str = keystr, all_lhs = all_lhs, desc = opts.desc or '' })
end
end
table.sort(keymap_entries, function(a, b)
@ -108,20 +108,20 @@ M.show_help = function(keymaps)
local highlights = {}
local max_line = 1
for _, entry in ipairs(keymap_entries) do
local line = string.format(" %s %s", util.pad_align(entry.str, max_lhs, "left"), entry.desc)
local line = string.format(' %s %s', util.pad_align(entry.str, max_lhs, 'left'), entry.desc)
max_line = math.max(max_line, vim.api.nvim_strwidth(line))
table.insert(lines, line)
local start = 1
for _, key in ipairs(entry.all_lhs) do
local keywidth = vim.api.nvim_strwidth(key)
table.insert(highlights, { "Special", #lines, start, start + keywidth })
table.insert(highlights, { 'Special', #lines, start, start + keywidth })
start = start + keywidth + 1
end
end
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
local ns = vim.api.nvim_create_namespace("Oil")
local ns = vim.api.nvim_create_namespace('Oil')
for _, hl in ipairs(highlights) do
local hl_group, lnum, start_col, end_col = unpack(hl)
vim.api.nvim_buf_set_extmark(bufnr, ns, lnum - 1, start_col, {
@ -129,21 +129,21 @@ M.show_help = function(keymaps)
hl_group = hl_group,
})
end
vim.keymap.set("n", "q", "<cmd>close<CR>", { buffer = bufnr })
vim.keymap.set("n", "<c-c>", "<cmd>close<CR>", { buffer = bufnr })
vim.keymap.set('n', 'q', '<cmd>close<CR>', { buffer = bufnr })
vim.keymap.set('n', '<c-c>', '<cmd>close<CR>', { buffer = bufnr })
vim.bo[bufnr].modifiable = false
vim.bo[bufnr].bufhidden = "wipe"
vim.bo[bufnr].bufhidden = 'wipe'
local editor_width = vim.o.columns
local editor_height = layout.get_editor_height()
local winid = vim.api.nvim_open_win(bufnr, true, {
relative = "editor",
relative = 'editor',
row = math.max(0, (editor_height - #lines) / 2),
col = math.max(0, (editor_width - max_line - 1) / 2),
width = math.min(editor_width, max_line + 1),
height = math.min(editor_height, #lines),
zindex = 150,
style = "minimal",
style = 'minimal',
border = config.keymaps_help.border,
})
local function close()
@ -151,13 +151,13 @@ M.show_help = function(keymaps)
vim.api.nvim_win_close(winid, true)
end
end
vim.api.nvim_create_autocmd("BufLeave", {
vim.api.nvim_create_autocmd('BufLeave', {
callback = close,
once = true,
nested = true,
buffer = bufnr,
})
vim.api.nvim_create_autocmd("WinLeave", {
vim.api.nvim_create_autocmd('WinLeave', {
callback = close,
once = true,
nested = true,

View file

@ -43,7 +43,7 @@ local function calc_list(values, max_value, aggregator, limit)
local ret = limit
if not max_value or not values then
return nil
elseif type(values) == "table" then
elseif type(values) == 'table' then
for _, v in ipairs(values) do
ret = aggregator(ret, calc_float(v, max_value))
end
@ -106,12 +106,12 @@ end
---@return vim.api.keyset.win_config
M.get_fullscreen_win_opts = function()
local config = require("oil.config")
local config = require('oil.config')
local total_width = M.get_editor_width()
local total_height = M.get_editor_height()
local width = total_width - 2 * config.float.padding
if config.float.border ~= "none" then
if config.float.border ~= 'none' then
width = width - 2 -- The border consumes 1 col on each side
end
if config.float.max_width > 0 then
@ -127,7 +127,7 @@ M.get_fullscreen_win_opts = function()
local col = math.floor((total_width - width) / 2) - 1 -- adjust for border width
local win_opts = {
relative = "editor",
relative = 'editor',
width = width,
height = height,
row = row,
@ -144,8 +144,8 @@ end
---@return oil.WinLayout root_dim New dimensions of the original window
---@return oil.WinLayout new_dim New dimensions of the new window
M.split_window = function(winid, direction, gap)
if direction == "auto" then
direction = vim.o.splitright and "right" or "left"
if direction == 'auto' then
direction = vim.o.splitright and 'right' or 'left'
end
local float_config = vim.api.nvim_win_get_config(winid)
@ -156,14 +156,14 @@ M.split_window = function(winid, direction, gap)
col = float_config.col,
row = float_config.row,
}
if vim.fn.has("nvim-0.10") == 0 then
if vim.fn.has('nvim-0.10') == 0 then
-- read https://github.com/neovim/neovim/issues/24430 for more infos.
dim_root.col = float_config.col[vim.val_idx]
dim_root.row = float_config.row[vim.val_idx]
end
local dim_new = vim.deepcopy(dim_root)
if direction == "left" or direction == "right" then
if direction == 'left' or direction == 'right' then
dim_new.width = math.floor(float_config.width / 2) - math.ceil(gap / 2)
dim_root.width = dim_new.width
else
@ -171,13 +171,13 @@ M.split_window = function(winid, direction, gap)
dim_root.height = dim_new.height
end
if direction == "left" then
if direction == 'left' then
dim_root.col = dim_root.col + dim_root.width + gap
elseif direction == "right" then
elseif direction == 'right' then
dim_new.col = dim_new.col + dim_new.width + gap
elseif direction == "above" then
elseif direction == 'above' then
dim_root.row = dim_root.row + dim_root.height + gap
elseif direction == "below" then
elseif direction == 'below' then
dim_new.row = dim_new.row + dim_new.height + gap
end

View file

@ -1,4 +1,4 @@
local util = require("oil.util")
local util = require('oil.util')
local M = {}
local timers = {}
@ -12,14 +12,14 @@ M.is_loading = function(bufnr)
end
local spinners = {
dots = { "", "", "", "", "", "", "", "", "", "" },
dots = { '', '', '', '', '', '', '', '', '', '' },
}
---@param name_or_frames string|string[]
---@return fun(): string
M.get_iter = function(name_or_frames)
local frames
if type(name_or_frames) == "string" then
if type(name_or_frames) == 'string' then
frames = spinners[name_or_frames]
if not frames then
error(string.format("Unrecognized spinner: '%s'", name_or_frames))
@ -35,26 +35,26 @@ M.get_iter = function(name_or_frames)
end
M.get_bar_iter = function(opts)
opts = vim.tbl_deep_extend("keep", opts or {}, {
opts = vim.tbl_deep_extend('keep', opts or {}, {
bar_size = 3,
width = 20,
})
local i = 0
return function()
local chars = { "[" }
local chars = { '[' }
for _ = 1, opts.width - 2 do
table.insert(chars, " ")
table.insert(chars, ' ')
end
table.insert(chars, "]")
table.insert(chars, ']')
for j = i - opts.bar_size, i do
if j > 1 and j < opts.width then
chars[j] = "="
chars[j] = '='
end
end
i = (i + 1) % (opts.width + opts.bar_size)
return table.concat(chars, "")
return table.concat(chars, '')
end
end
@ -75,7 +75,7 @@ M.set_loading = function(bufnr, is_loading)
return
end
local lines =
{ util.pad_align("Loading", math.floor(width / 2) - 3, "right"), bar_iter() }
{ util.pad_align('Loading', math.floor(width / 2) - 3, 'right'), bar_iter() }
util.render_text(bufnr, lines)
end)
)

View file

@ -11,14 +11,14 @@ Log.level = vim.log.levels.WARN
---@return string
Log.get_logfile = function()
local fs = require("oil.fs")
local fs = require('oil.fs')
local ok, stdpath = pcall(vim.fn.stdpath, "log")
local ok, stdpath = pcall(vim.fn.stdpath, 'log')
if not ok then
stdpath = vim.fn.stdpath("cache")
stdpath = vim.fn.stdpath('cache')
end
assert(type(stdpath) == "string")
return fs.join(stdpath, "oil.log")
assert(type(stdpath) == 'string')
return fs.join(stdpath, 'oil.log')
end
---@param level integer
@ -29,19 +29,19 @@ local function format(level, msg, ...)
local args = vim.F.pack_len(...)
for i = 1, args.n do
local v = args[i]
if type(v) == "table" then
if type(v) == 'table' then
args[i] = vim.inspect(v)
elseif v == nil then
args[i] = "nil"
args[i] = 'nil'
end
end
local ok, text = pcall(string.format, msg, vim.F.unpack_len(args))
-- TODO figure out how to get formatted time inside luv callback
-- local timestr = vim.fn.strftime("%Y-%m-%d %H:%M:%S")
local timestr = ""
local timestr = ''
if ok then
local str_level = levels_reverse[level]
return string.format("%s[%s] %s", timestr, str_level, text)
return string.format('%s[%s] %s', timestr, str_level, text)
else
return string.format(
"%s[ERROR] error formatting log line: '%s' args %s",
@ -67,22 +67,22 @@ local function initialize()
local stat = uv.fs_stat(filepath)
if stat and stat.size > 10 * 1024 * 1024 then
local backup = filepath .. ".1"
local backup = filepath .. '.1'
uv.fs_unlink(backup)
uv.fs_rename(filepath, backup)
end
local parent = vim.fs.dirname(filepath)
require("oil.fs").mkdirp(parent)
require('oil.fs').mkdirp(parent)
local logfile, openerr = io.open(filepath, "a+")
local logfile, openerr = io.open(filepath, 'a+')
if not logfile then
local err_msg = string.format("Failed to open oil.nvim log file: %s", openerr)
local err_msg = string.format('Failed to open oil.nvim log file: %s', openerr)
vim.notify(err_msg, vim.log.levels.ERROR)
else
write = function(line)
logfile:write(line)
logfile:write("\n")
logfile:write('\n')
logfile:flush()
end
end

View file

@ -1,7 +1,7 @@
local config = require("oil.config")
local fs = require("oil.fs")
local util = require("oil.util")
local workspace = require("oil.lsp.workspace")
local config = require('oil.config')
local fs = require('oil.fs')
local util = require('oil.util')
local workspace = require('oil.lsp.workspace')
local M = {}
@ -12,7 +12,7 @@ M.will_perform_file_operations = function(actions)
local creates = {}
local deletes = {}
for _, action in ipairs(actions) do
if action.type == "move" then
if action.type == 'move' then
local src_scheme, src_path = util.parse_url(action.src_url)
assert(src_path)
local src_adapter = assert(config.get_adapter_by_scheme(src_scheme))
@ -20,32 +20,32 @@ M.will_perform_file_operations = function(actions)
local dest_adapter = assert(config.get_adapter_by_scheme(dest_scheme))
src_path = fs.posix_to_os_path(src_path)
dest_path = fs.posix_to_os_path(assert(dest_path))
if src_adapter.name == "files" and dest_adapter.name == "files" then
if src_adapter.name == 'files' and dest_adapter.name == 'files' then
moves[src_path] = dest_path
elseif src_adapter.name == "files" then
elseif src_adapter.name == 'files' then
table.insert(deletes, src_path)
elseif dest_adapter.name == "files" then
elseif dest_adapter.name == 'files' then
table.insert(creates, src_path)
end
elseif action.type == "create" then
elseif action.type == 'create' then
local scheme, path = util.parse_url(action.url)
path = fs.posix_to_os_path(assert(path))
local adapter = assert(config.get_adapter_by_scheme(scheme))
if adapter.name == "files" then
if adapter.name == 'files' then
table.insert(creates, path)
end
elseif action.type == "delete" then
elseif action.type == 'delete' then
local scheme, path = util.parse_url(action.url)
path = fs.posix_to_os_path(assert(path))
local adapter = assert(config.get_adapter_by_scheme(scheme))
if adapter.name == "files" then
if adapter.name == 'files' then
table.insert(deletes, path)
end
elseif action.type == "copy" then
elseif action.type == 'copy' then
local scheme, path = util.parse_url(action.dest_url)
path = fs.posix_to_os_path(assert(path))
local adapter = assert(config.get_adapter_by_scheme(scheme))
if adapter.name == "files" then
if adapter.name == 'files' then
table.insert(creates, path)
end
end
@ -84,7 +84,7 @@ M.will_perform_file_operations = function(actions)
accum(workspace.will_rename_files(moves, { timeout_ms = timeout_ms }))
if final_err then
vim.notify(
string.format("[lsp] file operation error: %s", vim.inspect(final_err)),
string.format('[lsp] file operation error: %s', vim.inspect(final_err)),
vim.log.levels.WARN
)
end
@ -102,7 +102,7 @@ M.will_perform_file_operations = function(actions)
local bufnr = vim.uri_to_bufnr(uri)
local was_open = buf_was_modified[bufnr] ~= nil
local was_modified = buf_was_modified[bufnr]
local should_save = autosave == true or (autosave == "unmodified" and not was_modified)
local should_save = autosave == true or (autosave == 'unmodified' and not was_modified)
-- Autosave changed buffers if they were not modified before
if should_save then
vim.api.nvim_buf_call(bufnr, function()

View file

@ -1,13 +1,13 @@
local fs = require("oil.fs")
local ms = require("vim.lsp.protocol").Methods
if vim.fn.has("nvim-0.10") == 0 then
local fs = require('oil.fs')
local ms = require('vim.lsp.protocol').Methods
if vim.fn.has('nvim-0.10') == 0 then
ms = {
workspace_willCreateFiles = "workspace/willCreateFiles",
workspace_didCreateFiles = "workspace/didCreateFiles",
workspace_willDeleteFiles = "workspace/willDeleteFiles",
workspace_didDeleteFiles = "workspace/didDeleteFiles",
workspace_willRenameFiles = "workspace/willRenameFiles",
workspace_didRenameFiles = "workspace/didRenameFiles",
workspace_willCreateFiles = 'workspace/willCreateFiles',
workspace_didCreateFiles = 'workspace/didCreateFiles',
workspace_willDeleteFiles = 'workspace/willDeleteFiles',
workspace_didDeleteFiles = 'workspace/didDeleteFiles',
workspace_willRenameFiles = 'workspace/willRenameFiles',
workspace_didRenameFiles = 'workspace/didRenameFiles',
}
end
@ -16,7 +16,7 @@ local M = {}
---@param method string
---@return vim.lsp.Client[]
local function get_clients(method)
if vim.fn.has("nvim-0.10") == 1 then
if vim.fn.has('nvim-0.10') == 1 then
return vim.lsp.get_clients({ method = method })
else
---@diagnostic disable-next-line: deprecated
@ -32,7 +32,7 @@ end
---@return boolean
local function match_glob(glob, path)
-- nvim-0.10 will have vim.glob.to_lpeg, so this will be a LPeg pattern
if type(glob) ~= "string" then
if type(glob) ~= 'string' then
return glob:match(path) ~= nil
end
@ -59,7 +59,7 @@ local function get_matching_paths(client, filters, paths)
local match_fns = {}
for _, filter in ipairs(filters) do
if filter.scheme == nil or filter.scheme == "file" then
if filter.scheme == nil or filter.scheme == 'file' then
local pattern = filter.pattern
local glob = pattern.glob
local ignore_case = pattern.options and pattern.options.ignoreCase
@ -69,32 +69,32 @@ local function get_matching_paths(client, filters, paths)
-- Some language servers use forward slashes as path separators on Windows (LuaLS)
-- We no longer need this after 0.12: https://github.com/neovim/neovim/commit/322a6d305d088420b23071c227af07b7c1beb41a
if vim.fn.has("nvim-0.12") == 0 and fs.is_windows then
glob = glob:gsub("/", "\\")
if vim.fn.has('nvim-0.12') == 0 and fs.is_windows then
glob = glob:gsub('/', '\\')
end
---@type string|vim.lpeg.Pattern
local glob_to_match = glob
if vim.glob and vim.glob.to_lpeg then
glob = glob:gsub("{(.-)}", function(s)
local patterns = vim.split(s, ",")
glob = glob:gsub('{(.-)}', function(s)
local patterns = vim.split(s, ',')
local filtered = {}
for _, pat in ipairs(patterns) do
if pat ~= "" then
if pat ~= '' then
table.insert(filtered, pat)
end
end
if #filtered == 0 then
return ""
return ''
end
-- HACK around https://github.com/neovim/neovim/issues/28931
-- find alternations and sort them by length to try to match the longest first
if vim.fn.has("nvim-0.11") == 0 then
if vim.fn.has('nvim-0.11') == 0 then
table.sort(filtered, function(a, b)
return a:len() > b:len()
end)
end
return "{" .. table.concat(filtered, ",") .. "}"
return '{' .. table.concat(filtered, ',') .. '}'
end)
glob_to_match = vim.glob.to_lpeg(glob)
@ -102,7 +102,7 @@ local function get_matching_paths(client, filters, paths)
local matches = pattern.matches
table.insert(match_fns, function(path)
local is_dir = vim.fn.isdirectory(path) == 1
if matches and ((matches == "file" and is_dir) or (matches == "folder" and not is_dir)) then
if matches and ((matches == 'file' and is_dir) or (matches == 'folder' and not is_dir)) then
return false
end
@ -163,10 +163,10 @@ local function will_file_operation(method, capability_name, files, options)
for _, client in ipairs(clients) do
local filters = vim.tbl_get(
client.server_capabilities,
"workspace",
"fileOperations",
'workspace',
'fileOperations',
capability_name,
"filters"
'filters'
)
local matching_files = get_matching_paths(client, filters, files)
if matching_files then
@ -178,7 +178,7 @@ local function will_file_operation(method, capability_name, files, options)
end, matching_files),
}
local result, err
if vim.fn.has("nvim-0.11") == 1 then
if vim.fn.has('nvim-0.11') == 1 then
result, err = client:request_sync(method, params, options.timeout_ms or 1000, 0)
else
---@diagnostic disable-next-line: param-type-mismatch
@ -205,10 +205,10 @@ local function did_file_operation(method, capability_name, files)
for _, client in ipairs(clients) do
local filters = vim.tbl_get(
client.server_capabilities,
"workspace",
"fileOperations",
'workspace',
'fileOperations',
capability_name,
"filters"
'filters'
)
local matching_files = get_matching_paths(client, filters, files)
if matching_files then
@ -219,7 +219,7 @@ local function did_file_operation(method, capability_name, files)
}
end, matching_files),
}
if vim.fn.has("nvim-0.11") == 1 then
if vim.fn.has('nvim-0.11') == 1 then
client:notify(method, params)
else
---@diagnostic disable-next-line: param-type-mismatch
@ -239,13 +239,13 @@ end
---@return nil|{edit: lsp.WorkspaceEdit, offset_encoding: string}[]
---@return nil|string|lsp.ResponseError err
function M.will_create_files(files, options)
return will_file_operation(ms.workspace_willCreateFiles, "willCreate", files, options)
return will_file_operation(ms.workspace_willCreateFiles, 'willCreate', files, options)
end
--- Notify the server that files were created from within the client.
---@param files string[] The files and folders that will be created
function M.did_create_files(files)
did_file_operation(ms.workspace_didCreateFiles, "didCreate", files)
did_file_operation(ms.workspace_didCreateFiles, 'didCreate', files)
end
--- Notify the server that the client is about to delete files.
@ -258,13 +258,13 @@ end
---@return nil|{edit: lsp.WorkspaceEdit, offset_encoding: string}[]
---@return nil|string|lsp.ResponseError err
function M.will_delete_files(files, options)
return will_file_operation(ms.workspace_willDeleteFiles, "willDelete", files, options)
return will_file_operation(ms.workspace_willDeleteFiles, 'willDelete', files, options)
end
--- Notify the server that files were deleted from within the client.
---@param files string[] The files and folders that were deleted
function M.did_delete_files(files)
did_file_operation(ms.workspace_didDeleteFiles, "didDelete", files)
did_file_operation(ms.workspace_didDeleteFiles, 'didDelete', files)
end
--- Notify the server that the client is about to rename files.
@ -284,10 +284,10 @@ function M.will_rename_files(files, options)
for _, client in ipairs(clients) do
local filters = vim.tbl_get(
client.server_capabilities,
"workspace",
"fileOperations",
"willRename",
"filters"
'workspace',
'fileOperations',
'willRename',
'filters'
)
local matching_files = get_matching_paths(client, filters, vim.tbl_keys(files))
if matching_files then
@ -300,7 +300,7 @@ function M.will_rename_files(files, options)
end, matching_files),
}
local result, err
if vim.fn.has("nvim-0.11") == 1 then
if vim.fn.has('nvim-0.11') == 1 then
result, err =
client:request_sync(ms.workspace_willRenameFiles, params, options.timeout_ms or 1000, 0)
else
@ -327,7 +327,7 @@ function M.did_rename_files(files)
local clients = get_clients(ms.workspace_didRenameFiles)
for _, client in ipairs(clients) do
local filters =
vim.tbl_get(client.server_capabilities, "workspace", "fileOperations", "didRename", "filters")
vim.tbl_get(client.server_capabilities, 'workspace', 'fileOperations', 'didRename', 'filters')
local matching_files = get_matching_paths(client, filters, vim.tbl_keys(files))
if matching_files then
local params = {
@ -338,7 +338,7 @@ function M.did_rename_files(files)
}
end, matching_files),
}
if vim.fn.has("nvim-0.11") == 1 then
if vim.fn.has('nvim-0.11') == 1 then
client:notify(ms.workspace_didRenameFiles, params)
else
---@diagnostic disable-next-line: param-type-mismatch

View file

@ -1,7 +1,7 @@
local columns = require("oil.columns")
local config = require("oil.config")
local layout = require("oil.layout")
local util = require("oil.util")
local columns = require('oil.columns')
local config = require('oil.config')
local layout = require('oil.layout')
local util = require('oil.util')
local M = {}
---@param actions oil.Action[]
@ -12,17 +12,17 @@ local function is_simple_edit(actions)
local num_move = 0
for _, action in ipairs(actions) do
-- If there are any deletes, it is not a simple edit
if action.type == "delete" then
if action.type == 'delete' then
return false
elseif action.type == "create" then
elseif action.type == 'create' then
num_create = num_create + 1
elseif action.type == "copy" then
elseif action.type == 'copy' then
num_copy = num_copy + 1
-- Cross-adapter copies are not simple
if util.parse_url(action.src_url) ~= util.parse_url(action.dest_url) then
return false
end
elseif action.type == "move" then
elseif action.type == 'move' then
num_move = num_move + 1
-- Cross-adapter moves are not simple
if util.parse_url(action.src_url) ~= util.parse_url(action.dest_url) then
@ -46,10 +46,10 @@ end
---@param lines string[]
local function render_lines(winid, bufnr, lines)
util.render_text(bufnr, lines, {
v_align = "top",
h_align = "left",
v_align = 'top',
h_align = 'left',
winid = winid,
actions = { "[Y]es", "[N]o" },
actions = { '[Y]es', '[N]o' },
})
end
@ -70,48 +70,48 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb)
-- Create the buffer
local bufnr = vim.api.nvim_create_buf(false, true)
vim.bo[bufnr].bufhidden = "wipe"
vim.bo[bufnr].bufhidden = 'wipe'
local lines = {}
local max_line_width = 0
for _, action in ipairs(actions) do
local adapter = util.get_adapter_for_action(action)
local line
if action.type == "change" then
if action.type == 'change' then
---@cast action oil.ChangeAction
line = columns.render_change_action(adapter, action)
else
line = adapter.render_action(action)
end
-- We can't handle lines with newlines in them
line = line:gsub("\n", "")
line = line:gsub('\n', '')
table.insert(lines, line)
local line_width = vim.api.nvim_strwidth(line)
if line_width > max_line_width then
max_line_width = line_width
end
end
table.insert(lines, "")
table.insert(lines, '')
-- Create the floating window
local width, height = layout.calculate_dims(max_line_width, #lines + 1, config.confirmation)
local ok, winid = pcall(vim.api.nvim_open_win, bufnr, true, {
relative = "editor",
relative = 'editor',
width = width,
height = height,
row = math.floor((layout.get_editor_height() - height) / 2),
col = math.floor((layout.get_editor_width() - width) / 2),
zindex = 152, -- render on top of the floating window title
style = "minimal",
style = 'minimal',
border = config.confirmation.border,
})
if not ok then
vim.notify(string.format("Error showing oil preview window: %s", winid), vim.log.levels.ERROR)
vim.notify(string.format('Error showing oil preview window: %s', winid), vim.log.levels.ERROR)
cb(false)
end
vim.bo[bufnr].filetype = "oil_preview"
vim.bo[bufnr].syntax = "oil_preview"
vim.bo[bufnr].filetype = 'oil_preview'
vim.bo[bufnr].syntax = 'oil_preview'
for k, v in pairs(config.confirmation.win_options) do
vim.api.nvim_set_option_value(k, v, { scope = "local", win = winid })
vim.api.nvim_set_option_value(k, v, { scope = 'local', win = winid })
end
render_lines(winid, bufnr, lines)
@ -137,7 +137,7 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb)
end
cancel = make_callback(false)
confirm = make_callback(true)
vim.api.nvim_create_autocmd("BufLeave", {
vim.api.nvim_create_autocmd('BufLeave', {
callback = function()
cancel()
end,
@ -145,7 +145,7 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb)
nested = true,
buffer = bufnr,
})
vim.api.nvim_create_autocmd("WinLeave", {
vim.api.nvim_create_autocmd('WinLeave', {
callback = function()
cancel()
end,
@ -154,12 +154,12 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb)
})
table.insert(
autocmds,
vim.api.nvim_create_autocmd("VimResized", {
vim.api.nvim_create_autocmd('VimResized', {
callback = function()
if vim.api.nvim_win_is_valid(winid) then
width, height = layout.calculate_dims(max_line_width, #lines, config.confirmation)
vim.api.nvim_win_set_config(winid, {
relative = "editor",
relative = 'editor',
width = width,
height = height,
row = math.floor((layout.get_editor_height() - height) / 2),
@ -173,17 +173,17 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb)
)
-- We used to use [C]ancel to cancel, so preserve the old keymap
local cancel_keys = { "n", "N", "c", "C", "q", "<C-c>", "<Esc>" }
local cancel_keys = { 'n', 'N', 'c', 'C', 'q', '<C-c>', '<Esc>' }
for _, cancel_key in ipairs(cancel_keys) do
vim.keymap.set("n", cancel_key, function()
vim.keymap.set('n', cancel_key, function()
cancel()
end, { buffer = bufnr, nowait = true })
end
-- We used to use [O]k to confirm, so preserve the old keymap
local confirm_keys = { "y", "Y", "o", "O" }
local confirm_keys = { 'y', 'Y', 'o', 'O' }
for _, confirm_key in ipairs(confirm_keys) do
vim.keymap.set("n", confirm_key, function()
vim.keymap.set('n', confirm_key, function()
confirm()
end, { buffer = bufnr, nowait = true })
end

View file

@ -1,16 +1,16 @@
local Progress = require("oil.mutator.progress")
local Trie = require("oil.mutator.trie")
local cache = require("oil.cache")
local columns = require("oil.columns")
local config = require("oil.config")
local confirmation = require("oil.mutator.confirmation")
local constants = require("oil.constants")
local fs = require("oil.fs")
local lsp_helpers = require("oil.lsp.helpers")
local oil = require("oil")
local parser = require("oil.mutator.parser")
local util = require("oil.util")
local view = require("oil.view")
local Progress = require('oil.mutator.progress')
local Trie = require('oil.mutator.trie')
local cache = require('oil.cache')
local columns = require('oil.columns')
local config = require('oil.config')
local confirmation = require('oil.mutator.confirmation')
local constants = require('oil.constants')
local fs = require('oil.fs')
local lsp_helpers = require('oil.lsp.helpers')
local oil = require('oil')
local parser = require('oil.mutator.parser')
local util = require('oil.util')
local view = require('oil.view')
local M = {}
local FIELD_NAME = constants.FIELD_NAME
@ -73,7 +73,7 @@ M.create_actions_from_diffs = function(all_diffs)
local function add_action(action)
local adapter = assert(config.get_adapter_by_scheme(action.dest_url or action.url))
if not adapter.filter_action or adapter.filter_action(action) then
if action.type == "create" then
if action.type == 'create' then
if seen_creates[action.url] then
return
else
@ -87,11 +87,11 @@ M.create_actions_from_diffs = function(all_diffs)
for bufnr, diffs in pairs(all_diffs) do
local adapter = util.get_adapter(bufnr, true)
if not adapter then
error("Missing adapter")
error('Missing adapter')
end
local parent_url = vim.api.nvim_buf_get_name(bufnr)
for _, diff in ipairs(diffs) do
if diff.type == "new" then
if diff.type == 'new' then
if diff.id then
local by_id = diff_by_id[diff.id]
---HACK: set the destination on this diff for use later
@ -100,28 +100,28 @@ M.create_actions_from_diffs = function(all_diffs)
table.insert(by_id, diff)
else
-- Parse nested files like foo/bar/baz
local path_sep = fs.is_windows and "[/\\]" or "/"
local path_sep = fs.is_windows and '[/\\]' or '/'
local pieces = vim.split(diff.name, path_sep)
local url = parent_url:gsub("/$", "")
local url = parent_url:gsub('/$', '')
for i, v in ipairs(pieces) do
local is_last = i == #pieces
local entry_type = is_last and diff.entry_type or "directory"
local alternation = v:match("{([^}]+)}")
local entry_type = is_last and diff.entry_type or 'directory'
local alternation = v:match('{([^}]+)}')
if is_last and alternation then
-- Parse alternations like foo.{js,test.js}
for _, alt in ipairs(vim.split(alternation, ",")) do
local alt_url = url .. "/" .. v:gsub("{[^}]+}", alt)
for _, alt in ipairs(vim.split(alternation, ',')) do
local alt_url = url .. '/' .. v:gsub('{[^}]+}', alt)
add_action({
type = "create",
type = 'create',
url = alt_url,
entry_type = entry_type,
link = diff.link,
})
end
else
url = url .. "/" .. v
url = url .. '/' .. v
add_action({
type = "create",
type = 'create',
url = url,
entry_type = entry_type,
link = diff.link,
@ -129,9 +129,9 @@ M.create_actions_from_diffs = function(all_diffs)
end
end
end
elseif diff.type == "change" then
elseif diff.type == 'change' then
add_action({
type = "change",
type = 'change',
url = parent_url .. diff.name,
entry_type = diff.entry_type,
column = diff.column,
@ -151,7 +151,7 @@ M.create_actions_from_diffs = function(all_diffs)
for id, diffs in pairs(diff_by_id) do
local entry = cache.get_entry_by_id(id)
if not entry then
error(string.format("Could not find entry %d", id))
error(string.format('Could not find entry %d', id))
end
---HACK: access the has_delete field on the list-like table of diffs
---@diagnostic disable-next-line: undefined-field
@ -161,7 +161,7 @@ M.create_actions_from_diffs = function(all_diffs)
-- MOVE (+ optional copies) when has both creates and delete
for i, diff in ipairs(diffs) do
add_action({
type = i == #diffs and "move" or "copy",
type = i == #diffs and 'move' or 'copy',
entry_type = entry[FIELD_TYPE],
---HACK: access the dest field we set above
---@diagnostic disable-next-line: undefined-field
@ -172,7 +172,7 @@ M.create_actions_from_diffs = function(all_diffs)
else
-- DELETE when no create
add_action({
type = "delete",
type = 'delete',
entry_type = entry[FIELD_TYPE],
url = cache.get_parent_url(id) .. entry[FIELD_NAME],
})
@ -181,7 +181,7 @@ M.create_actions_from_diffs = function(all_diffs)
-- COPY when create but no delete
for _, diff in ipairs(diffs) do
add_action({
type = "copy",
type = 'copy',
entry_type = entry[FIELD_TYPE],
src_url = cache.get_parent_url(id) .. entry[FIELD_NAME],
---HACK: access the dest field we set above
@ -201,9 +201,9 @@ M.enforce_action_order = function(actions)
local src_trie = Trie.new()
local dest_trie = Trie.new()
for _, action in ipairs(actions) do
if action.type == "delete" or action.type == "change" then
if action.type == 'delete' or action.type == 'change' then
src_trie:insert_action(action.url, action)
elseif action.type == "create" then
elseif action.type == 'create' then
dest_trie:insert_action(action.url, action)
else
dest_trie:insert_action(action.dest_url, action)
@ -223,18 +223,18 @@ M.enforce_action_order = function(actions)
---@param action oil.Action
local function get_deps(action)
local ret = {}
if action.type == "delete" then
if action.type == 'delete' then
src_trie:accum_children_of(action.url, ret)
elseif action.type == "create" then
elseif action.type == 'create' then
-- Finish operating on parents first
-- e.g. NEW /a BEFORE NEW /a/b
dest_trie:accum_first_parents_of(action.url, ret)
-- Process remove path before creating new path
-- e.g. DELETE /a BEFORE NEW /a
src_trie:accum_actions_at(action.url, ret, function(a)
return a.type == "move" or a.type == "delete"
return a.type == 'move' or a.type == 'delete'
end)
elseif action.type == "change" then
elseif action.type == 'change' then
-- Finish operating on parents first
-- e.g. NEW /a BEFORE CHANGE /a/b
dest_trie:accum_first_parents_of(action.url, ret)
@ -244,9 +244,9 @@ M.enforce_action_order = function(actions)
-- Finish copy from operations first
-- e.g. COPY /a -> /b BEFORE CHANGE /a
src_trie:accum_actions_at(action.url, ret, function(entry)
return entry.type == "copy"
return entry.type == 'copy'
end)
elseif action.type == "move" then
elseif action.type == 'move' then
-- Finish operating on parents first
-- e.g. NEW /a BEFORE MOVE /z -> /a/b
dest_trie:accum_first_parents_of(action.dest_url, ret)
@ -260,9 +260,9 @@ M.enforce_action_order = function(actions)
-- Process remove path before moving to new path
-- e.g. MOVE /a -> /b BEFORE MOVE /c -> /a
src_trie:accum_actions_at(action.dest_url, ret, function(a)
return a.type == "move" or a.type == "delete"
return a.type == 'move' or a.type == 'delete'
end)
elseif action.type == "copy" then
elseif action.type == 'copy' then
-- Finish operating on parents first
-- e.g. NEW /a BEFORE COPY /z -> /a/b
dest_trie:accum_first_parents_of(action.dest_url, ret)
@ -272,7 +272,7 @@ M.enforce_action_order = function(actions)
-- Process remove path before copying to new path
-- e.g. MOVE /a -> /b BEFORE COPY /c -> /a
src_trie:accum_actions_at(action.dest_url, ret, function(a)
return a.type == "move" or a.type == "delete"
return a.type == 'move' or a.type == 'delete'
end)
end
return ret
@ -312,24 +312,24 @@ M.enforce_action_order = function(actions)
if selected then
to_remove = selected
else
if loop_action and loop_action.type == "move" then
if loop_action and loop_action.type == 'move' then
-- If this is moving a parent into itself, that's an error
if vim.startswith(loop_action.dest_url, loop_action.src_url) then
error("Detected cycle in desired paths")
error('Detected cycle in desired paths')
end
-- We've detected a move cycle (e.g. MOVE /a -> /b + MOVE /b -> /a)
-- Split one of the moves and retry
local intermediate_url =
string.format("%s__oil_tmp_%05d", loop_action.src_url, math.random(999999))
string.format('%s__oil_tmp_%05d', loop_action.src_url, math.random(999999))
local move_1 = {
type = "move",
type = 'move',
entry_type = loop_action.entry_type,
src_url = loop_action.src_url,
dest_url = intermediate_url,
}
local move_2 = {
type = "move",
type = 'move',
entry_type = loop_action.entry_type,
src_url = intermediate_url,
dest_url = loop_action.dest_url,
@ -340,16 +340,16 @@ M.enforce_action_order = function(actions)
dest_trie:insert_action(move_1.dest_url, move_1)
src_trie:insert_action(move_1.src_url, move_1)
else
error("Detected cycle in desired paths")
error('Detected cycle in desired paths')
end
end
if selected then
if selected.type == "move" or selected.type == "copy" then
if vim.startswith(selected.dest_url, selected.src_url .. "/") then
if selected.type == 'move' or selected.type == 'copy' then
if vim.startswith(selected.dest_url, selected.src_url .. '/') then
error(
string.format(
"Cannot move or copy parent into itself: %s -> %s",
'Cannot move or copy parent into itself: %s -> %s',
selected.src_url,
selected.dest_url
)
@ -360,9 +360,9 @@ M.enforce_action_order = function(actions)
end
if to_remove then
if to_remove.type == "delete" or to_remove.type == "change" then
if to_remove.type == 'delete' or to_remove.type == 'change' then
src_trie:remove_action(to_remove.url, to_remove)
elseif to_remove.type == "create" then
elseif to_remove.type == 'create' then
dest_trie:remove_action(to_remove.url, to_remove)
else
dest_trie:remove_action(to_remove.dest_url, to_remove)
@ -387,8 +387,8 @@ local progress
---@param cb fun(err: nil|string)
M.process_actions = function(actions, cb)
vim.api.nvim_exec_autocmds(
"User",
{ pattern = "OilActionsPre", modeline = false, data = { actions = actions } }
'User',
{ pattern = 'OilActionsPre', modeline = false, data = { actions = actions } }
)
local did_complete = nil
@ -398,14 +398,14 @@ M.process_actions = function(actions, cb)
-- Convert some cross-adapter moves to a copy + delete
for _, action in ipairs(actions) do
if action.type == "move" then
if action.type == 'move' then
local _, cross_action = util.get_adapter_for_action(action)
-- Only do the conversion if the cross-adapter support is "copy"
if cross_action == "copy" then
if cross_action == 'copy' then
---@diagnostic disable-next-line: assign-type-mismatch
action.type = "copy"
action.type = 'copy'
table.insert(actions, {
type = "delete",
type = 'delete',
url = action.src_url,
entry_type = action.entry_type,
})
@ -421,8 +421,8 @@ M.process_actions = function(actions, cb)
progress:close()
progress = nil
vim.api.nvim_exec_autocmds(
"User",
{ pattern = "OilActionsPost", modeline = false, data = { err = err, actions = actions } }
'User',
{ pattern = 'OilActionsPost', modeline = false, data = { err = err, actions = actions } }
)
cb(err)
end
@ -435,7 +435,7 @@ M.process_actions = function(actions, cb)
-- TODO some actions are actually cancelable.
-- We should stop them instead of stopping after the current action
cancel = function()
finish("Canceled")
finish('Canceled')
end,
})
end
@ -472,7 +472,7 @@ M.process_actions = function(actions, cb)
next_action()
end
end)
if action.type == "change" then
if action.type == 'change' then
---@cast action oil.ChangeAction
columns.perform_change_action(adapter, action, callback)
else
@ -502,7 +502,7 @@ M.try_write_changes = function(confirm, cb)
cb = function(_err) end
end
if mutation_in_progress then
cb("Cannot perform mutation when already in progress")
cb('Cannot perform mutation when already in progress')
return
end
local current_buf = vim.api.nvim_get_current_buf()
@ -537,7 +537,7 @@ M.try_write_changes = function(confirm, cb)
mutation_in_progress = false
end
local ns = vim.api.nvim_create_namespace("Oil")
local ns = vim.api.nvim_create_namespace('Oil')
vim.diagnostic.reset(ns)
if not vim.tbl_isempty(all_errors) then
for bufnr, errors in pairs(all_errors) do
@ -564,7 +564,7 @@ M.try_write_changes = function(confirm, cb)
end)
end
unlock()
cb("Error parsing oil buffers")
cb('Error parsing oil buffers')
return
end
@ -572,7 +572,7 @@ M.try_write_changes = function(confirm, cb)
confirmation.show(actions, confirm, function(proceed)
if not proceed then
unlock()
cb("Canceled")
cb('Canceled')
return
end
@ -581,7 +581,7 @@ M.try_write_changes = function(confirm, cb)
vim.schedule_wrap(function(err)
view.unlock_buffers()
if err then
err = string.format("[oil] Error applying actions: %s", err)
err = string.format('[oil] Error applying actions: %s', err)
view.rerender_all_oil_buffers(nil, function()
cb(err)
end)
@ -591,13 +591,13 @@ M.try_write_changes = function(confirm, cb)
-- get the entry under the cursor and make sure the cursor stays on it
view.set_last_cursor(
vim.api.nvim_buf_get_name(0),
vim.split(current_entry.parsed_name or current_entry.name, "/")[1]
vim.split(current_entry.parsed_name or current_entry.name, '/')[1]
)
end
view.rerender_all_oil_buffers(nil, function(render_err)
vim.api.nvim_exec_autocmds(
"User",
{ pattern = "OilMutationComplete", modeline = false }
'User',
{ pattern = 'OilMutationComplete', modeline = false }
)
cb(render_err)
end)

View file

@ -1,10 +1,10 @@
local cache = require("oil.cache")
local columns = require("oil.columns")
local config = require("oil.config")
local constants = require("oil.constants")
local fs = require("oil.fs")
local util = require("oil.util")
local view = require("oil.view")
local cache = require('oil.cache')
local columns = require('oil.columns')
local config = require('oil.config')
local constants = require('oil.constants')
local fs = require('oil.fs')
local util = require('oil.util')
local view = require('oil.view')
local M = {}
local FIELD_ID = constants.FIELD_ID
@ -37,7 +37,7 @@ local FIELD_META = constants.FIELD_META
---@return string
---@return boolean
local function parsedir(name)
local isdir = vim.endswith(name, "/") or (fs.is_windows and vim.endswith(name, "\\"))
local isdir = vim.endswith(name, '/') or (fs.is_windows and vim.endswith(name, '\\'))
if isdir then
name = name:sub(1, name:len() - 1)
end
@ -52,8 +52,8 @@ local function compare_link_target(meta, parsed_entry)
return false
end
-- Make sure we trim off any trailing path slashes from both sources
local meta_name = meta.link:gsub("[/\\]$", "")
local parsed_name = parsed_entry.link_target:gsub("[/\\]$", "")
local meta_name = meta.link:gsub('[/\\]$', '')
local parsed_name = parsed_entry.link_target:gsub('[/\\]$', '')
return meta_name == parsed_name
end
@ -72,9 +72,9 @@ M.parse_line = function(adapter, line, column_defs)
local ret = {}
local ranges = {}
local start = 1
local value, rem = line:match("^/(%d+) (.+)$")
local value, rem = line:match('^/(%d+) (.+)$')
if not value then
return nil, "Malformed ID at start of line"
return nil, 'Malformed ID at start of line'
end
ranges.id = { start, value:len() + 1 }
start = ranges.id[2] + 1
@ -97,7 +97,7 @@ M.parse_line = function(adapter, line, column_defs)
local start_len = string.len(rem)
value, rem = columns.parse_col(adapter, assert(rem), def)
if not rem then
return nil, string.format("Parsing %s failed", name)
return nil, string.format('Parsing %s failed', name)
end
ret[name] = value
range[2] = range[1] + start_len - string.len(rem) - 1
@ -108,10 +108,10 @@ M.parse_line = function(adapter, line, column_defs)
if name then
local isdir
name, isdir = parsedir(vim.trim(name))
if name ~= "" then
if name ~= '' then
ret.name = name
end
ret._type = isdir and "directory" or "file"
ret._type = isdir and 'directory' or 'file'
end
local entry = cache.get_entry_by_id(ret.id)
ranges.name = { start, start + string.len(rem) - 1 }
@ -122,20 +122,20 @@ M.parse_line = function(adapter, line, column_defs)
-- Parse the symlink syntax
local meta = entry[FIELD_META]
local entry_type = entry[FIELD_TYPE]
if entry_type == "link" and meta and meta.link then
local name_pieces = vim.split(ret.name, " -> ", { plain = true })
if entry_type == 'link' and meta and meta.link then
local name_pieces = vim.split(ret.name, ' -> ', { plain = true })
if #name_pieces ~= 2 then
ret.name = ""
ret.name = ''
return { data = ret, ranges = ranges }
end
ranges.name = { start, start + string.len(name_pieces[1]) - 1 }
ret.name = parsedir(vim.trim(name_pieces[1]))
ret.link_target = name_pieces[2]
ret._type = "link"
ret._type = 'link'
end
-- Try to keep the same file type
if entry_type ~= "directory" and entry_type ~= "file" and ret._type ~= "directory" then
if entry_type ~= 'directory' and entry_type ~= 'file' and ret._type ~= 'directory' then
ret._type = entry[FIELD_TYPE]
end
@ -187,7 +187,7 @@ M.parse = function(bufnr)
name = name:lower()
end
if seen_names[name] then
table.insert(errors, { message = "Duplicate filename", lnum = i - 1, end_lnum = i, col = 0 })
table.insert(errors, { message = 'Duplicate filename', lnum = i - 1, end_lnum = i, col = 0 })
else
seen_names[name] = true
end
@ -197,7 +197,7 @@ M.parse = function(bufnr)
-- hack to be compatible with Lua 5.1
-- use return instead of goto
(function()
if line:match("^/%d+") then
if line:match('^/%d+') then
-- Parse the line for an existing entry
local result, err = M.parse_line(adapter, line, column_defs)
if not result or err then
@ -217,11 +217,11 @@ M.parse = function(bufnr)
local err_message
if not parsed_entry.name then
err_message = "No filename found"
err_message = 'No filename found'
elseif not entry then
err_message = "Could not find existing entry (was the ID changed?)"
elseif parsed_entry.name:match("/") or parsed_entry.name:match(fs.sep) then
err_message = "Filename cannot contain path separator"
err_message = 'Could not find existing entry (was the ID changed?)'
elseif parsed_entry.name:match('/') or parsed_entry.name:match(fs.sep) then
err_message = 'Filename cannot contain path separator'
end
if err_message then
table.insert(errors, {
@ -237,16 +237,16 @@ M.parse = function(bufnr)
check_dupe(parsed_entry.name, i)
local meta = entry[FIELD_META]
if original_entries[parsed_entry.name] == parsed_entry.id then
if entry[FIELD_TYPE] == "link" and not compare_link_target(meta, parsed_entry) then
if entry[FIELD_TYPE] == 'link' and not compare_link_target(meta, parsed_entry) then
table.insert(diffs, {
type = "new",
type = 'new',
name = parsed_entry.name,
entry_type = "link",
entry_type = 'link',
link = parsed_entry.link_target,
})
elseif entry[FIELD_TYPE] ~= parsed_entry._type then
table.insert(diffs, {
type = "new",
type = 'new',
name = parsed_entry.name,
entry_type = parsed_entry._type,
})
@ -255,7 +255,7 @@ M.parse = function(bufnr)
end
else
table.insert(diffs, {
type = "new",
type = 'new',
name = parsed_entry.name,
entry_type = parsed_entry._type,
id = parsed_entry.id,
@ -267,7 +267,7 @@ M.parse = function(bufnr)
local col_name = util.split_config(col_def)
if columns.compare(adapter, col_name, entry, parsed_entry[col_name]) then
table.insert(diffs, {
type = "change",
type = 'change',
name = parsed_entry.name,
entry_type = entry[FIELD_TYPE],
column = col_name,
@ -278,7 +278,7 @@ M.parse = function(bufnr)
else
-- Parse a new entry
local name, isdir = parsedir(vim.trim(line))
if vim.startswith(name, "/") then
if vim.startswith(name, '/') then
table.insert(errors, {
message = "Paths cannot start with '/'",
lnum = i - 1,
@ -287,17 +287,17 @@ M.parse = function(bufnr)
})
return
end
if name ~= "" then
local link_pieces = vim.split(name, " -> ", { plain = true })
local entry_type = isdir and "directory" or "file"
if name ~= '' then
local link_pieces = vim.split(name, ' -> ', { plain = true })
local entry_type = isdir and 'directory' or 'file'
local link
if #link_pieces == 2 then
entry_type = "link"
entry_type = 'link'
name, link = unpack(link_pieces)
end
check_dupe(name, i)
table.insert(diffs, {
type = "new",
type = 'new',
name = name,
entry_type = entry_type,
link = link,
@ -309,7 +309,7 @@ M.parse = function(bufnr)
for name, child_id in pairs(original_entries) do
table.insert(diffs, {
type = "delete",
type = 'delete',
name = name,
id = child_id,
})

View file

@ -1,17 +1,17 @@
local columns = require("oil.columns")
local config = require("oil.config")
local layout = require("oil.layout")
local loading = require("oil.loading")
local util = require("oil.util")
local columns = require('oil.columns')
local config = require('oil.config')
local layout = require('oil.layout')
local loading = require('oil.loading')
local util = require('oil.util')
local Progress = {}
local FPS = 20
function Progress.new()
return setmetatable({
lines = { "", "" },
count = "",
spinner = "",
lines = { '', '' },
count = '',
spinner = '',
bufnr = nil,
winid = nil,
min_bufnr = nil,
@ -40,18 +40,18 @@ function Progress:show(opts)
return
end
local bufnr = vim.api.nvim_create_buf(false, true)
vim.bo[bufnr].bufhidden = "wipe"
vim.bo[bufnr].bufhidden = 'wipe'
self.bufnr = bufnr
self.cancel = opts.cancel or self.cancel
local loading_iter = loading.get_bar_iter()
local spinner = loading.get_iter("dots")
local spinner = loading.get_iter('dots')
if not self.timer then
self.timer = vim.loop.new_timer()
self.timer:start(
0,
math.floor(1000 / FPS),
vim.schedule_wrap(function()
self.lines[2] = string.format("%s %s", self.count, loading_iter())
self.lines[2] = string.format('%s %s', self.count, loading_iter())
self.spinner = spinner()
self:_render()
end)
@ -59,22 +59,22 @@ function Progress:show(opts)
end
local width, height = layout.calculate_dims(120, 10, config.progress)
self.winid = vim.api.nvim_open_win(self.bufnr, true, {
relative = "editor",
relative = 'editor',
width = width,
height = height,
row = math.floor((layout.get_editor_height() - height) / 2),
col = math.floor((layout.get_editor_width() - width) / 2),
zindex = 152, -- render on top of the floating window title
style = "minimal",
style = 'minimal',
border = config.progress.border,
})
vim.bo[self.bufnr].filetype = "oil_progress"
vim.bo[self.bufnr].filetype = 'oil_progress'
for k, v in pairs(config.progress.win_options) do
vim.api.nvim_set_option_value(k, v, { scope = "local", win = self.winid })
vim.api.nvim_set_option_value(k, v, { scope = 'local', win = self.winid })
end
table.insert(
self.autocmds,
vim.api.nvim_create_autocmd("VimResized", {
vim.api.nvim_create_autocmd('VimResized', {
callback = function()
self:_reposition()
end,
@ -82,7 +82,7 @@ function Progress:show(opts)
)
table.insert(
self.autocmds,
vim.api.nvim_create_autocmd("WinLeave", {
vim.api.nvim_create_autocmd('WinLeave', {
callback = function()
self:minimize()
end,
@ -94,17 +94,17 @@ function Progress:show(opts)
vim.api.nvim_win_close(self.winid, true)
end
end
vim.keymap.set("n", "c", cancel, { buffer = self.bufnr, nowait = true })
vim.keymap.set("n", "C", cancel, { buffer = self.bufnr, nowait = true })
vim.keymap.set("n", "m", minimize, { buffer = self.bufnr, nowait = true })
vim.keymap.set("n", "M", minimize, { buffer = self.bufnr, nowait = true })
vim.keymap.set('n', 'c', cancel, { buffer = self.bufnr, nowait = true })
vim.keymap.set('n', 'C', cancel, { buffer = self.bufnr, nowait = true })
vim.keymap.set('n', 'm', minimize, { buffer = self.bufnr, nowait = true })
vim.keymap.set('n', 'M', minimize, { buffer = self.bufnr, nowait = true })
end
function Progress:restore()
if self.closing then
return
elseif not self:is_minimized() then
error("Cannot restore progress window: not minimized")
error('Cannot restore progress window: not minimized')
end
self:_cleanup_minimized_win()
self:show()
@ -115,14 +115,14 @@ function Progress:_render()
util.render_text(
self.bufnr,
self.lines,
{ winid = self.winid, actions = { "[M]inimize", "[C]ancel" } }
{ winid = self.winid, actions = { '[M]inimize', '[C]ancel' } }
)
end
if self.min_bufnr and vim.api.nvim_buf_is_valid(self.min_bufnr) then
util.render_text(
self.min_bufnr,
{ string.format("%sOil: %s", self.spinner, self.count) },
{ winid = self.min_winid, h_align = "left" }
{ string.format('%sOil: %s', self.spinner, self.count) },
{ winid = self.min_winid, h_align = 'left' }
)
end
end
@ -136,7 +136,7 @@ function Progress:_reposition()
end
local width, height = layout.calculate_dims(min_width, 10, config.progress)
vim.api.nvim_win_set_config(self.winid, {
relative = "editor",
relative = 'editor',
width = width,
height = height,
row = math.floor((layout.get_editor_height() - height) / 2),
@ -174,22 +174,22 @@ function Progress:minimize()
end
self:_cleanup_main_win()
local bufnr = vim.api.nvim_create_buf(false, true)
vim.bo[bufnr].bufhidden = "wipe"
vim.bo[bufnr].bufhidden = 'wipe'
local winid = vim.api.nvim_open_win(bufnr, false, {
relative = "editor",
relative = 'editor',
width = 16,
height = 1,
anchor = "SE",
anchor = 'SE',
row = layout.get_editor_height(),
col = layout.get_editor_width(),
zindex = 152, -- render on top of the floating window title
style = "minimal",
style = 'minimal',
border = config.progress.minimized_border,
})
self.min_bufnr = bufnr
self.min_winid = winid
self:_render()
vim.notify_once("Restore progress window with :Oil --progress")
vim.notify_once('Restore progress window with :Oil --progress')
end
---@param action oil.Action
@ -198,14 +198,14 @@ end
function Progress:set_action(action, idx, total)
local adapter = util.get_adapter_for_action(action)
local change_line
if action.type == "change" then
if action.type == 'change' then
---@cast action oil.ChangeAction
change_line = columns.render_change_action(adapter, action)
else
change_line = adapter.render_action(action)
end
self.lines[1] = change_line
self.count = string.format("%d/%d", idx, total)
self.count = string.format('%d/%d', idx, total)
self:_reposition()
self:_render()
end

View file

@ -1,4 +1,4 @@
local util = require("oil.util")
local util = require('oil.util')
---@class (exact) oil.Trie
---@field new fun(): oil.Trie
@ -20,7 +20,7 @@ end
function Trie:_url_to_path_pieces(url)
local scheme, path = util.parse_url(url)
assert(path)
local pieces = vim.split(path, "/")
local pieces = vim.split(path, '/')
table.insert(pieces, 1, scheme)
return pieces
end
@ -75,7 +75,7 @@ function Trie:remove(path_pieces, value)
return
end
end
error("Value not present in trie: " .. vim.inspect(value))
error('Value not present in trie: ' .. vim.inspect(value))
end
---Add the first action that affects a parent path of the url

View file

@ -3,26 +3,26 @@ local M = {}
---@param path string
---@return string
M.parent = function(path)
if path == "/" then
return "/"
elseif path == "" then
return ""
elseif vim.endswith(path, "/") then
return path:match("^(.*/)[^/]*/$") or ""
if path == '/' then
return '/'
elseif path == '' then
return ''
elseif vim.endswith(path, '/') then
return path:match('^(.*/)[^/]*/$') or ''
else
return path:match("^(.*/)[^/]*$") or ""
return path:match('^(.*/)[^/]*$') or ''
end
end
---@param path string
---@return nil|string
M.basename = function(path)
if path == "/" or path == "" then
if path == '/' or path == '' then
return
elseif vim.endswith(path, "/") then
return path:match("^.*/([^/]*)/$")
elseif vim.endswith(path, '/') then
return path:match('^.*/([^/]*)/$')
else
return path:match("^.*/([^/]*)$")
return path:match('^.*/([^/]*)$')
end
end

View file

@ -23,11 +23,11 @@ end
---@return string
function Ringbuf:as_str()
local postfix = ""
local postfix = ''
for i = 1, self.tail, 1 do
postfix = postfix .. self.buf[i]
end
local prefix = ""
local prefix = ''
for i = self.tail + 1, #self.buf, 1 do
prefix = prefix .. self.buf[i]
end

View file

@ -9,7 +9,7 @@ M.run = function(cmd, opts, callback)
local stderr = {}
local jid = vim.fn.jobstart(
cmd,
vim.tbl_deep_extend("keep", opts, {
vim.tbl_deep_extend('keep', opts, {
stdout_buffered = true,
stderr_buffered = true,
on_stdout = function(j, output)
@ -22,19 +22,19 @@ M.run = function(cmd, opts, callback)
if code == 0 then
callback(nil, stdout)
else
local err = table.concat(stderr, "\n")
if err == "" then
err = "Unknown error"
local err = table.concat(stderr, '\n')
if err == '' then
err = 'Unknown error'
end
local cmd_str = type(cmd) == "string" and cmd or table.concat(cmd, " ")
local cmd_str = type(cmd) == 'string' and cmd or table.concat(cmd, ' ')
callback(string.format("Error running command '%s'\n%s", cmd_str, err))
end
end),
})
)
local exe
if type(cmd) == "string" then
exe = vim.split(cmd, "%s+")[1]
if type(cmd) == 'string' then
exe = vim.split(cmd, '%s+')[1]
else
exe = cmd[1]
end

View file

@ -1,5 +1,5 @@
local config = require("oil.config")
local constants = require("oil.constants")
local config = require('oil.config')
local constants = require('oil.constants')
local M = {}
@ -14,7 +14,7 @@ local FIELD_META = constants.FIELD_META
---@return nil|string
---@return nil|string
M.parse_url = function(url)
return url:match("^(.*://)(.*)$")
return url:match('^(.*://)(.*)$')
end
---Escapes a filename for use in :edit
@ -26,51 +26,51 @@ M.escape_filename = function(filename)
end
local _url_escape_to_char = {
["20"] = " ",
["22"] = "",
["23"] = "#",
["24"] = "$",
["25"] = "%",
["26"] = "&",
["27"] = "",
["2B"] = "+",
["2C"] = ",",
["2F"] = "/",
["3A"] = ":",
["3B"] = ";",
["3C"] = "<",
["3D"] = "=",
["3E"] = ">",
["3F"] = "?",
["40"] = "@",
["5B"] = "[",
["5C"] = "\\",
["5D"] = "]",
["5E"] = "^",
["60"] = "`",
["7B"] = "{",
["7C"] = "|",
["7D"] = "}",
["7E"] = "~",
['20'] = ' ',
['22'] = '',
['23'] = '#',
['24'] = '$',
['25'] = '%',
['26'] = '&',
['27'] = '',
['2B'] = '+',
['2C'] = ',',
['2F'] = '/',
['3A'] = ':',
['3B'] = ';',
['3C'] = '<',
['3D'] = '=',
['3E'] = '>',
['3F'] = '?',
['40'] = '@',
['5B'] = '[',
['5C'] = '\\',
['5D'] = ']',
['5E'] = '^',
['60'] = '`',
['7B'] = '{',
['7C'] = '|',
['7D'] = '}',
['7E'] = '~',
}
local _char_to_url_escape = {}
for k, v in pairs(_url_escape_to_char) do
_char_to_url_escape[v] = "%" .. k
_char_to_url_escape[v] = '%' .. k
end
-- TODO this uri escape handling is very incomplete
---@param string string
---@return string
M.url_escape = function(string)
return (string:gsub(".", _char_to_url_escape))
return (string:gsub('.', _char_to_url_escape))
end
---@param string string
---@return string
M.url_unescape = function(string)
return (
string:gsub("%%([0-9A-Fa-f][0-9A-Fa-f])", function(seq)
return _url_escape_to_char[seq:upper()] or ("%" .. seq)
string:gsub('%%([0-9A-Fa-f][0-9A-Fa-f])', function(seq)
return _url_escape_to_char[seq:upper()] or ('%' .. seq)
end)
)
end
@ -105,14 +105,14 @@ M.pad_align = function(text, width, align)
return text, 0
end
if align == "right" then
return string.rep(" ", total_pad) .. text, total_pad
elseif align == "center" then
if align == 'right' then
return string.rep(' ', total_pad) .. text, total_pad
elseif align == 'center' then
local left_pad = math.floor(total_pad / 2)
local right_pad = total_pad - left_pad
return string.rep(" ", left_pad) .. text .. string.rep(" ", right_pad), left_pad
return string.rep(' ', left_pad) .. text .. string.rep(' ', right_pad), left_pad
else
return text .. string.rep(" ", total_pad), 0
return text .. string.rep(' ', total_pad), 0
end
end
@ -150,7 +150,7 @@ end
---@param dest_buf_name string
---@return boolean True if the buffer was replaced instead of renamed
M.rename_buffer = function(src_bufnr, dest_buf_name)
if type(src_bufnr) == "string" then
if type(src_bufnr) == 'string' then
src_bufnr = vim.fn.bufadd(src_bufnr)
if not vim.api.nvim_buf_is_loaded(src_bufnr) then
vim.api.nvim_buf_delete(src_bufnr, {})
@ -164,7 +164,7 @@ M.rename_buffer = function(src_bufnr, dest_buf_name)
-- 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
---@diagnostic disable-next-line: param-type-mismatch
local altbuf = vim.fn.bufnr("#")
local altbuf = vim.fn.bufnr('#')
-- This will fail if the dest buf name already exists
local ok = pcall(vim.api.nvim_buf_set_name, src_bufnr, dest_buf_name)
if ok then
@ -173,7 +173,7 @@ M.rename_buffer = function(src_bufnr, dest_buf_name)
-- where Neovim doesn't allow buffer modifications.
pcall(vim.api.nvim_buf_delete, vim.fn.bufadd(bufname), {})
if altbuf and vim.api.nvim_buf_is_valid(altbuf) then
vim.fn.setreg("#", altbuf)
vim.fn.setreg('#', altbuf)
end
return false
@ -229,6 +229,7 @@ end
M.cb_collect = function(count, cb)
return function(err)
if err then
-- selene: allow(mismatched_arg_count)
cb(err)
cb = function() end
else
@ -243,9 +244,9 @@ end
---@param url string
---@return string[]
local function get_possible_buffer_names_from_url(url)
local fs = require("oil.fs")
local fs = require('oil.fs')
local scheme, path = M.parse_url(url)
if config.adapters[scheme] == "files" then
if config.adapters[scheme] == 'files' then
assert(path)
return { fs.posix_to_os_path(path) }
end
@ -258,7 +259,7 @@ end
M.update_moved_buffers = function(entry_type, src_url, dest_url)
local src_buf_names = get_possible_buffer_names_from_url(src_url)
local dest_buf_name = get_possible_buffer_names_from_url(dest_url)[1]
if entry_type ~= "directory" then
if entry_type ~= 'directory' then
for _, src_buf_name in ipairs(src_buf_names) do
M.rename_buffer(src_buf_name, dest_buf_name)
end
@ -272,13 +273,13 @@ M.update_moved_buffers = function(entry_type, src_url, dest_url)
if vim.startswith(bufname, src_url) then
-- Handle oil directory buffers
vim.api.nvim_buf_set_name(bufnr, dest_url .. bufname:sub(src_url:len() + 1))
elseif bufname ~= "" and vim.bo[bufnr].buftype == "" then
elseif bufname ~= '' and vim.bo[bufnr].buftype == '' then
-- Handle regular buffers
local scheme = M.parse_url(bufname)
-- If the buffer is a local file, make sure we're using the absolute path
if not scheme then
bufname = vim.fn.fnamemodify(bufname, ":p")
bufname = vim.fn.fnamemodify(bufname, ':p')
end
for _, src_buf_name in ipairs(src_buf_names) do
@ -296,13 +297,13 @@ end
---@return string
---@return table|nil
M.split_config = function(name_or_config)
if type(name_or_config) == "string" then
if type(name_or_config) == 'string' then
return name_or_config, nil
else
if not name_or_config[1] and name_or_config["1"] then
if not name_or_config[1] and name_or_config['1'] then
-- This was likely loaded from json, so the first element got coerced to a string key
name_or_config[1] = name_or_config["1"]
name_or_config["1"] = nil
name_or_config[1] = name_or_config['1']
name_or_config['1'] = nil
end
return name_or_config[1], name_or_config
end
@ -324,7 +325,7 @@ M.render_table = function(lines, col_width, col_align)
local pieces = {}
for i, chunk in ipairs(cols) do
local text, hl
if type(chunk) == "table" then
if type(chunk) == 'table' then
text = chunk[1]
hl = chunk[2]
else
@ -333,11 +334,11 @@ M.render_table = function(lines, col_width, col_align)
local unpadded_len = text:len()
local padding
text, padding = M.pad_align(text, col_width[i], col_align[i] or "left")
text, padding = M.pad_align(text, col_width[i], col_align[i] or 'left')
table.insert(pieces, text)
if hl then
if type(hl) == "table" then
if type(hl) == 'table' then
-- hl has the form { [1]: hl_name, [2]: col_start, [3]: col_end }[]
-- Notice that col_start and col_end are relative position inside
-- that col, so we need to add the offset to them
@ -355,7 +356,7 @@ M.render_table = function(lines, col_width, col_align)
end
col = col + text:len() + 1
end
table.insert(str_lines, table.concat(pieces, " "))
table.insert(str_lines, table.concat(pieces, ' '))
end
return str_lines, highlights
end
@ -363,7 +364,7 @@ end
---@param bufnr integer
---@param highlights any[][] List of highlights {group, lnum, col_start, col_end}
M.set_highlights = function(bufnr, highlights)
local ns = vim.api.nvim_create_namespace("Oil")
local ns = vim.api.nvim_create_namespace('Oil')
vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
for _, hl in ipairs(highlights) do
local group, line, col_start, col_end = unpack(hl)
@ -379,12 +380,12 @@ end
---@param os_slash? boolean use os filesystem slash instead of posix slash
---@return string
M.addslash = function(path, os_slash)
local slash = "/"
if os_slash and require("oil.fs").is_windows then
slash = "\\"
local slash = '/'
if os_slash and require('oil.fs').is_windows then
slash = '\\'
end
local endslash = path:match(slash .. "$")
local endslash = path:match(slash .. '$')
if not endslash then
return path .. slash
else
@ -395,7 +396,7 @@ end
---@param winid nil|integer
---@return boolean
M.is_floating_win = function(winid)
return vim.api.nvim_win_get_config(winid or 0).relative ~= ""
return vim.api.nvim_win_get_config(winid or 0).relative ~= ''
end
---Recalculate the window title for the current buffer
@ -410,10 +411,10 @@ M.get_title = function(winid)
local title = vim.api.nvim_buf_get_name(src_buf)
local scheme, path = M.parse_url(title)
if config.adapters[scheme] == "files" then
if config.adapters[scheme] == 'files' then
assert(path)
local fs = require("oil.fs")
title = vim.fn.fnamemodify(fs.posix_to_os_path(path), ":~")
local fs = require('oil.fs')
title = vim.fn.fnamemodify(fs.posix_to_os_path(path), ':~')
end
return title
end
@ -421,7 +422,7 @@ end
local winid_map = {}
M.add_title_to_win = function(winid, opts)
opts = opts or {}
opts.align = opts.align or "left"
opts.align = opts.align or 'left'
if not vim.api.nvim_win_is_valid(winid) then
return
end
@ -438,18 +439,18 @@ M.add_title_to_win = function(winid, opts)
else
bufnr = vim.api.nvim_create_buf(false, true)
local col = 1
if opts.align == "center" then
if opts.align == 'center' then
col = math.floor((vim.api.nvim_win_get_width(winid) - width) / 2)
elseif opts.align == "right" then
elseif opts.align == 'right' then
col = vim.api.nvim_win_get_width(winid) - 1 - width
elseif opts.align ~= "left" then
elseif opts.align ~= 'left' then
vim.notify(
string.format("Unknown oil window title alignment: '%s'", opts.align),
vim.log.levels.ERROR
)
end
title_winid = vim.api.nvim_open_win(bufnr, false, {
relative = "win",
relative = 'win',
win = winid,
width = width,
height = 1,
@ -457,20 +458,20 @@ M.add_title_to_win = function(winid, opts)
col = col,
focusable = false,
zindex = 151,
style = "minimal",
style = 'minimal',
noautocmd = true,
})
winid_map[winid] = title_winid
vim.api.nvim_set_option_value(
"winblend",
'winblend',
vim.wo[winid].winblend,
{ scope = "local", win = title_winid }
{ scope = 'local', win = title_winid }
)
vim.bo[bufnr].bufhidden = "wipe"
vim.bo[bufnr].bufhidden = 'wipe'
local update_autocmd = vim.api.nvim_create_autocmd("BufWinEnter", {
desc = "Update oil floating window title when buffer changes",
pattern = "*",
local update_autocmd = vim.api.nvim_create_autocmd('BufWinEnter', {
desc = 'Update oil floating window title when buffer changes',
pattern = '*',
callback = function(params)
local winbuf = params.buf
if vim.api.nvim_win_get_buf(winid) ~= winbuf then
@ -479,17 +480,17 @@ M.add_title_to_win = function(winid, opts)
local new_title = M.get_title(winid)
local new_width =
math.min(vim.api.nvim_win_get_width(winid) - 4, 2 + vim.api.nvim_strwidth(new_title))
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, { " " .. new_title .. " " })
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, { ' ' .. new_title .. ' ' })
vim.bo[bufnr].modified = false
vim.api.nvim_win_set_width(title_winid, new_width)
local new_col = 1
if opts.align == "center" then
if opts.align == 'center' then
new_col = math.floor((vim.api.nvim_win_get_width(winid) - new_width) / 2)
elseif opts.align == "right" then
elseif opts.align == 'right' then
new_col = vim.api.nvim_win_get_width(winid) - 1 - new_width
end
vim.api.nvim_win_set_config(title_winid, {
relative = "win",
relative = 'win',
win = winid,
row = -1,
col = new_col,
@ -498,8 +499,8 @@ M.add_title_to_win = function(winid, opts)
})
end,
})
vim.api.nvim_create_autocmd("WinClosed", {
desc = "Close oil floating window title when floating window closes",
vim.api.nvim_create_autocmd('WinClosed', {
desc = 'Close oil floating window title when floating window closes',
pattern = tostring(winid),
callback = function()
if title_winid and vim.api.nvim_win_is_valid(title_winid) then
@ -512,12 +513,12 @@ M.add_title_to_win = function(winid, opts)
nested = true,
})
end
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, { " " .. title .. " " })
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, { ' ' .. title .. ' ' })
vim.bo[bufnr].modified = false
vim.api.nvim_set_option_value(
"winhighlight",
"Normal:FloatTitle,NormalFloat:FloatTitle",
{ scope = "local", win = title_winid }
'winhighlight',
'Normal:FloatTitle,NormalFloat:FloatTitle',
{ scope = 'local', win = title_winid }
)
end
@ -542,7 +543,7 @@ M.get_adapter_for_action = function(action)
else
error(
string.format(
"Cannot copy files from %s -> %s; no cross-adapter transfer method found",
'Cannot copy files from %s -> %s; no cross-adapter transfer method found',
action.src_url,
action.dest_url
)
@ -559,12 +560,12 @@ end
---@return string
---@return integer
M.h_align = function(str, align, width)
if align == "center" then
if align == 'center' then
local padding = math.floor((width - vim.api.nvim_strwidth(str)) / 2)
return string.rep(" ", padding) .. str, padding
elseif align == "right" then
return string.rep(' ', padding) .. str, padding
elseif align == 'right' then
local padding = width - vim.api.nvim_strwidth(str)
return string.rep(" ", padding) .. str, padding
return string.rep(' ', padding) .. str, padding
else
return str, 0
end
@ -578,15 +579,15 @@ end
--- actions nil|string[]
--- winid nil|integer
M.render_text = function(bufnr, text, opts)
opts = vim.tbl_deep_extend("keep", opts or {}, {
h_align = "center",
v_align = "center",
opts = vim.tbl_deep_extend('keep', opts or {}, {
h_align = 'center',
v_align = 'center',
})
---@cast opts -nil
if not vim.api.nvim_buf_is_valid(bufnr) then
return
end
if type(text) == "string" then
if type(text) == 'string' then
text = { text }
end
local height = 40
@ -608,17 +609,17 @@ M.render_text = function(bufnr, text, opts)
local lines = {}
-- Add vertical spacing for vertical alignment
if opts.v_align == "center" then
if opts.v_align == 'center' then
for _ = 1, (height / 2) - (#text / 2) do
table.insert(lines, "")
table.insert(lines, '')
end
elseif opts.v_align == "bottom" then
elseif opts.v_align == 'bottom' then
local num_lines = height
if opts.actions then
num_lines = num_lines - 2
end
while #lines + #text < num_lines do
table.insert(lines, "")
table.insert(lines, '')
end
end
@ -632,12 +633,12 @@ M.render_text = function(bufnr, text, opts)
local highlights = {}
if opts.actions then
while #lines < height - 1 do
table.insert(lines, "")
table.insert(lines, '')
end
local last_line, padding = M.h_align(table.concat(opts.actions, " "), "center", width)
local last_line, padding = M.h_align(table.concat(opts.actions, ' '), 'center', width)
local col = padding
for _, action in ipairs(opts.actions) do
table.insert(highlights, { "Special", #lines, col, col + 3 })
table.insert(highlights, { 'Special', #lines, col, col + 3 })
col = padding + action:len() + 4
end
table.insert(lines, last_line)
@ -656,10 +657,10 @@ end
M.run_in_fullscreen_win = function(bufnr, callback)
if not bufnr then
bufnr = vim.api.nvim_create_buf(false, true)
vim.bo[bufnr].bufhidden = "wipe"
vim.bo[bufnr].bufhidden = 'wipe'
end
local winid = vim.api.nvim_open_win(bufnr, false, {
relative = "editor",
relative = 'editor',
width = vim.o.columns,
height = vim.o.lines,
row = 0,
@ -667,7 +668,7 @@ M.run_in_fullscreen_win = function(bufnr, callback)
noautocmd = true,
})
local winnr = vim.api.nvim_win_get_number(winid)
vim.cmd.wincmd({ count = winnr, args = { "w" }, mods = { noautocmd = true } })
vim.cmd.wincmd({ count = winnr, args = { 'w' }, mods = { noautocmd = true } })
callback()
vim.cmd.close({ count = winnr, mods = { noautocmd = true, emsg_silent = true } })
end
@ -676,9 +677,9 @@ end
---@return boolean
M.is_oil_bufnr = function(bufnr)
local filetype = vim.bo[bufnr].filetype
if filetype == "oil" then
if filetype == 'oil' then
return true
elseif filetype ~= "" then
elseif filetype ~= '' then
-- If the filetype is set and is NOT "oil", then it's not an oil buffer
return false
end
@ -693,10 +694,10 @@ M.hack_around_termopen_autocmd = function(prev_mode)
vim.defer_fn(function()
local new_mode = vim.api.nvim_get_mode().mode
if new_mode ~= prev_mode then
if string.find(new_mode, "i") == 1 then
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<ESC>", true, true, true), "n", false)
if string.find(prev_mode, "v") == 1 or string.find(prev_mode, "V") == 1 then
vim.cmd.normal({ bang = true, args = { "gv" } })
if string.find(new_mode, 'i') == 1 then
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<ESC>', true, true, true), 'n', false)
if string.find(prev_mode, 'v') == 1 or string.find(prev_mode, 'V') == 1 then
vim.cmd.normal({ bang = true, args = { 'gv' } })
end
end
end
@ -712,7 +713,7 @@ M.get_preview_win = function(opts)
if
vim.api.nvim_win_is_valid(winid)
and vim.wo[winid].previewwindow
and (opts.include_not_owned or vim.w[winid]["oil_preview"])
and (opts.include_not_owned or vim.w[winid]['oil_preview'])
then
return winid
end
@ -721,13 +722,13 @@ end
---@return fun() restore Function that restores the cursor
M.hide_cursor = function()
vim.api.nvim_set_hl(0, "OilPreviewCursor", { nocombine = true, blend = 100 })
vim.api.nvim_set_hl(0, 'OilPreviewCursor', { nocombine = true, blend = 100 })
local original_guicursor = vim.go.guicursor
vim.go.guicursor = "a:OilPreviewCursor/OilPreviewCursor"
vim.go.guicursor = 'a:OilPreviewCursor/OilPreviewCursor'
return function()
-- HACK: see https://github.com/neovim/neovim/issues/21018
vim.go.guicursor = "a:"
vim.go.guicursor = 'a:'
vim.cmd.redrawstatus()
vim.go.guicursor = original_guicursor
end
@ -762,7 +763,7 @@ end
---@param opts {columns?: string[], no_cache?: boolean}
---@param callback fun(err: nil|string, entries: nil|oil.InternalEntry[])
M.adapter_list_all = function(adapter, url, opts, callback)
local cache = require("oil.cache")
local cache = require('oil.cache')
if not opts.no_cache then
local entries = cache.list_url(url)
if not vim.tbl_isempty(entries) then
@ -790,23 +791,23 @@ end
---based on the provided options.
---@param opts {target?: "qflist"|"loclist", action?: "r"|"a", only_matching_search?: boolean}
M.send_to_quickfix = function(opts)
if type(opts) ~= "table" then
if type(opts) ~= 'table' then
opts = {}
end
local oil = require("oil")
local oil = require('oil')
local dir = oil.get_current_dir()
if type(dir) ~= "string" then
if type(dir) ~= 'string' then
return
end
local range = M.get_visual_range()
if not range then
range = { start_lnum = 1, end_lnum = vim.fn.line("$") }
range = { start_lnum = 1, end_lnum = vim.fn.line('$') }
end
local match_all = not opts.only_matching_search
local qf_entries = {}
for i = range.start_lnum, range.end_lnum do
local entry = oil.get_entry_on_line(0, i)
if entry and entry.type == "file" and (match_all or M.is_matching(entry)) then
if entry and entry.type == 'file' and (match_all or M.is_matching(entry)) then
local qf_entry = {
filename = dir .. entry.name,
lnum = 1,
@ -817,26 +818,26 @@ M.send_to_quickfix = function(opts)
end
end
if #qf_entries == 0 then
vim.notify("[oil] No entries found to send to quickfix", vim.log.levels.WARN)
vim.notify('[oil] No entries found to send to quickfix', vim.log.levels.WARN)
return
end
vim.api.nvim_exec_autocmds("QuickFixCmdPre", {})
local qf_title = "oil files"
local action = opts.action == "a" and "a" or "r"
if opts.target == "loclist" then
vim.api.nvim_exec_autocmds('QuickFixCmdPre', {})
local qf_title = 'oil files'
local action = opts.action == 'a' and 'a' or 'r'
if opts.target == 'loclist' then
vim.fn.setloclist(0, {}, action, { title = qf_title, items = qf_entries })
vim.cmd.lopen()
else
vim.fn.setqflist({}, action, { title = qf_title, items = qf_entries })
vim.cmd.copen()
end
vim.api.nvim_exec_autocmds("QuickFixCmdPost", {})
vim.api.nvim_exec_autocmds('QuickFixCmdPost', {})
end
---@return boolean
M.is_visual_mode = function()
local mode = vim.api.nvim_get_mode().mode
return mode:match("^[vV]") ~= nil
return mode:match('^[vV]') ~= nil
end
---Get the current visual selection range. If not in visual mode, return nil.
@ -847,7 +848,7 @@ M.get_visual_range = function()
end
-- This is the best way to get the visual selection at the moment
-- https://github.com/neovim/neovim/pull/13896
local _, start_lnum, _, _ = unpack(vim.fn.getpos("v"))
local _, start_lnum, _, _ = unpack(vim.fn.getpos('v'))
local _, end_lnum, _, _, _ = unpack(vim.fn.getcurpos())
if start_lnum > end_lnum then
start_lnum, end_lnum = end_lnum, start_lnum
@ -863,7 +864,7 @@ M.is_matching = function(entry)
if search_highlighting_is_off then
return true
end
local pattern = vim.fn.getreg("/")
local pattern = vim.fn.getreg('/')
local position_of_match = vim.fn.match(entry.name, pattern)
return position_of_match ~= -1
end
@ -877,8 +878,8 @@ M.run_after_load = function(bufnr, callback)
if vim.b[bufnr].oil_ready then
callback()
else
vim.api.nvim_create_autocmd("User", {
pattern = "OilEnter",
vim.api.nvim_create_autocmd('User', {
pattern = 'OilEnter',
callback = function(args)
if args.data.buf == bufnr then
vim.api.nvim_buf_call(bufnr, callback)
@ -892,12 +893,12 @@ end
---@param entry oil.Entry
---@return boolean
M.is_directory = function(entry)
local is_directory = entry.type == "directory"
local is_directory = entry.type == 'directory'
or (
entry.type == "link"
entry.type == 'link'
and entry.meta
and entry.meta.link_stat
and entry.meta.link_stat.type == "directory"
and entry.meta.link_stat.type == 'directory'
)
return is_directory == true
end
@ -907,7 +908,7 @@ end
---@param entry oil.Entry
---@param callback fun(normalized_url: string)
M.get_edit_path = function(bufnr, entry, callback)
local pathutil = require("oil.pathutil")
local pathutil = require('oil.pathutil')
local bufname = vim.api.nvim_buf_get_name(bufnr)
local scheme, dir = M.parse_url(bufname)
@ -916,10 +917,10 @@ M.get_edit_path = function(bufnr, entry, callback)
local url = scheme .. dir .. entry.name
if M.is_directory(entry) then
url = url .. "/"
url = url .. '/'
end
if entry.name == ".." then
if entry.name == '..' then
callback(scheme .. pathutil.parent(dir))
elseif adapter.get_entry_path then
adapter.get_entry_path(url, entry, callback)
@ -932,33 +933,34 @@ end
---@return (oil.IconProvider)?
M.get_icon_provider = function()
-- prefer mini.icons
local _, mini_icons = pcall(require, "mini.icons")
local _, mini_icons = pcall(require, 'mini.icons')
-- selene: allow(global_usage)
---@diagnostic disable-next-line: undefined-field
if _G.MiniIcons then -- `_G.MiniIcons` is a better check to see if the module is setup
if _G.MiniIcons then
return function(type, name, conf, ft)
if ft then
return mini_icons.get("filetype", ft)
return mini_icons.get('filetype', ft)
end
return mini_icons.get(type == "directory" and "directory" or "file", name)
return mini_icons.get(type == 'directory' and 'directory' or 'file', name)
end
end
-- fallback to `nvim-web-devicons`
local has_devicons, devicons = pcall(require, "nvim-web-devicons")
local has_devicons, devicons = pcall(require, 'nvim-web-devicons')
if has_devicons then
return function(type, name, conf, ft)
if type == "directory" then
return conf and conf.directory or "", "OilDirIcon"
if type == 'directory' then
return conf and conf.directory or '', 'OilDirIcon'
else
if ft then
local ft_icon, ft_hl = devicons.get_icon_by_filetype(ft)
if ft_icon and ft_icon ~= "" then
if ft_icon and ft_icon ~= '' then
return ft_icon, ft_hl
end
end
local icon, hl = devicons.get_icon(name)
hl = hl or "OilFileIcon"
icon = icon or (conf and conf.default_file or "")
hl = hl or 'OilFileIcon'
icon = icon or (conf and conf.default_file or '')
return icon, hl
end
end
@ -975,24 +977,25 @@ M.read_file_to_scratch_buffer = function(path, preview_method)
return
end
vim.bo[bufnr].bufhidden = "wipe"
vim.bo[bufnr].buftype = "nofile"
vim.bo[bufnr].bufhidden = 'wipe'
vim.bo[bufnr].buftype = 'nofile'
local has_lines, read_res
if preview_method == "fast_scratch" then
has_lines, read_res = pcall(vim.fn.readfile, path, "", vim.o.lines)
if preview_method == 'fast_scratch' then
has_lines, read_res = pcall(vim.fn.readfile, path, '', vim.o.lines)
else
has_lines, read_res = pcall(vim.fn.readfile, path)
end
local lines = has_lines and vim.split(table.concat(read_res, "\n"), "\n") or {}
local lines = has_lines and vim.split(table.concat(read_res, '\n'), '\n') or {}
local ok = pcall(vim.api.nvim_buf_set_lines, bufnr, 0, -1, false, lines)
if not ok then
return
end
local ft = vim.filetype.match({ filename = path, buf = bufnr })
if ft and ft ~= "" and vim.treesitter.language.get_lang then
if ft and ft ~= '' and vim.treesitter.language.get_lang then
local lang = vim.treesitter.language.get_lang(ft)
-- selene: allow(empty_if)
if not pcall(vim.treesitter.start, bufnr, lang) then
vim.bo[bufnr].syntax = ft
else
@ -1000,8 +1003,8 @@ M.read_file_to_scratch_buffer = function(path, preview_method)
end
-- Replace the scratch buffer with a real buffer if we enter it
vim.api.nvim_create_autocmd("BufEnter", {
desc = "oil.nvim replace scratch buffer with real buffer",
vim.api.nvim_create_autocmd('BufEnter', {
desc = 'oil.nvim replace scratch buffer with real buffer',
buffer = bufnr,
callback = function()
local winid = vim.api.nvim_get_current_win()
@ -1013,7 +1016,7 @@ M.read_file_to_scratch_buffer = function(path, preview_method)
-- If we're still in a preview window, make sure this buffer still gets treated as a
-- preview
if vim.wo.previewwindow then
vim.bo.bufhidden = "wipe"
vim.bo.bufhidden = 'wipe'
vim.b.oil_preview_buffer = true
end
end
@ -1030,7 +1033,7 @@ local _regcache = {}
---@return boolean
M.file_matches_bufreadcmd = function(filename)
local autocmds = vim.api.nvim_get_autocmds({
event = "BufReadCmd",
event = 'BufReadCmd',
})
for _, au in ipairs(autocmds) do
local pat = _regcache[au.pattern]

View file

@ -1,12 +1,12 @@
local uv = vim.uv or vim.loop
local cache = require("oil.cache")
local columns = require("oil.columns")
local config = require("oil.config")
local constants = require("oil.constants")
local fs = require("oil.fs")
local keymap_util = require("oil.keymap_util")
local loading = require("oil.loading")
local util = require("oil.util")
local cache = require('oil.cache')
local columns = require('oil.columns')
local config = require('oil.config')
local constants = require('oil.constants')
local fs = require('oil.fs')
local keymap_util = require('oil.keymap_util')
local loading = require('oil.loading')
local util = require('oil.util')
local M = {}
local FIELD_ID = constants.FIELD_ID
@ -41,7 +41,7 @@ end
---Set the cursor to the last_cursor_entry if one exists
M.maybe_set_cursor = function()
local oil = require("oil")
local oil = require('oil')
local bufname = vim.api.nvim_buf_get_name(0)
local entry_name = last_cursor_entry[bufname]
if not entry_name then
@ -52,7 +52,7 @@ M.maybe_set_cursor = function()
local entry = oil.get_entry_on_line(0, lnum)
if entry and entry.name == entry_name then
local line = vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, true)[1]
local id_str = line:match("^/(%d+)")
local id_str = line:match('^/(%d+)')
local col = line:find(entry_name, 1, true) or (id_str:len() + 1)
vim.api.nvim_win_set_cursor(0, { lnum, col - 1 })
M.set_last_cursor(bufname, nil)
@ -78,14 +78,14 @@ local function are_any_modified()
end
local function is_unix_executable(entry)
if entry[FIELD_TYPE] == "directory" then
if entry[FIELD_TYPE] == 'directory' then
return false
end
local meta = entry[FIELD_META]
if not meta or not meta.stat then
return false
end
if meta.stat.type == "directory" then
if meta.stat.type == 'directory' then
return false
end
@ -98,7 +98,7 @@ end
M.toggle_hidden = function()
local any_modified = are_any_modified()
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
config.view_options.show_hidden = not config.view_options.show_hidden
M.rerender_all_oil_buffers({ refetch = false })
@ -109,7 +109,7 @@ end
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)
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 })
@ -119,7 +119,7 @@ end
M.set_columns = function(cols)
local any_modified = are_any_modified()
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
config.columns = cols
-- TODO only refetch if we don't have all the necessary data for the columns
@ -130,7 +130,7 @@ end
M.set_sort = function(new_sort)
local any_modified = are_any_modified()
if any_modified then
vim.notify("Cannot change sorting when you have unsaved changes", vim.log.levels.WARN)
vim.notify('Cannot change sorting when you have unsaved changes', vim.log.levels.WARN)
else
config.view_options.sort = new_sort
-- TODO only refetch if we don't have all the necessary data for the columns
@ -207,14 +207,14 @@ M.set_win_options = function()
local winid = vim.api.nvim_get_current_win()
-- work around https://github.com/neovim/neovim/pull/27422
vim.api.nvim_set_option_value("foldmethod", "manual", { scope = "local", win = winid })
vim.api.nvim_set_option_value('foldmethod', 'manual', { scope = 'local', win = winid })
for k, v in pairs(config.win_options) do
vim.api.nvim_set_option_value(k, v, { scope = "local", win = winid })
vim.api.nvim_set_option_value(k, v, { scope = 'local', win = winid })
end
if vim.wo[winid].previewwindow then -- apply preview window options last
for k, v in pairs(config.preview_win.win_options) do
vim.api.nvim_set_option_value(k, v, { scope = "local", win = winid })
vim.api.nvim_set_option_value(k, v, { scope = 'local', win = winid })
end
end
end
@ -251,7 +251,7 @@ M.delete_hidden_buffers = function()
not visible_buffers
or not hidden_buffers
or not vim.tbl_isempty(visible_buffers)
or vim.fn.win_gettype() == "command"
or vim.fn.win_gettype() == 'command'
then
return
end
@ -283,15 +283,15 @@ end
--- @param cur integer[]
--- @return integer[] | nil
local function calc_constrained_cursor_pos(bufnr, adapter, mode, cur)
local parser = require("oil.mutator.parser")
local parser = require('oil.mutator.parser')
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.ranges then
local min_col
if mode == "editable" then
if mode == 'editable' then
min_col = get_first_mutable_column_col(adapter, result.ranges)
elseif mode == "name" then
elseif mode == 'name' then
min_col = result.ranges.name[1]
else
error(string.format('Unexpected value "%s" for option constrain_cursor', mode))
@ -318,7 +318,7 @@ local function constrain_cursor(bufnr, mode)
return
end
local mc = package.loaded["multicursor-nvim"]
local mc = package.loaded['multicursor-nvim']
if mc then
mc.onSafeState(function()
mc.action(function(ctx)
@ -346,14 +346,14 @@ local function redraw_trash_virtual_text(bufnr)
if not vim.api.nvim_buf_is_valid(bufnr) or not vim.api.nvim_buf_is_loaded(bufnr) then
return
end
local parser = require("oil.mutator.parser")
local parser = require('oil.mutator.parser')
local adapter = util.get_adapter(bufnr, true)
if not adapter or adapter.name ~= "trash" then
if not adapter or adapter.name ~= 'trash' then
return
end
local _, buf_path = util.parse_url(vim.api.nvim_buf_get_name(bufnr))
local os_path = fs.posix_to_os_path(assert(buf_path))
local ns = vim.api.nvim_create_namespace("OilVtext")
local ns = vim.api.nvim_create_namespace('OilVtext')
vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
local column_defs = columns.get_supported_columns(adapter)
for lnum, line in ipairs(vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)) do
@ -367,8 +367,8 @@ local function redraw_trash_virtual_text(bufnr)
vim.api.nvim_buf_set_extmark(bufnr, ns, lnum - 1, 0, {
virt_text = {
{
"" .. fs.shorten_path(trash_info.original_path, os_path),
"OilTrashSourcePath",
'' .. fs.shorten_path(trash_info.original_path, os_path),
'OilTrashSourcePath',
},
},
})
@ -387,13 +387,13 @@ M.initialize = function(bufnr)
end
vim.api.nvim_clear_autocmds({
buffer = bufnr,
group = "Oil",
group = 'Oil',
})
vim.bo[bufnr].buftype = "acwrite"
vim.bo[bufnr].buftype = 'acwrite'
vim.bo[bufnr].readonly = false
vim.bo[bufnr].swapfile = false
vim.bo[bufnr].syntax = "oil"
vim.bo[bufnr].filetype = "oil"
vim.bo[bufnr].syntax = 'oil'
vim.bo[bufnr].filetype = 'oil'
vim.b[bufnr].EditorConfig_disable = 1
session[bufnr] = session[bufnr] or {}
for k, v in pairs(config.buf_options) do
@ -401,9 +401,9 @@ M.initialize = function(bufnr)
end
vim.api.nvim_buf_call(bufnr, M.set_win_options)
vim.api.nvim_create_autocmd("BufHidden", {
desc = "Delete oil buffers when no longer in use",
group = "Oil",
vim.api.nvim_create_autocmd('BufHidden', {
desc = 'Delete oil buffers when no longer in use',
group = 'Oil',
nested = true,
buffer = bufnr,
callback = function()
@ -413,7 +413,7 @@ M.initialize = function(bufnr)
-- Only delete oil buffers if none of them are visible
if visible_buffers and vim.tbl_isempty(visible_buffers) then
-- Check if cleanup is enabled
if type(config.cleanup_delay_ms) == "number" then
if type(config.cleanup_delay_ms) == 'number' then
if config.cleanup_delay_ms > 0 then
vim.defer_fn(function()
M.delete_hidden_buffers()
@ -426,8 +426,8 @@ M.initialize = function(bufnr)
end, 100)
end,
})
vim.api.nvim_create_autocmd("BufUnload", {
group = "Oil",
vim.api.nvim_create_autocmd('BufUnload', {
group = 'Oil',
nested = true,
once = true,
buffer = bufnr,
@ -439,8 +439,8 @@ M.initialize = function(bufnr)
end
end,
})
vim.api.nvim_create_autocmd("BufEnter", {
group = "Oil",
vim.api.nvim_create_autocmd('BufEnter', {
group = 'Oil',
buffer = bufnr,
callback = function(args)
local opts = vim.b[args.buf].oil_dirty
@ -451,9 +451,9 @@ M.initialize = function(bufnr)
end,
})
local timer
vim.api.nvim_create_autocmd("InsertEnter", {
desc = "Constrain oil cursor position",
group = "Oil",
vim.api.nvim_create_autocmd('InsertEnter', {
desc = 'Constrain oil cursor position',
group = 'Oil',
buffer = bufnr,
callback = function()
-- For some reason the cursor bounces back to its original position,
@ -461,12 +461,12 @@ M.initialize = function(bufnr)
vim.schedule_wrap(constrain_cursor)(bufnr, config.constrain_cursor)
end,
})
vim.api.nvim_create_autocmd({ "CursorMoved", "ModeChanged" }, {
desc = "Update oil preview window",
group = "Oil",
vim.api.nvim_create_autocmd({ 'CursorMoved', 'ModeChanged' }, {
desc = 'Update oil preview window',
group = 'Oil',
buffer = bufnr,
callback = function()
local oil = require("oil")
local oil = require('oil')
if vim.wo.previewwindow then
return
end
@ -513,7 +513,7 @@ M.initialize = function(bufnr)
-- Set up a watcher that will refresh the directory
if
adapter
and adapter.name == "files"
and adapter.name == 'files'
and config.watch_for_changes
and not session[bufnr].fs_event
then
@ -532,7 +532,7 @@ M.initialize = function(bufnr)
fs_event:stop()
return
end
local mutator = require("oil.mutator")
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
@ -553,11 +553,11 @@ M.initialize = function(bufnr)
end
-- Watch for TextChanged and update the trash original path extmarks
if adapter and adapter.name == "trash" then
if adapter and adapter.name == 'trash' then
local debounce_timer = assert(uv.new_timer())
local pending = false
vim.api.nvim_create_autocmd("TextChanged", {
desc = "Update oil virtual text of original path",
vim.api.nvim_create_autocmd('TextChanged', {
desc = 'Update oil virtual text of original path',
buffer = bufnr,
callback = function()
-- Respond immediately to prevent flickering, the set the timer for a "cooldown period"
@ -583,14 +583,14 @@ M.initialize = function(bufnr)
M.render_buffer_async(bufnr, {}, function(err)
if err then
vim.notify(
string.format("Error rendering oil buffer %s: %s", vim.api.nvim_buf_get_name(bufnr), err),
string.format('Error rendering oil buffer %s: %s', vim.api.nvim_buf_get_name(bufnr), err),
vim.log.levels.ERROR
)
else
vim.b[bufnr].oil_ready = true
vim.api.nvim_exec_autocmds(
"User",
{ pattern = "OilEnter", modeline = false, data = { buf = bufnr } }
'User',
{ pattern = 'OilEnter', modeline = false, data = { buf = bufnr } }
)
end
end)
@ -606,12 +606,12 @@ local function get_sort_function(adapter, num_entries)
-- If empty, default to type + name sorting
if vim.tbl_isempty(sort_config) then
sort_config = { { "type", "asc" }, { "name", "asc" } }
sort_config = { { 'type', 'asc' }, { 'name', 'asc' } }
end
for _, sort_pair in ipairs(sort_config) do
local col_name, order = unpack(sort_pair)
if order ~= "asc" and order ~= "desc" then
if order ~= 'asc' and order ~= 'desc' then
vim.notify_once(
string.format(
"Column '%s' has invalid sort order '%s'. Should be either 'asc' or 'desc'",
@ -639,7 +639,7 @@ local function get_sort_function(adapter, num_entries)
local a_val = get_sort_value(a)
local b_val = get_sort_value(b)
if a_val ~= b_val then
if order == "desc" then
if order == 'desc' then
return a_val > b_val
else
return a_val < b_val
@ -663,7 +663,7 @@ local function render_buffer(bufnr, opts)
return false
end
local bufname = vim.api.nvim_buf_get_name(bufnr)
opts = vim.tbl_extend("keep", opts or {}, {
opts = vim.tbl_extend('keep', opts or {}, {
jump = false,
jump_first = false,
})
@ -693,10 +693,10 @@ local function render_buffer(bufnr, opts)
for i, col_def in ipairs(column_defs) do
col_width[i + 1] = 1
local _, conf = util.split_config(col_def)
col_align[i + 1] = conf and conf.align or "left"
col_align[i + 1] = conf and conf.align or 'left'
end
local parent_entry = { 0, "..", "directory" }
local parent_entry = { 0, '..', 'directory' }
if M.should_display(bufnr, parent_entry) then
local cols = M.format_entry_cols(parent_entry, column_defs, col_width, adapter, true, bufnr)
table.insert(line_table, cols)
@ -732,7 +732,7 @@ local function render_buffer(bufnr, opts)
if jump_idx then
local lnum = jump_idx
local line = vim.api.nvim_buf_get_lines(bufnr, lnum - 1, lnum, true)[1]
local id_str = line:match("^/(%d+)")
local id_str = line:match('^/(%d+)')
local id = tonumber(id_str)
if id then
local entry = cache.get_entry_by_id(id)
@ -745,7 +745,7 @@ local function render_buffer(bufnr, opts)
end
end
constrain_cursor(bufnr, "name")
constrain_cursor(bufnr, 'name')
end
end
end)
@ -760,13 +760,13 @@ end
local function get_link_text(name, meta)
local link_text
if meta then
if meta.link_stat and meta.link_stat.type == "directory" then
name = name .. "/"
if meta.link_stat and meta.link_stat.type == 'directory' then
name = name .. '/'
end
if meta.link then
link_text = "-> " .. meta.link:gsub("\n", "")
if meta.link_stat and meta.link_stat.type == "directory" then
link_text = '-> ' .. meta.link:gsub('\n', '')
if meta.link_stat and meta.link_stat.type == 'directory' then
link_text = util.addslash(link_text)
end
end
@ -786,15 +786,15 @@ end
M.format_entry_cols = function(entry, column_defs, col_width, adapter, is_hidden, bufnr)
local name = entry[FIELD_NAME]
local meta = entry[FIELD_META]
local hl_suffix = ""
local hl_suffix = ''
if is_hidden then
hl_suffix = "Hidden"
hl_suffix = 'Hidden'
end
if meta and meta.display_name then
name = meta.display_name
end
-- We can't handle newlines in filenames (and shame on you for doing that)
name = name:gsub("\n", "")
name = name:gsub('\n', '')
-- First put the unique ID
local cols = {}
local id_key = cache.format_id(entry[FIELD_ID])
@ -803,7 +803,7 @@ M.format_entry_cols = function(entry, column_defs, col_width, adapter, is_hidden
-- Then add all the configured columns
for i, column in ipairs(column_defs) do
local chunk = columns.render_col(adapter, column, entry, bufnr)
local text = type(chunk) == "table" and chunk[1] or chunk
local text = type(chunk) == 'table' and chunk[1] or chunk
---@cast text string
col_width[i + 1] = math.max(col_width[i + 1], vim.api.nvim_strwidth(text))
table.insert(cols, chunk)
@ -816,7 +816,7 @@ M.format_entry_cols = function(entry, column_defs, col_width, adapter, is_hidden
if get_custom_hl then
local external_entry = util.export_entry(entry)
if entry_type == "link" then
if entry_type == 'link' then
link_name, link_target = get_link_text(name, meta)
local is_orphan = not (meta and meta.link_stat)
link_name_hl = get_custom_hl(external_entry, is_hidden, false, is_orphan, bufnr)
@ -830,8 +830,8 @@ M.format_entry_cols = function(entry, column_defs, col_width, adapter, is_hidden
local hl = get_custom_hl(external_entry, is_hidden, false, false, bufnr)
if hl then
-- Add the trailing / if this is a directory, this is important
if entry_type == "directory" then
name = name .. "/"
if entry_type == 'directory' then
name = name .. '/'
end
table.insert(cols, { name, hl })
return cols
@ -840,49 +840,50 @@ M.format_entry_cols = function(entry, column_defs, col_width, adapter, is_hidden
end
local highlight_as_executable = false
if entry_type ~= "directory" then
if entry_type ~= 'directory' then
local lower = name:lower()
if
lower:match("%.exe$")
or lower:match("%.bat$")
or lower:match("%.cmd$")
or lower:match("%.com$")
or lower:match("%.ps1$")
lower:match('%.exe$')
or lower:match('%.bat$')
or lower:match('%.cmd$')
or lower:match('%.com$')
or lower:match('%.ps1$')
then
highlight_as_executable = true
-- selene: allow(if_same_then_else)
elseif is_unix_executable(entry) then
highlight_as_executable = true
end
end
if entry_type == "directory" then
table.insert(cols, { name .. "/", "OilDir" .. hl_suffix })
elseif entry_type == "socket" then
table.insert(cols, { name, "OilSocket" .. hl_suffix })
elseif entry_type == "link" then
if entry_type == 'directory' then
table.insert(cols, { name .. '/', 'OilDir' .. hl_suffix })
elseif entry_type == 'socket' then
table.insert(cols, { name, 'OilSocket' .. hl_suffix })
elseif entry_type == 'link' then
if not link_name then
link_name, link_target = get_link_text(name, meta)
end
local is_orphan = not (meta and meta.link_stat)
if not link_name_hl then
if highlight_as_executable then
link_name_hl = "OilExecutable" .. hl_suffix
link_name_hl = 'OilExecutable' .. hl_suffix
else
link_name_hl = (is_orphan and "OilOrphanLink" or "OilLink") .. hl_suffix
link_name_hl = (is_orphan and 'OilOrphanLink' or 'OilLink') .. hl_suffix
end
end
table.insert(cols, { link_name, link_name_hl })
if link_target then
if not link_target_hl then
link_target_hl = (is_orphan and "OilOrphanLinkTarget" or "OilLinkTarget") .. hl_suffix
link_target_hl = (is_orphan and 'OilOrphanLinkTarget' or 'OilLinkTarget') .. hl_suffix
end
table.insert(cols, { link_target, link_target_hl })
end
elseif highlight_as_executable then
table.insert(cols, { name, "OilExecutable" .. hl_suffix })
table.insert(cols, { name, 'OilExecutable' .. hl_suffix })
else
table.insert(cols, { name, "OilFile" .. hl_suffix })
table.insert(cols, { name, 'OilFile' .. hl_suffix })
end
return cols
@ -914,8 +915,8 @@ M.render_buffer_async = function(bufnr, opts, caller_callback)
local function callback(err)
if not err then
vim.api.nvim_exec_autocmds(
"User",
{ pattern = "OilReadPost", modeline = false, data = { buf = bufnr } }
'User',
{ pattern = 'OilReadPost', modeline = false, data = { buf = bufnr } }
)
end
if caller_callback then
@ -923,7 +924,7 @@ M.render_buffer_async = function(bufnr, opts, caller_callback)
end
end
opts = vim.tbl_deep_extend("keep", opts or {}, {
opts = vim.tbl_deep_extend('keep', opts or {}, {
refetch = true,
})
---@cast opts -nil
@ -949,8 +950,8 @@ M.render_buffer_async = function(bufnr, opts, caller_callback)
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 })
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[bufnr]) do
cb(message)
@ -987,7 +988,7 @@ M.render_buffer_async = function(bufnr, opts, caller_callback)
loading.set_loading(bufnr, false)
render_buffer(bufnr, { jump = true })
M.set_last_cursor(bufname, nil)
vim.bo[bufnr].undolevels = vim.api.nvim_get_option_value("undolevels", { scope = "global" })
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()

View file

@ -1,7 +1,7 @@
local M = {}
M.is_win_supported = function(winid, bufnr)
return vim.bo[bufnr].filetype == "oil"
return vim.bo[bufnr].filetype == 'oil'
end
M.save_win = function(winid)
@ -11,7 +11,7 @@ M.save_win = function(winid)
end
M.load_win = function(winid, config)
require("oil").open(config.bufname)
require('oil').open(config.bufname)
end
return M