はじめに
コマンドラインから画像をクリップボードにコピーしたくなることがたまによくあります。
毎回Finderを開いてコピーするのがストレスなので、画像ファイルをクリップボードへコピーするfish関数を作成しました。
普段はエディタとしてNeovim、ファイラとしてnvim-treeを使っているので、nvim-tree上からも同じ処理を実行できるようにしました。
fish関数の作成
普段シェルとしてfishを使っているので、
fishの関数として、画像ファイルをクリップボードへコピーするcopyimageコマンドを作成しました
macOSには、JavaScriptからmacOSのAPIを操作できるJXAが用意されているのでこれを使って操作を宣言しています。
指定されたファイルをNSImageとして読み込み、クリップボードを表すNSPasteboard.generalPasteboardへ書き込んでいます。
function copyimage --description "Copy image file to clipboard"
if test (count $argv) -ne 1
echo "usage: copyimage <image-path>" >&2
return 1
end
set -l path $argv[1]
if not test -f "$path"
echo "copyimage: file not found: $path" >&2
return 1
end
osascript -l JavaScript -e '
ObjC.import("AppKit")
function run(argv) {
const path = $.NSString
.stringWithString(argv[0])
.stringByStandardizingPath
const image = $.NSImage.alloc.initWithContentsOfFile(path)
if (!image) {
throw new Error("failed to load image: " + argv[0])
}
const clipboard = $.NSPasteboard.generalPasteboard
clipboard.clearContents
const objects = $.NSArray.arrayWithObject(image)
if (!clipboard.writeObjects(objects)) {
throw new Error("failed to write image to clipboard")
}
}
' "$path"
or return $status
end
次のように実行できます。
copyimage ./image.png
nvim-treeから実行できるようにする
nvim-tree上でカーソルが置かれているファイルをコピーできるようにします。
<Leader>yyへ次の処理を割り当てます。
- 画像ファイルの場合は、copyimageを実行する
- 画像以外の場合は、ファイルをテキストとして読み込んでクリップボードへコピーする
local function attach(bufnr)
local api = require("nvim-tree.api")
local function opts(desc)
return {
desc = "nvim-tree: " .. desc,
buffer = bufnr,
noremap = true,
silent = true,
nowait = true,
}
end
local image_extensions = {
png = true,
jpg = true,
jpeg = true,
gif = true,
tif = true,
tiff = true,
bmp = true,
webp = true,
heic = true,
}
local function is_image(path)
local extension = path:lower():match("%.([^./]+)$")
return extension ~= nil and image_extensions[extension] == true
end
api.config.mappings.default_on_attach(bufnr)
vim.keymap.set("n", "<Leader>yy", function()
local node = api.tree.get_node_under_cursor()
if not node or node.type ~= "file" then
vim.notify(
"nvim-tree: cursor is not on a file",
vim.log.levels.WARN
)
return
end
if is_image(node.absolute_path) then
local result = vim.system({
"fish",
"-lc",
'copyimage "$argv[1]"',
"--",
node.absolute_path,
}):wait()
if result.code ~= 0 then
local message = result.stderr
if not message or message == "" then
message = "unknown error"
end
vim.notify(
"nvim-tree: failed to copy image\n" .. message,
vim.log.levels.ERROR
)
return
end
vim.notify("nvim-tree: copied image to clipboard")
return
end
local ok, lines = pcall(vim.fn.readfile, node.absolute_path)
if not ok then
vim.notify(
"nvim-tree: failed to read " .. node.absolute_path,
vim.log.levels.ERROR
)
return
end
vim.fn.setreg("+", lines, "l")
vim.notify("nvim-tree: copied file contents to clipboard")
end, opts("Copy file contents"))
end
require("nvim-tree").setup({
on_attach = attach,
})
おわりに
気づけばなくてはならないコマンドになっていました。
macOS、fish、Neovim、nvim-treeという限定的な構成ではありますが、同じような環境を使っている方の参考になれば幸いです。