Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ should you!
plenty of room to display the whole tree.
- Neo-tree does not need to be manually refreshed (set `use_libuv_file_watcher=true`)
- Neo-tree can intelligently follow the current file (set `follow_current_file.enabled=true`)
- Neo-tree can copy and cut files over many neovim instances (est `filesystem.shared_clipboard=true`)
- Neo-tree is thoughtful about maintaining or setting focus on the right node
- Neo-tree windows in different tabs are completely separate
- `respect_gitignore` actually works!
Expand Down
1 change: 1 addition & 0 deletions lua/neo-tree/defaults.lua
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ local config = {
async_directory_scan = "auto", -- "auto" means refreshes are async, but it's synchronous when called from the Neotree commands.
-- "always" means directory scans are always async.
-- "never" means directory scans are never async.
shared_clipboard = false, -- Enabling this feature allows you to copy and cut files over different instances of neovim
scan_mode = "shallow", -- "shallow": Don't scan into directories to detect possible empty directory a priori
-- "deep": Scan into directories to detect empty or grouped empty directories a priori.
bind_to_cwd = true, -- true creates a 2-way binding between vim's cwd and neo-tree's root
Expand Down
12 changes: 12 additions & 0 deletions lua/neo-tree/sources/filesystem/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ local utils = require("neo-tree.utils")
local filter = require("neo-tree.sources.filesystem.lib.filter")
local renderer = require("neo-tree.ui.renderer")
local log = require("neo-tree.log")
local shared_clipboard = require("neo-tree.sources.filesystem.lib.shared_clipboard")

local M = {}
local refresh = function(state)
Expand All @@ -16,6 +17,12 @@ local redraw = function(state)
renderer.redraw(state)
end

local sync_with_shared_clipboard = function(state)
if state.shared_clipboard then
shared_clipboard.save_clipboard(state.clipboard)
end
end

M.add = function(state)
cc.add(state, utils.wrap(fs.show_new_children, state))
end
Expand All @@ -35,19 +42,23 @@ end
---Marks node as copied, so that it can be pasted somewhere else.
M.copy_to_clipboard = function(state)
cc.copy_to_clipboard(state, utils.wrap(redraw, state))
sync_with_shared_clipboard(state)
end

M.copy_to_clipboard_visual = function(state, selected_nodes)
cc.copy_to_clipboard_visual(state, selected_nodes, utils.wrap(redraw, state))
sync_with_shared_clipboard(state)
end

---Marks node as cut, so that it can be pasted (moved) somewhere else.
M.cut_to_clipboard = function(state)
cc.cut_to_clipboard(state, utils.wrap(redraw, state))
sync_with_shared_clipboard(state)
end

M.cut_to_clipboard_visual = function(state, selected_nodes)
cc.cut_to_clipboard_visual(state, selected_nodes, utils.wrap(redraw, state))
sync_with_shared_clipboard(state)
end

M.move = function(state)
Expand All @@ -57,6 +68,7 @@ end
---Pastes all items from the clipboard to the current directory.
M.paste_from_clipboard = function(state)
cc.paste_from_clipboard(state, utils.wrap(fs.show_new_children, state))
sync_with_shared_clipboard(state)
end

M.delete = function(state)
Expand Down
4 changes: 4 additions & 0 deletions lua/neo-tree/sources/filesystem/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,10 @@ M.setup = function(config, global_config)
end,
})
end

if config.shared_clipboard then
require("neo-tree.sources.filesystem.lib.shared_clipboard").init()
end
end

---Expands or collapses the current node.
Expand Down
92 changes: 92 additions & 0 deletions lua/neo-tree/sources/filesystem/lib/shared_clipboard.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
local fs_watch = require("neo-tree.sources.filesystem.lib.fs_watch")
local manager = require("neo-tree.sources.manager")
local events = require("neo-tree.events")
local renderer = require("neo-tree.ui.renderer")

local M = {}

local clipboard_state_dir_path = vim.fn.stdpath("state") .. "/neo-tree/"
local clipboard_file_path = clipboard_state_dir_path .. "filesystem-clipboard.json"
local clipboard_file_last_mtime = nil


M.save_clipboard = function(clipboard)
local file = io.open(clipboard_file_path, "w+")
-- We want to erase data in the file if clipboard is nil instead writing null
if not clipboard or not file then
return
end

file:write(vim.json.encode(clipboard))
file:flush()
clipboard_file_last_mtime = vim.uv.fs_stat(clipboard_file_path).mtime
end

M._load_clipboard = function()
local file = io.open(clipboard_file_path, "r")
if not file then
return nil
end
local content = file:read("*a")
local is_success, clipboard = pcall(vim.json.decode, content)
if not is_success then
return nil
end
return clipboard
end

M._update_cilpboard = function()
if not vim.fn.filereadable(clipboard_file_path) then
return
end
local cur_mtime = vim.uv.fs_stat(clipboard_file_path).mtime
if
not clipboard_file_last_mtime
-- We need exactly >= because it allows us to synchronize the clipboard
-- even with trees openned in another window (in the current Neovim instance)
or M._is_left_mtime_greater_or_equal(cur_mtime, clipboard_file_last_mtime)
then
local clipboard = M._load_clipboard()
manager._for_each_state("filesystem", function(state)
state.clipboard = clipboard
vim.schedule(function()
renderer.redraw(state)
end)
end)
clipboard_file_last_mtime = cur_mtime
end
end

M._is_left_mtime_greater_or_equal = function(a, b)
if a.sec > b.sec then
return true
elseif a.sec == b.sec and a.nsec >= b.nsec then
return true
end
return false
end

M.init = function()
if vim.fn.isdirectory(clipboard_state_dir_path) == 0 then
vim.fn.mkdir(clipboard_state_dir_path)
end

events.subscribe({
event = events.STATE_CREATED,
handler = function(state)
if state.name ~= "filesystem" then
return
end
vim.schedule(function()
M._update_cilpboard()
end)
end,
})

-- Using watch_folder because we haven't "watch_file" :)
fs_watch.watch_folder(clipboard_state_dir_path, function()
M._update_cilpboard()
end, true)
end

return M
Loading