From b126b118405b90c30c47119862e6c040738ac540 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 11 Oct 2022 13:25:51 +0100 Subject: [PATCH] fix(man): support MacOS 13 MacOS 13 has changed its version of `man` to an version that doesn't properly support `man -w` (without arguments). In order to workaround this we simply fallback to $MANPATH. Fixes #20579 --- runtime/lua/man.lua | 96 +++++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua index 8ca1bb90f5..6477786dbb 100644 --- a/runtime/lua/man.lua +++ b/runtime/lua/man.lua @@ -1,6 +1,6 @@ local api, fn = vim.api, vim.fn -local find_arg = '-w' +local FIND_ARG = '-w' local localfile_arg = true -- Always use -l if possible. #6683 local buf_hls = {} @@ -12,7 +12,7 @@ local function man_error(msg) end -- Run a system command and timeout after 30 seconds. -local function man_system(args, silent, env) +local function system(cmd, silent, env) local stdout_data = {} local stderr_data = {} local stdout = vim.loop.new_pipe(false) @@ -22,8 +22,8 @@ local function man_system(args, silent, env) local exit_code local handle - handle = vim.loop.spawn('man', { - args = args, + handle = vim.loop.spawn(cmd[1], { + args = vim.list_slice(cmd, 2), stdio = { nil, stdout, stderr }, env = env, }, function(code) @@ -45,8 +45,8 @@ local function man_system(args, silent, env) stdout:close() stderr:close() if not silent then - local cmd = table.concat({ 'man', unpack(args) }, ' ') - man_error(string.format('command error: %s', cmd)) + local cmd_str = table.concat(cmd, ' ') + man_error(string.format('command error: %s', cmd_str)) end end @@ -60,13 +60,13 @@ local function man_system(args, silent, env) stdout:close() stderr:close() end - local cmd = table.concat({ 'man', unpack(args) }, ' ') - man_error(string.format('command timed out: %s', cmd)) + local cmd_str = table.concat(cmd, ' ') + man_error(string.format('command timed out: %s', cmd_str)) end if exit_code ~= 0 and not silent then - local cmd = table.concat({ 'man', unpack(args) }, ' ') - man_error(string.format("command error '%s': %s", cmd, table.concat(stderr_data))) + local cmd_str = table.concat(cmd, ' ') + man_error(string.format("command error '%s': %s", cmd_str, table.concat(stderr_data))) end return table.concat(stdout_data) @@ -269,14 +269,14 @@ local function get_path(sect, name, silent) -- -- Finally, we can avoid relying on -S or -s here since they are very -- inconsistently supported. Instead, call -w with a section and a name. - local args + local cmd if sect == '' then - args = { find_arg, name } + cmd = { 'man', FIND_ARG, name } else - args = { find_arg, sect, name } + cmd = { 'man', FIND_ARG, sect, name } end - local lines = man_system(args, silent) + local lines = system(cmd, silent) local results = vim.split(lines or {}, '\n', { trimempty = true }) if #results == 0 then @@ -342,7 +342,7 @@ end -- 2. If it still could not be found, then we try again without a section. -- 3. If still not found but $MANSECT is set, then we try again with $MANSECT -- unset. -local function verify_exists(sect, name) +local function verify_exists(sect, name, silent) if sect and sect ~= '' then local ret = get_path(sect, name, true) if ret then @@ -378,8 +378,10 @@ local function verify_exists(sect, name) end end - -- finally, if that didn't work, there is no hope - man_error('no manual entry for ' .. name) + if not silent then + -- finally, if that didn't work, there is no hope + man_error('no manual entry for ' .. name) + end end local EXT_RE = vim.regex([[\.\%([glx]z\|bz2\|lzma\|Z\)$]]) @@ -434,12 +436,12 @@ local function get_page(path, silent) manwidth = api.nvim_win_get_width(0) end - local args = localfile_arg and { '-l', path } or { path } + local cmd = localfile_arg and { 'man', '-l', path } or { 'man', path } -- Force MANPAGER=cat to ensure Vim is not recursively invoked (by man-db). -- http://comments.gmane.org/gmane.editors.vim.devel/29085 -- Set MAN_KEEP_FORMATTING so Debian man doesn't discard backspaces. - return man_system(args, silent, { + return system(cmd, silent, { 'MANPAGER=cat', 'MANWIDTH=' .. manwidth, 'MAN_KEEP_FORMATTING=1', @@ -480,38 +482,40 @@ local function format_candidate(path, psect) return '' end -local function get_paths(sect, name, do_fallback) - -- callers must try-catch this, as some `man` implementations don't support `s:find_arg` - local ok, ret = pcall(function() - local mandirs = - table.concat(vim.split(man_system({ find_arg }), '[:\n]', { trimempty = true }), ',') - local paths = fn.globpath(mandirs, 'man?/' .. name .. '*.' .. sect .. '*', false, true) - pcall(function() - -- Prioritize the result from verify_exists as it obeys b:man_default_sects. - local first = verify_exists(sect, name) - paths = vim.tbl_filter(function(v) - return v ~= first - end, paths) - paths = { first, unpack(paths) } - end) - return paths - end) +local function move_elem_to_head(list, elem) + local list1 = vim.tbl_filter(function(v) + return v ~= elem + end, list) + return { elem, unpack(list1) } +end - if not ok then - if not do_fallback then - error(ret) - end +local function get_paths(sect, name) + -- Try several sources for getting the list man directories: + -- 1. `man -w` (works on most systems) + -- 2. `manpath` + -- 3. $MANPATH + local mandirs_raw = vim.F.npcall(system, { 'man', FIND_ARG }) + or vim.F.npcall(system, { 'manpath', '-q' }) + or vim.env.MANPATH - -- Fallback to a single path, with the page we're trying to find. - ok, ret = pcall(verify_exists, sect, name) - - return { ok and ret or nil } + if not mandirs_raw then + man_error("Could not determine man directories from: 'man -w', 'manpath' or $MANPATH") end - return ret or {} + + local mandirs = table.concat(vim.split(mandirs_raw, '[:\n]', { trimempty = true }), ',') + local paths = fn.globpath(mandirs, 'man?/' .. name .. '*.' .. sect .. '*', false, true) + + -- Prioritize the result from verify_exists as it obeys b:man_default_sects. + local first = verify_exists(sect, name, true) + if first then + paths = move_elem_to_head(paths, first) + end + + return paths end local function complete(sect, psect, name) - local pages = get_paths(sect, name, false) + local pages = get_paths(sect, name) -- We remove duplicates in case the same manpage in different languages was found. return fn.uniq(fn.sort(vim.tbl_map(function(v) return format_candidate(v, psect) @@ -587,7 +591,7 @@ end function M.goto_tag(pattern, _, _) local sect, name = extract_sect_and_name_ref(pattern) - local paths = get_paths(sect, name, true) + local paths = get_paths(sect, name) local structured = {} for _, path in ipairs(paths) do