refactor: modularized into separate files

remotes/origin/HEAD
Chris Grieser 2023-12-30 12:55:50 +01:00
parent c8ac2e47af
commit e9868e9780
3 changed files with 118 additions and 101 deletions

View File

@ -0,0 +1,63 @@
local M = {}
local u = require("genghis.utils")
--------------------------------------------------------------------------------
---Requests a 'workspace/willRenameFiles' on any running LSP client, that supports it
---stolen from https://github.com/LazyVim/LazyVim/blob/fecc5faca25c209ed62e3658dd63731e26c0c643/lua/lazyvim/util/init.lua#L304
---@param fromName string
---@param toName string
function M.onRename(fromName, toName)
local clients = vim.lsp.get_active_clients { bufnr = 0 }
for _, client in ipairs(clients) do
if client:supports_method("workspace/willRenameFiles") then
local resp = client.request_sync("workspace/willRenameFiles", {
files = {
{ oldUri = vim.uri_from_fname(fromName), newUri = vim.uri_from_fname(toName) },
},
}, 1000)
if resp and resp.result ~= nil then
vim.lsp.util.apply_workspace_edit(resp.result, client.offset_encoding)
end
end
end
end
---@nodiscard
---@return boolean
function M.lspSupportsRenaming()
-- INFO `client:supports_method()` seems to always return true, whatever is
-- supplied as argument. This does not affect `onRename`, but here we need to
-- check for the server_capabilities to properly identify whether our LSP
-- supports renaming or not.
-- TODO investigate if `client:supports_method()` works in nvim 0.10 or later
local clients = vim.lsp.get_active_clients { bufnr = 0 }
for _, client in ipairs(clients) do
local workspaceCap = client.server_capabilities.workspace
local supports = workspaceCap and workspaceCap.fileOperations and workspaceCap.fileOperations.willRename
if supports then return true end
end
return false
end
---use instead of fs_rename to support moving across partitions
---@param oldFilePath string
---@param newFilePath string
function M.moveFile(oldFilePath, newFilePath)
local copied, copiedError = vim.loop.fs_copyfile(oldFilePath, newFilePath)
if copied then
local deleted, deletedError = vim.loop.fs_unlink(oldFilePath)
if deleted then
return true
else
u.notify(("Failed to delete %q: %q"):format(oldFilePath, deletedError), "error")
return false
end
else
u.notify(("Failed to move %q to %q: %q"):format(oldFilePath, newFilePath, copiedError), "error")
return false
end
end
--------------------------------------------------------------------------------
return M

View File

@ -3,88 +3,9 @@ local M = {}
local expand = vim.fn.expand local expand = vim.fn.expand
local fn = vim.fn local fn = vim.fn
local cmd = vim.cmd local cmd = vim.cmd
--------------------------------------------------------------------------------
---@param bufnr? number|"#"|"$" local mv = require("genghis.file-movement")
local function bwipeout(bufnr) local u = require("genghis.utils")
bufnr = bufnr and fn.bufnr(bufnr) or 0 ---@diagnostic disable-line: param-type-mismatch
vim.api.nvim_buf_delete(bufnr, { force = true })
end
--- Requests a 'workspace/willRenameFiles' on any running LSP client, that supports it
--- stolen from https://github.com/LazyVim/LazyVim/blob/fecc5faca25c209ed62e3658dd63731e26c0c643/lua/lazyvim/util/init.lua#L304
---@param fromName string
---@param toName string
local function onRename(fromName, toName)
local clients = vim.lsp.get_active_clients { bufnr = 0 }
for _, client in ipairs(clients) do
if client:supports_method("workspace/willRenameFiles") then
local resp = client.request_sync("workspace/willRenameFiles", {
files = {
{ oldUri = vim.uri_from_fname(fromName), newUri = vim.uri_from_fname(toName) },
},
}, 1000)
if resp and resp.result ~= nil then
vim.lsp.util.apply_workspace_edit(resp.result, client.offset_encoding)
end
end
end
end
---@nodiscard
---@return boolean
local function lspSupportsRenaming()
-- INFO `client:supports_method()` seems to always return true, whatever is
-- supplied as argument. This does not affect `onRename`, but here we need to
-- check for the server_capabilities to properly identify whether our LSP
-- supports renaming or not.
-- TODO investigate if `client:supports_method()` works in nvim 0.10 or later
local clients = vim.lsp.get_active_clients { bufnr = 0 }
for _, client in ipairs(clients) do
local workspaceCap = client.server_capabilities.workspace
local supports = workspaceCap and workspaceCap.fileOperations and workspaceCap.fileOperations.willRename
if supports then return true end
end
return false
end
-- https://github.com/neovim/neovim/issues/17735#issuecomment-1068525617
local function leaveVisualMode()
local escKey = vim.api.nvim_replace_termcodes("<Esc>", false, true, true)
vim.api.nvim_feedkeys(escKey, "nx", false)
end
---send notification
---@param msg string
---@param level? "info"|"trace"|"debug"|"warn"|"error"
local function notify(msg, level)
if not level then level = "info" end
vim.notify(msg, vim.log.levels[level:upper()], { title = "nvim-genghis" })
end
---@param filepath string
---@return boolean
local function fileExists(filepath) return vim.loop.fs_stat(filepath) ~= nil end
---move file
---use instead of fs_rename to support moving across partitions
---@param oldFilePath string
---@param newFilePath string
local function moveFile(oldFilePath, newFilePath)
local copied, copiedError = vim.loop.fs_copyfile(oldFilePath, newFilePath)
if copied then
local deleted, deletedError = vim.loop.fs_unlink(oldFilePath)
if deleted then
return true
else
notify(("Failed to delete %q: %q"):format(oldFilePath, deletedError), "error")
return false
end
else
notify(("Failed to move %q to %q: %q"):format(oldFilePath, newFilePath, copiedError), "error")
return false
end
end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -101,7 +22,7 @@ local function fileOp(op)
local prevReg local prevReg
if op == "newFromSel" then if op == "newFromSel" then
prevReg = fn.getreg("z") prevReg = fn.getreg("z")
leaveVisualMode() u.leaveVisualMode()
cmd([['<,'>delete z]]) cmd([['<,'>delete z]])
end end
@ -110,10 +31,11 @@ local function fileOp(op)
promptStr = "Duplicate File as: " promptStr = "Duplicate File as: "
prefill = oldNameNoExt .. "-1" prefill = oldNameNoExt .. "-1"
elseif op == "rename" then elseif op == "rename" then
promptStr = lspSupportsRenaming() and "Rename File & Update Imports:" or "Rename File to:" promptStr = mv.lspSupportsRenaming() and "Rename File & Update Imports:" or "Rename File to:"
prefill = oldNameNoExt prefill = oldNameNoExt
elseif op == "move-rename" then elseif op == "move-rename" then
promptStr = lspSupportsRenaming() and "Move-Rename File & Update Imports:" or "Move & Rename File to:" promptStr = mv.lspSupportsRenaming() and "Move-Rename File & Update Imports:"
or "Move & Rename File to:"
prefill = dir .. "/" prefill = dir .. "/"
elseif op == "new" or op == "newFromSel" then elseif op == "new" or op == "newFromSel" then
promptStr = "Name for New File: " promptStr = "Name for New File: "
@ -121,7 +43,11 @@ local function fileOp(op)
end end
-- INFO completion = "dir" allows for completion via cmp-omni -- INFO completion = "dir" allows for completion via cmp-omni
vim.ui.input({ prompt = promptStr, default = prefill, completion = "dir" }, function(newName) vim.ui.input({
prompt = promptStr,
default = prefill,
completion = "dir",
}, function(newName)
cmd.redraw() -- Clear message area from ui.input prompt cmd.redraw() -- Clear message area from ui.input prompt
-- VALIDATION OF FILENAME -- VALIDATION OF FILENAME
@ -143,9 +69,9 @@ local function fileOp(op)
fn.setreg("z", prevReg) -- restore register content fn.setreg("z", prevReg) -- restore register content
end end
if invalidName or emptyInput then if invalidName or emptyInput then
notify("Invalid filename.", "error") u.notify("Invalid filename.", "error")
elseif sameName then elseif sameName then
notify("Cannot use the same filename.", "error") u.notify("Cannot use the same filename.", "error")
end end
return return
end end
@ -164,8 +90,8 @@ local function fileOp(op)
if not extProvided then newName = newName .. oldExt end if not extProvided then newName = newName .. oldExt end
local newFilePath = (op == "move-rename") and newName or dir .. "/" .. newName local newFilePath = (op == "move-rename") and newName or dir .. "/" .. newName
if fileExists(newFilePath) then if u.fileExists(newFilePath) then
notify(("File with name %q already exists."):format(newFilePath), "error") u.notify(("File with name %q already exists."):format(newFilePath), "error")
return return
end end
@ -175,15 +101,15 @@ local function fileOp(op)
local success = vim.loop.fs_copyfile(oldFilePath, newFilePath) local success = vim.loop.fs_copyfile(oldFilePath, newFilePath)
if success then if success then
cmd.edit(newFilePath) cmd.edit(newFilePath)
notify(("Duplicated %q as %q."):format(oldName, newName)) u.notify(("Duplicated %q as %q."):format(oldName, newName))
end end
elseif op == "rename" or op == "move-rename" then elseif op == "rename" or op == "move-rename" then
onRename(oldFilePath, newFilePath) mv.onRename(oldFilePath, newFilePath)
local success = moveFile(oldFilePath, newFilePath) local success = mv.moveFile(oldFilePath, newFilePath)
if success then if success then
cmd.edit(newFilePath) cmd.edit(newFilePath)
bwipeout("#") u.bwipeout("#")
notify(("Renamed %q to %q."):format(oldName, newName)) u.notify(("Renamed %q to %q."):format(oldName, newName))
end end
elseif op == "new" or op == "newFromSel" then elseif op == "new" or op == "newFromSel" then
cmd.edit(newFilePath) cmd.edit(newFilePath)
@ -232,11 +158,11 @@ function M.chmodx()
local perm = fn.getfperm(filename) local perm = fn.getfperm(filename)
perm = perm:gsub("r(.)%-", "r%1x") -- add x to every group that has r perm = perm:gsub("r(.)%-", "r%1x") -- add x to every group that has r
fn.setfperm(filename, perm) fn.setfperm(filename, perm)
notify("Execution Permission granted.") u.notify("Execution Permission granted.")
cmd.edit() cmd.edit()
end end
---@param opts? table ---@param opts? {trashLocation: string}
function M.trashFile(opts) function M.trashFile(opts)
cmd.update { bang = true } cmd.update { bang = true }
local trash local trash
@ -262,15 +188,15 @@ function M.trashFile(opts)
-- overwrite trash location, if specified by user -- overwrite trash location, if specified by user
if opts and opts.trashLocation then if opts and opts.trashLocation then
trash = opts.trashLocation trash = opts.trashLocation
if not (trash:find("/$")) then trash = trash .. "/" end if not (trash:find("/$")) then trash = trash .. "/" end
end end
fn.mkdir(trash, "p") fn.mkdir(trash, "p")
if fileExists(trash .. oldName) then oldName = oldName .. "~" end if u.fileExists(trash .. oldName) then oldName = oldName .. "~" end
if moveFile(oldFilePath, trash .. oldName) then if mv.moveFile(oldFilePath, trash .. oldName) then
bwipeout() u.bwipeout()
notify(("%q deleted"):format(oldName)) u.notify(("%q deleted"):format(oldName))
end end
end end

28
lua/genghis/utils.lua Normal file
View File

@ -0,0 +1,28 @@
local M = {}
--------------------------------------------------------------------------------
---@param bufnr? number|"#"|"$"
function M.bwipeout(bufnr)
bufnr = bufnr and vim.fn.bufnr(bufnr) or 0 ---@diagnostic disable-line: param-type-mismatch
vim.api.nvim_buf_delete(bufnr, { force = true })
end
-- https://github.com/neovim/neovim/issues/17735#issuecomment-1068525617
function M.leaveVisualMode()
local escKey = vim.api.nvim_replace_termcodes("<Esc>", false, true, true)
vim.api.nvim_feedkeys(escKey, "nx", false)
end
---@param msg string
---@param level? "info"|"trace"|"debug"|"warn"|"error"
function M.notify(msg, level)
if not level then level = "info" end
vim.notify(msg, vim.log.levels[level:upper()], { title = "nvim-genghis" })
end
---@param filepath string
---@return boolean
function M.fileExists(filepath) return vim.loop.fs_stat(filepath) ~= nil end
--------------------------------------------------------------------------------
return M