Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
264 changes: 264 additions & 0 deletions lua/fzf/remote-sshfs.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
local api = vim.api
local fn = vim.fn

-- Build virtualized host file from parsed hosts from plugin
local function build_host_preview(hosts, name)
if name == "" or name == nil then
return {}
end

local lines = {}
local host = hosts[name]

table.insert(lines, "# Config: " .. host["Config"])
table.insert(lines, "Host " .. host["Name"])
for key, value in pairs(host) do
if key ~= "Name" and key ~= "Config" then
table.insert(lines, string.format("\t%s %s", key, value))
end
end
table.insert(lines, "")

return table.concat(lines, "\n")
end

-- FZF action to select a host to connect to
local function connect(_)
local connections = require "remote-sshfs.connections"
local hosts = connections.list_hosts()

-- Create a temporary file to store host previews
local preview_file = vim.fn.tempname()
local preview_data = {}

-- Prepare preview data for each host
local source = {}
for hostname, _ in pairs(hosts) do
table.insert(source, hostname)
preview_data[hostname] = build_host_preview(hosts, hostname)
end

-- Write preview data to temp file
local lines = {}
for hostname, preview in pairs(preview_data) do
table.insert(lines, hostname .. "\n" .. preview .. "\n---")
end
vim.fn.writefile(lines, preview_file)

-- Create preview command that reads from the temp file
local preview_cmd = string.format('awk -v RS="---" "/^%s\\n/{print}" %s | tail -n +2', "{}", preview_file)

-- Build fzf options string
local opts = '--prompt="Connect to remote host> "'
.. ' --preview="'
.. preview_cmd
.. '"'
.. ' --preview-window="right:50%"'

-- Create spec table for fzf
local spec = {
source = source,
sink = function(selected)
local host = hosts[selected]
connections.connect(host)
-- Clean up temp file
vim.fn.delete(preview_file)
end,
options = opts,
}

-- Run fzf with proper wrapping
vim.fn["fzf#run"](vim.fn["fzf#wrap"]("remote-sshfs", spec, 0))
end

-- FZF action to select ssh config file to edit
local function edit_config(_)
local connections = require "remote-sshfs.connections"
local ssh_configs = connections.list_ssh_configs()

vim.fn["fzf#run"](vim.fn["fzf#wrap"] {
source = ssh_configs,
sink = function(selected)
vim.cmd("edit " .. selected)
end,
options = {
["--prompt"] = "Choose SSH config file to edit> ",
["--preview"] = "cat {}",
["--preview-window"] = "right:50%",
},
})
end

local function command_exists_on_remote(command, server)
local ssh_cmd = string.format('ssh %s "which %s"', server, command)
local result = vim.fn.system(ssh_cmd)
return result ~= ""
end

-- Remote find_files implementation
local function find_files(opts)
opts = opts or {}
local connections = require "remote-sshfs.connections"

if not connections.is_connected() then
vim.notify "You are not currently connected to a remote host."
return
end

local mount_point = opts.mount_point or connections.get_current_mount_point()
local current_host = connections.get_current_host()

-- Build the find command
local find_command = (function()
if opts.find_command then
if type(opts.find_command) == "function" then
return opts.find_command(opts)
end
return opts.find_command
elseif command_exists_on_remote("rg", current_host["Name"]) then
return { "ssh", current_host["Name"], "-C", "rg", "--files", "--color", "never" }
elseif command_exists_on_remote("fd", current_host["Name"]) then
return { "ssh", current_host["Name"], "fd", "--type", "f", "--color", "never" }
elseif command_exists_on_remote("fdfind", current_host["Name"]) then
return { "ssh", current_host["Name"], "fdfind", "--type", "f", "--color", "never" }
elseif command_exists_on_remote("where", current_host["Name"]) then
return { "ssh", current_host["Name"], "where", "/r", ".", "*" }
end
end)()

if not find_command then
vim.notify "Remote host does not support any available find commands (rg, fd, fdfind, where)."
return
end

-- Adapt command based on options
local command = find_command[3]
if command == "fd" or command == "fdfind" or command == "rg" then
if opts.hidden then
table.insert(find_command, "--hidden")
end
if opts.no_ignore then
table.insert(find_command, "--no-ignore")
end
if opts.follow then
table.insert(find_command, "-L")
end
end

-- Build fzf options string
local opts = '--prompt="Remote Find Files> "'
.. ' --preview="cat '
.. mount_point
.. '/{}"'
.. ' --preview-window="right:50%"'

-- Create spec table for fzf
local spec = {
source = table.concat(find_command, " "),
sink = function(selected)
vim.cmd("edit " .. mount_point .. "/" .. selected)
end,
options = opts,
}

-- Run fzf with proper wrapping
vim.fn["fzf#run"](vim.fn["fzf#wrap"]("remote-find", spec, 0))
end

-- Remote live_grep implementation
local function live_grep(opts)
opts = opts or {}
local connections = require "remote-sshfs.connections"

if not connections.is_connected() then
vim.notify "You are not currently connected to a remote host."
return
end

local current_host = connections.get_current_host()
local mount_point = opts.mount_point or connections.get_current_mount_point()

-- Build the rg command
local rg_command = {
"ssh",
current_host["Name"],
"-C",
"rg",
"--column",
"--line-number",
"--no-heading",
"--color=always",
"--smart-case",
}

if opts.type_filter then
table.insert(rg_command, "--type=" .. opts.type_filter)
end

if opts.glob_pattern then
if type(opts.glob_pattern) == "string" then
table.insert(rg_command, "--glob=" .. opts.glob_pattern)
elseif type(opts.glob_pattern) == "table" then
for _, pattern in ipairs(opts.glob_pattern) do
table.insert(rg_command, "--glob=" .. pattern)
end
end
end

-- Build fzf options string
local opts = '--prompt="Remote Live Grep> "'
.. ' --preview="bat --style=numbers --color=always '
.. mount_point
.. '/$(echo {} | cut -d: -f1)"'
.. ' --preview-window="right:50%"'
.. ' --delimiter=":"'
.. ' --nth="4.."'

-- Create spec table for fzf
local spec = {
source = table.concat(rg_command, " "),
sink = function(selected)
-- Parse the selection (format: file:line:col:text)
local parts = vim.split(selected, ":")
local file = parts[1]
local line = tonumber(parts[2])
local col = tonumber(parts[3])

-- Open the file at the specific location
vim.cmd("edit " .. mount_point .. "/" .. file)
vim.api.nvim_win_set_cursor(0, { line, col - 1 })
end,
options = opts,
}

-- Run fzf with proper wrapping
vim.fn["fzf#run"](vim.fn["fzf#wrap"]("remote-grep", spec, 0))
end

-- Initialize plugin
local function setup()
-- Create user commands
vim.api.nvim_create_user_command("RemoteConnect", function(opts)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we create these user commands, as similar user commands are created in init.lua?

connect(opts)
end, {})

vim.api.nvim_create_user_command("RemoteEdit", function(opts)
edit_config(opts)
end, {})

vim.api.nvim_create_user_command("RemoteFiles", function(opts)
find_files(opts)
end, {})

vim.api.nvim_create_user_command("RemoteGrep", function(opts)
live_grep(opts)
end, {})
end

return {
setup = setup,
connect = connect,
edit_config = edit_config,
find_files = find_files,
live_grep = live_grep,
}
20 changes: 14 additions & 6 deletions lua/remote-sshfs/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,28 @@ local default_opts = {
sshfs = false,
},
},
picker = "fzf"
}

M.setup_commands = function()
M.setup_commands = function(config)
local picker
if config.picker == "fzf" then
picker = require("../fzf/remote-sshfs")
else
picker = require("telescope").extensions["remote-sshfs"]
end

-- Create commands to connect/edit/reload/disconnect/find_files/live_grep
vim.api.nvim_create_user_command("RemoteSSHFSConnect", function(opts)
if opts.args and opts.args ~= "" then
local host = require("remote-sshfs.utils").parse_host_from_command(opts.args)
require("remote-sshfs.connections").connect(host)
else
require("telescope").extensions["remote-sshfs"].connect()
picker.connect()
end
end, { nargs = "?", desc = "Remotely connect to host via picker or command as argument." })
vim.api.nvim_create_user_command("RemoteSSHFSEdit", function()
require("telescope").extensions["remote-sshfs"].edit()
picker.edit()
end, {})
vim.api.nvim_create_user_command("RemoteSSHFSReload", function()
require("remote-sshfs.connections").reload()
Expand All @@ -65,10 +73,10 @@ M.setup_commands = function()
require("remote-sshfs.connections").unmount_host()
end, {})
vim.api.nvim_create_user_command("RemoteSSHFSFindFiles", function()
require("telescope").extensions["remote-sshfs"].find_files {}
picker.find_files {}
end, {})
vim.api.nvim_create_user_command("RemoteSSHFSLiveGrep", function()
require("telescope").extensions["remote-sshfs"].live_grep {}
picker.live_grep {}
end, {})
end

Expand All @@ -80,7 +88,7 @@ M.setup = function(config)
require("remote-sshfs.handler").setup(opts)
require("remote-sshfs.log").setup(opts)

M.setup_commands()
M.setup_commands(opts)
end

return M