initial commit
commit
c1f1ec5cc1
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 Christopher Grieser
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# nvim-ghengis
|
||||
Convenience file operations for neovim written in lua.
|
||||
|
||||
<!--toc:start-->
|
||||
- [How is this different from `vim.eunuch`?](#how-is-this-different-from-vimeunuch)
|
||||
- [Installation and Setup](#installation-and-setup)
|
||||
- [Available Commands](#available-commands)
|
||||
- [Why that name?](#why-that-name)
|
||||
<!--toc:end-->
|
||||
|
||||
## How is this different from `vim.eunuch`?
|
||||
- Written 100% in lua.
|
||||
- Uses up-to-date nvim features like `vim.ui.input` or `vim.notify`. This means you can get nicer input fields via plugins like [dressing.nvim](https://github.com/stevearc/dressing.nvim), and nice confirmation notices with plugins like [nvim-notify](https://github.com/rcarriga/nvim-notify), if they are installed and setup.
|
||||
- Some minor improvements like automatically keeping the extensions when no extension is given.
|
||||
- Except for `trashFile` and `chmodx` only vim commands or lua os-modules are used to keep shell requirements to a minimum.
|
||||
|
||||
## Installation and Setup
|
||||
|
||||
```lua
|
||||
-- Packer
|
||||
use "chrisgrieser/nvim-ghengis"
|
||||
```
|
||||
|
||||
> __Note__
|
||||
> `ghengis` requires no `.setup()` function. Simply create keybindings for the commands you want to use.
|
||||
|
||||
```lua
|
||||
local ghengis = require("ghengis")
|
||||
local keymap = vim.keymap.set
|
||||
keymap("n", "<leader>yp", ghengis.copyFilepath)
|
||||
keymap("n", "<leader>yn", ghengis.copyFilename)
|
||||
keymap("n", "<leader>cx", ghengis.chmodx)
|
||||
keymap("n", "<leader>rf", ghengis.renameFile)
|
||||
keymap("n", "<leader>nf", ghengis.createNewFile)
|
||||
keymap("n", "<leader>yf", ghengis.duplicateFile)
|
||||
keymap("n", "<leader>df", ghengis.trashFile) -- requires macOS or Linux `mv` command
|
||||
keymap("x", "<leader>x", ghengis.moveSelectionToNewFile)
|
||||
```
|
||||
|
||||
## Available Commands
|
||||
- `ghengis.copyFilepath`: Copy the absolute file path. When `clipboard='unnamed[plus]`, copies to the `+` register, otherwise to `"`.
|
||||
- `ghengis.copyFilename`: Copy file name. When `clipboard='unnamed[plus]`, copies to the `+` register, otherwise to `"`.
|
||||
- `ghengis.chmodx`: Run `chmod +x` on the current file.
|
||||
- `ghengis.renameFile`: Rename the current file. If no extension is provided, will keep the current file extension.
|
||||
- `ghengis.createNewFile`: Create a new file. If no extension is provided, will keep use the extension of the current file.
|
||||
- `ghengis.duplicateFile`: Duplicate the current file. If no extension is provided, will keep the current file extension.
|
||||
- `ghengis.trashFile`: Move the current file to `$HOME/.Trash`. (Requires macOS or Linux, since is using `mv`.)
|
||||
- `ghengis.moveSelectionToNewFile`: Visual Mode Command. Prompts for a new file name and moves the current selection to that new file.
|
||||
|
||||
## Why that name?
|
||||
A tribute to [vim.eunuch](https://github.com/tpope/vim-eunuch) – as opposed to childless eunuchs, it is said that [Ghengis Khan has fathered thousands of children](https://allthatsinteresting.com/genghis-khan-children).
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
local M = {}
|
||||
|
||||
local error = vim.log.levels.ERROR
|
||||
local expand = vim.fn.expand
|
||||
local fn = vim.fn
|
||||
local cmd = vim.cmd
|
||||
|
||||
local function leaveVisualMode()
|
||||
-- https://github.com/neovim/neovim/issues/17735#issuecomment-1068525617
|
||||
local escKey = vim.api.nvim_replace_termcodes("<Esc>", false, true, true)
|
||||
vim.api.nvim_feedkeys(escKey, "nx", false)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
---Performing common file operation tasks
|
||||
---@param op string rename|duplicate|new|newFromSel
|
||||
local function fileOp(op)
|
||||
local dir = expand("%:p:h")
|
||||
local oldName = expand("%:t")
|
||||
local oldExt = expand("%:e")
|
||||
local prevReg
|
||||
if op == "newFromSel" then
|
||||
prevReg = fn.getreg("z")
|
||||
leaveVisualMode()
|
||||
cmd [['<,'>delete z]]
|
||||
end
|
||||
|
||||
local promptStr
|
||||
if op == "duplicate" then promptStr = "Duplicate File as: "
|
||||
elseif op == "rename" then promptStr = "Rename File to: "
|
||||
elseif op == "new" or op == "newFromSel" then promptStr = "Name for New File: "
|
||||
end
|
||||
|
||||
vim.ui.input({prompt = promptStr}, function(newName)
|
||||
local invalidName
|
||||
if newName then
|
||||
invalidName = newName:find("^%s*$") or newName:find("/") or newName:find(":") or newName:find("\\")
|
||||
end
|
||||
|
||||
if not (newName) or invalidName then -- cancel
|
||||
if op == "newFromSel" then
|
||||
cmd [[undo]] -- undo deletion
|
||||
fn.setreg("z", prevReg) -- restore register content
|
||||
end
|
||||
if invalidName then vim.notify(" Invalid Filename.", error) end
|
||||
return
|
||||
end
|
||||
if not (newName) or invalidName then -- cancel
|
||||
if op == "newFromSel" then
|
||||
cmd [[undo]] -- undo deletion
|
||||
fn.setreg("z", prevReg) -- restore register content
|
||||
end
|
||||
if invalidName then
|
||||
vim.notify(" Invalid Filename.", error)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local extProvided = newName:find(".%.") -- non-leading dot to exclude dotfile-dots
|
||||
local isDotfile = newName:match("^%.")
|
||||
if not (extProvided) and not (isDotfile) then
|
||||
newName = newName .. "." .. oldExt
|
||||
end
|
||||
local filepath = dir .. "/" .. newName
|
||||
|
||||
if op == "duplicate" then
|
||||
cmd("saveas " .. filepath)
|
||||
cmd("edit " .. filepath)
|
||||
vim.notify(" Duplicated '" .. oldName .. "' as '" .. newName .. "'.")
|
||||
elseif op == "rename" then
|
||||
os.rename(oldName, newName)
|
||||
cmd("edit " .. filepath)
|
||||
cmd("bdelete #")
|
||||
vim.notify(" Renamed '" .. oldName .. "' to '" .. newName .. "'.")
|
||||
elseif op == "new" then
|
||||
cmd("edit " .. filepath)
|
||||
cmd("write " .. filepath)
|
||||
elseif op == "newFromSel" then
|
||||
cmd("edit " .. filepath)
|
||||
cmd("put z")
|
||||
cmd("write " .. filepath)
|
||||
fn.setreg("z", prevReg) -- restore register content
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
---Rename Current File
|
||||
function M.renameFile() fileOp("rename") end
|
||||
|
||||
---Duplicate Current File
|
||||
function M.duplicateFile() fileOp("duplicate") end
|
||||
|
||||
---Create New File
|
||||
function M.createNewFile() fileOp("new") end
|
||||
|
||||
---Move Selection to New File
|
||||
function M.moveSelectionToNewFile() fileOp("newFromSel") end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
---copying file information
|
||||
---@param operation string filename|filepath
|
||||
local function copyOp(operation)
|
||||
local useSystemClipb = vim.opt.clipboard:get()[1]:find("unnamed")
|
||||
local reg = '"'
|
||||
if useSystemClipb then reg = "+" end
|
||||
|
||||
local toCopy
|
||||
if operation == "filename" then
|
||||
toCopy = expand("%:t")
|
||||
elseif operation == "filepath" then
|
||||
toCopy = expand("%:p")
|
||||
end
|
||||
|
||||
fn.setreg(reg, toCopy)
|
||||
vim.notify(" COPIED\n " .. toCopy)
|
||||
end
|
||||
|
||||
---Copy absolute path of current file
|
||||
function M.copyFilepath() copyOp("filepath") end
|
||||
|
||||
---Copy name of current file
|
||||
function M.copyFilename() copyOp("filename") end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
---Run `chmod +x` on the current file. Requires `chmod`.
|
||||
function M.chmodx()
|
||||
local currentFile = expand("%:p")
|
||||
os.execute("chmod +x '" .. currentFile .. "'")
|
||||
vim.notify(" Execution permission granted. ")
|
||||
end
|
||||
|
||||
---Trash the Current File. Requires `mv`.
|
||||
---@param opts? table
|
||||
function M.trashFile(opts)
|
||||
if not (opts) then opts = {trashLocation = "$HOME/.Trash/"} end
|
||||
|
||||
local currentFile = fn.expand("%:p")
|
||||
local filename = fn.expand("%:t")
|
||||
cmd [[update!]]
|
||||
os.execute('mv -f "' .. currentFile .. '" "' .. opts.trashLocation .. '"')
|
||||
cmd [[bdelete]]
|
||||
vim.notify(" '" .. filename .. "' deleted. ")
|
||||
end
|
||||
|
||||
return M
|
||||
Loading…
Reference in New Issue