diff options
| -rw-r--r-- | init.lua | 155 | ||||
| -rw-r--r-- | util.lua | 164 |
2 files changed, 174 insertions, 145 deletions
@@ -31,6 +31,9 @@ lspc.logger = dofile(source_path .. 'log.lua').new('lspc', lspc.logging, lspc.lo local parser = dofile(source_path .. 'parser.lua') +-- initialise the util module +local util = dofile(source_path .. 'util.lua').init(lspc) + -- load a suitable json module lspc.json = dofile(source_path .. 'json.lua') @@ -51,35 +54,6 @@ jsonrpc.error_codes = { RequestCancelled = -32800, } --- Helper to execute a command and capture its output -local function capture_cmd(cmd) - local p = assert(io.popen(cmd, 'r')) - local s = assert(p:read('*a')) - local success, _, status = p:close() - if not success then - local err = cmd .. ' failed with exit code: ' .. status - lspc:err(err) - end - return s -end - -local vis_supports_pipe_buf = pcall(vis.pipe, vis, 'foo', 'true') - ---- Wrapper for the two vis:pipe variants --- If vis does not support vis:pipe(input, cmd), prefix the command --- with a printf call piping the result to the original command. --- @param input The input to pipe to the command --- @param cmd The external command to pipe the input to -local function _vis_pipe(input, cmd, fullscreen) - if vis_supports_pipe_buf then - return vis:pipe(input, cmd, fullscreen or false) - end - - local escaped_input = input:gsub('\'', '\'"\'"\'') - cmd = 'printf %s \'' .. escaped_input .. '\' | ' .. cmd - return vis:pipe(vis.win.file, {start = 0, finish = 0}, cmd) -end - -- get vis's pid to pass it to the language servers local vis_pid do @@ -89,7 +63,7 @@ do vis_proc_file:close() else -- fallback if /proc/self/stat - local out = capture_cmd('sh -c "echo $PPID"') + local out = util.capture_cmd('sh -c "echo $PPID"') vis_pid = tonumber(out) end end @@ -267,7 +241,7 @@ local function lspc_select(choices) end local fullscreen = lspc.menu_cmd == 'fzf' - local status, output = _vis_pipe(menu_input, lspc.menu_cmd, fullscreen) + local status, output = util.vis_pipe(menu_input, lspc.menu_cmd, fullscreen) local choice = nil if status == 0 then @@ -283,115 +257,6 @@ local function lspc_select(choices) return choice end ---- Split a path into its components --- @param path the path to split into components --- @return a table containing the path components -local function split_path_into_components(path) - local components = {} - - if #path == 1 then - return nil - end - - -- Skip the initial '/' - local start_idx = 2 - - while true do - local slash = path:find('/', start_idx + 1) - - if slash == nil then - table.insert(components, path:sub(start_idx, #path)) - return components - else - table.insert(components, path:sub(start_idx, slash - 1)) - start_idx = slash + 1 - end - end -end - ---- Get a path relative to the current working directory --- @param cwd_components Table of the path components of the CWD --- @param absolute_components table of the path components of the absolute path --- @return the relative path -local function get_relative_path(cwd_components, absolute_components) - for idx = 1, #cwd_components do - local cwd = cwd_components[idx] - local absolute = absolute_components[idx] - - if cwd ~= absolute then - local dir = '' - - -- Atleast the first component must match for us to convert - -- it to a relative path - if idx ~= 1 then - for _ = idx, #cwd_components do - dir = dir .. '..' .. '/' - end - - -- Skip trailing '/' - dir = dir:sub(1, #dir - 1) - end - - for i = idx, #absolute_components do - dir = dir .. '/' .. absolute_components[i] - end - - return dir - end - end - - -- cwd shorter than absolute path - local dir = '' - - for i = #cwd_components + 1, #absolute_components do - dir = dir .. '/' .. absolute_components[i] - end - - -- Skip leading '/' - return dir:sub(2) -end - ---- Create an iterator yielding the nth line of a file --- --- @param path The path to the file -local function file_line_iterator_to_n(path) - local file = assert(io.open(path, 'r')) - local lines = file:lines() - local last_line = nil - local last_n = 1 - - return function(n) - if n == -1 then - file:close() - return nil - end - - if n < last_n then - -- We might have multiple references on the same line, so we can - -- get called again with the previous line number - if (n + 1) == last_n then - return last_line - end - - return nil - end - - for line in lines do - if n == last_n then - last_n = last_n + 1 - last_line = line - - return line - end - - last_n = last_n + 1 - end - - -- Iterator exhausted - return nil - end -end - local function lspc_select_location(locations) -- Collect all paths with a list of their locations so we -- can sort the locations before calling file_line_iterator_to_n @@ -414,9 +279,9 @@ local function lspc_select_location(locations) end local choices = {} - local cwd_components = capture_cmd('pwd') + local cwd_components = util.capture_cmd('pwd') -- Strip trailing newline - cwd_components = split_path_into_components(cwd_components:sub(1, #cwd_components - 1)) + cwd_components = util.split_path_into_components(cwd_components:sub(1, #cwd_components - 1)) for _, path in ipairs(collected) do -- Sort positions @@ -424,7 +289,7 @@ local function lspc_select_location(locations) return a['position'].line < b['position'].line end) - local rel_path = get_relative_path(cwd_components, split_path_into_components(path)) + local rel_path = util.get_relative_path(cwd_components, path) -- Use the already open file if present to get accurate line content for references local line_iter if lspc.open_files[path] ~= nil then @@ -436,7 +301,7 @@ local function lspc_select_location(locations) return lspc.open_files[path].file.lines[n] end else - line_iter = file_line_iterator_to_n(path) + line_iter = util.file_line_iterator_to_n(path) end for _, val in ipairs(collected[path]) do @@ -479,7 +344,7 @@ local function lspc_confirm(prompt) -- local _, _, status = menu:close() local choice = nil - local status, output = _vis_pipe(choices, cmd) + local status, output = util.vis_pipe(choices, cmd) if status == 0 then -- trim newline from selection if output:sub(-1) == '\n' then diff --git a/util.lua b/util.lua new file mode 100644 index 0000000..363549e --- /dev/null +++ b/util.lua @@ -0,0 +1,164 @@ +--- Module containing simple utility functions for vis-lspc/ +-- @module util +-- @author Florian Fischer +-- @author git-bruh <prathamIN@proton.me> +-- @license GPL-3 +-- @copyright 2024 Florian Fischer +-- @copyright 2024 git-bruh <prathamIN@proton.me> +local util = {} + +local lspc + +function util.init(lspc_) + lspc = lspc_ + return util +end + +--- Execute a command and capture its output. +-- @param cmd the command to execute +-- @return the output of the command written to stdout +function util.capture_cmd(cmd) + local p = assert(io.popen(cmd, 'r')) + local s = assert(p:read('*a')) + local success, _, status = p:close() + if not success then + local err = cmd .. ' failed with exit code: ' .. status + lspc:err(err) + end + return s +end + +local vis_supports_pipe_buf = pcall(vis.pipe, vis, 'foo', 'true') + +--- Wrapper for the two vis:pipe variants. +-- If vis does not support vis:pipe(input, cmd), prefix the command +-- with a printf call piping the result to the original command. +-- @param input The input to pipe to the command +-- @param cmd The external command to pipe the input to +function util.vis_pipe(input, cmd, fullscreen) + if vis_supports_pipe_buf then + return vis:pipe(input, cmd, fullscreen or false) + end + + local escaped_input = input:gsub('\'', '\'"\'"\'') + cmd = 'printf %s \'' .. escaped_input .. '\' | ' .. cmd + return vis:pipe(vis.win.file, {start = 0, finish = 0}, cmd) +end + +--- Split a path into its components +-- @param path the path to split into components +-- @return a table containing the path components +function util.split_path_into_components(path) + local components = {} + + if #path == 1 then + return nil + end + + -- Skip the initial '/' + local start_idx = 2 + + while true do + local slash = path:find('/', start_idx + 1) + + if slash == nil then + table.insert(components, path:sub(start_idx, #path)) + return components + else + table.insert(components, path:sub(start_idx, slash - 1)) + start_idx = slash + 1 + end + end +end + +--- Get a path relative to the current working directory +-- @param cwd_components Table of the path components of the CWD +-- @param absolute_path_or_components absolute path or table of its path components +-- @return the relative path +function util.get_relative_path(cwd_components, absolute_path_or_components) + local absolute_components + if type(absolute_path_or_components) == "string" then + absolute_components = util.split_path_into_components(absolute_path_or_components) + else + absolute_components = absolute_path_or_components + end + + for idx = 1, #cwd_components do + local cwd = cwd_components[idx] + local absolute = absolute_components[idx] + + if cwd ~= absolute then + local dir = '' + + -- Atleast the first component must match for us to convert + -- it to a relative path + if idx ~= 1 then + for _ = idx, #cwd_components do + dir = dir .. '..' .. '/' + end + + -- Skip trailing '/' + dir = dir:sub(1, #dir - 1) + end + + for i = idx, #absolute_components do + dir = dir .. '/' .. absolute_components[i] + end + + return dir + end + end + + -- cwd shorter than absolute path + local dir = '' + + for i = #cwd_components + 1, #absolute_components do + dir = dir .. '/' .. absolute_components[i] + end + + -- Skip leading '/' + return dir:sub(2) +end + +--- Create an iterator yielding the nth line of a file +-- +-- @param path The path to the file +function util.file_line_iterator_to_n(path) + local file = assert(io.open(path, 'r')) + local lines = file:lines() + local last_line = nil + local last_n = 1 + + return function(n) + if n == -1 then + file:close() + return nil + end + + if n < last_n then + -- We might have multiple references on the same line, so we can + -- get called again with the previous line number + if (n + 1) == last_n then + return last_line + end + + return nil + end + + for line in lines do + if n == last_n then + last_n = last_n + 1 + last_line = line + + return line + end + + last_n = last_n + 1 + end + + -- Iterator exhausted + return nil + end +end + +return util |
