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:
parent
df53b172a9
commit
86f553cd0a
72 changed files with 2762 additions and 2649 deletions
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue