Skip to content
Draft
45 changes: 21 additions & 24 deletions lua/client/client.lua
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
require("common.common_functions")
require("common.arg_parse")

-- We don't want to overwrite :h shada
vim.o.sdf = "NONE"

-- We don't want to start. Send the args to the server instance instead.
local args = vim.call("argv")

local arg_str = ""
for index, iter in pairs(args) do
local absolute_filepath = unception_get_absolute_filepath(iter)

if (string.len(arg_str) == 0) then
arg_str = unception_escape_special_chars(absolute_filepath)
else
arg_str = arg_str.." "..unception_escape_special_chars(absolute_filepath)
end
-- get only files argument already parsed by neovim
local true_file_args = vim.call("argv")
local options = {
open_in_new_tab = vim.g.unception_open_buffer_in_new_tab,
delete_replaced_buffer = vim.g.unception_delete_replaced_buffer,
enable_flavor_text = vim.g.unception_enable_flavor_text,
multi_file_open_method = vim.g.unception_multi_file_open_method,
}
-- Use raw argv to retreive some options as well as files lines when it exist
local file_lines = unception_arg_parse(vim.v.argv, options)
local file_args = {}
for _, file in ipairs(true_file_args) do
local absolute_filepath = unception_get_absolute_filepath(file)
table.insert(file_args , {
path = absolute_filepath,
line = file_lines[file]
})
end

-- Send messages to host on existing pipe.
local sock = vim.fn.sockconnect("pipe", os.getenv(unception_pipe_path_host_env_var), {rpc = true})
local edit_files_call = "unception_edit_files("
.."\""..arg_str.."\", "
..#args..", "
..vim.inspect(vim.g.unception_open_buffer_in_new_tab)..", "
..vim.inspect(vim.g.unception_delete_replaced_buffer)..", "
..vim.inspect(vim.g.unception_enable_flavor_text)..")"
vim.fn.rpcrequest(sock, "nvim_exec_lua", edit_files_call, {})
local sock = vim.fn.sockconnect("pipe", os.getenv(unception_pipe_path_host_env_var), { rpc = true })
vim.rpcrequest(sock, "nvim_exec_lua", "unception_edit_files(...)", { file_args, options })

if (not vim.g.unception_block_while_host_edits) then
-- Our work here is done. Kill the nvim session that would have started otherwise.
Expand All @@ -50,14 +51,10 @@ end
local nested_pipe_path = vim.call("serverstart")

-- Send the pipe path and edited filepath to the host so that it knows what file to look for and who to respond to.
local notify_when_done_call = "unception_notify_when_done_editing("
..vim.inspect(nested_pipe_path)..","
..vim.inspect(arg_str)..")"
vim.fn.rpcnotify(sock, "nvim_exec_lua", notify_when_done_call, {})
vim.rpcnotify(sock, "nvim_exec_lua", "unception_notify_when_done_editing(...)", { nested_pipe_path, file_args[1].path })

-- Sleep forever. The host session will kill this when it's done editing.
while (true)
do
vim.cmd("sleep 10")
end

144 changes: 144 additions & 0 deletions lua/common/arg_parse.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
local function detect_line_option(str)
return str:match("^%+(%d+)$")
end

local function detect_dash_parametes(str)
return str:match("^-") ~= nil
end

local parameters_map = {
["-o"] = "split",
["-O"] = "vsplit",
["-p"] = "tab",
["-d"] = "diff",
}

local function detect_parameters(str)
local handled_opt = parameters_map[str]
if handled_opt then
return handled_opt
end
return detect_dash_parametes(str)
end


local function parse_double_dash(arg, state)
if state.double_dash then
return false
end
if arg == "--" then
state.double_dash = true
return true
end
return false
end

local function parse_option(arg, state)
if state.double_dash then
return false
end

local opt = detect_parameters(arg)

if not opt then
return false
end

if opt ~= true then
state.options.multi_file_open_method = opt
end
return true
end

local function parse_line_number(arg, state)
if state.double_dash then
return false
end

local line = detect_line_option(arg)
if not line then
return false
end

-- When + option used first let's apply this on the first file
local index = (state.index > 1) and (state.index - 1) or (state.index)

if not state.files[index] then
state.files[index] = {}
end

state.files[index].line = tonumber(line)

return true
end

local function parse_file(arg, state)
if not state.files[state.index] then
state.files[state.index] = {}
end
state.files[state.index].path = arg
state.index = state.index + 1
return true
end

---This function filter all files
---and return only the mapping of files -> line number
---@param files {[string]:any}[]
---@return { [string]: integer } dict with file -> line
local function extract_file_with_line_number(files)
local ret = {}
for _, file in ipairs(files) do
if file.line then
ret[file.path] = file.line
end
end

return ret
end

---This is the list of parsing function
---Note that the order matter
local parser = {
parse_double_dash,
parse_option,
parse_line_number,
parse_file,
}

---This function parse bare neovim argument list
---It has two purpose:
---
---1. Detect cmd line option `-o -O -p`, and fill the given options
---2. Detect +\d line number specifier, and returning a dictionary of file:line number
---Here we don't care of file without any file number since those
---will be handled by neovim argument parser which is smartest than this parser
---
---Limitation: this is a really dumb parser if you run neovim for example with
---`nvim --cmd "this_is_cmd_argument" +32` this_is_cmd_argument will end up within
---the list of file returned by this function, we don't really care since files
---to open will be retrieved by the neovim parser via vim.call("argv") here we want
---a good enough parser to detect line number after a file and some options that's it
---@param args string[] this argument need the bare argv from neovim
---@param options table
---@return { [string]: integer }
function unception_arg_parse(args, options)
local state = {
files = {},
index = 1,
double_dash = false,
options = options
}

-- remove nvim in argv[1]
table.remove(args, 1)

for _, arg in ipairs(args) do
for _, fn in ipairs(parser) do
if fn(arg, state) then
break
end
end
end

return extract_file_with_line_number(state.files)
end
6 changes: 3 additions & 3 deletions lua/common/common_functions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ function _G.unception_escape_special_chars(str)
-- filepaths. Lua needs \\ to define a \, so to escape special chars,
-- there are twice as many backslashes as you would think that there
-- should be.
str = string.gsub(str, "\\", "\\\\\\\\")
str = string.gsub(str, "\"", "\\\\\\\"")
str = string.gsub(str, " ", "\\\\ ")
str = string.gsub(str, "\\", "\\\\")
str = string.gsub(str, "\"", "\\\"")
str = string.gsub(str, " ", "\\ ")
return str
else
return ""
Expand Down
Loading