local t = require('test.testutil') local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') local command, feed = n.command, n.feed local clear = n.clear local exec_lua = n.exec_lua local fn = n.fn local nvim_prog = n.nvim_prog local matches = t.matches local tmpname = t.tmpname local eq = t.eq local pesc = vim.pesc local skip = t.skip local is_ci = t.is_ci -- Collects all names passed to find_path() after attempting ":Man foo". local function get_search_history(name) return exec_lua(function() local args = vim.split(name, ' ') local man = require('man') local res = {} --- @diagnostic disable-next-line:duplicate-set-field man.find_path = function(sect, name0) table.insert(res, { sect, name0 }) return nil end local ok, rv = pcall(man.open_page, -1, { tab = 0 }, args) assert(not ok) assert(rv and rv:match('no manual entry')) return res end) end clear() if fn.executable('man') == 0 then pending('missing "man" command', function() end) return end describe(':Man', function() before_each(function() clear() end) describe('man.lua: highlight_line()', function() local screen --- @type test.functional.ui.screen before_each(function() command('syntax on') command('set filetype=man') command('syntax off') -- Ignore syntax groups screen = Screen.new(52, 5) screen:set_default_attr_ids({ b = { bold = true }, i = { italic = true }, u = { underline = true }, bi = { bold = true, italic = true }, biu = { bold = true, italic = true, underline = true }, c = { foreground = Screen.colors.Blue }, -- control chars eob = { bold = true, foreground = Screen.colors.Blue }, -- empty line '~'s }) end) it('clears backspaces from text and adds highlights', function() feed( [[ ithis iiss aa test with _o_v_e_r_s_t_r_u_c_k text]] ) screen:expect { grid = [[ this i{c:^H}is{c:^H}s a{c:^H}a test | with _{c:^H}o_{c:^H}v_{c:^H}e_{c:^H}r_{c:^H}s_{c:^H}t_{c:^H}r_{c:^H}u_{c:^H}c_{c:^H}k tex^t | {eob:~ }|*2 | ]], } exec_lua [[require'man'.init_pager()]] screen:expect([[ ^this {b:is} {b:a} test | with {i:overstruck} text | {eob:~ }|*2 | ]]) end) it('clears escape sequences from text and adds highlights', function() feed( [[ ithis [1mis [3ma [4mtest[0m [4mwith[24m [4mescaped[24m [4mtext[24m]] ) screen:expect { grid = [=[ this {c:^[}[1mis {c:^[}[3ma {c:^[}[4mtest{c:^[}[0m | {c:^[}[4mwith{c:^[}[24m {c:^[}[4mescaped{c:^[}[24m {c:^[}[4mtext{c:^[}[24^m | {eob:~ }|*2 | ]=], } exec_lua [[require'man'.init_pager()]] screen:expect([[ ^this {b:is }{bi:a }{biu:test} | {u:with} {u:escaped} {u:text} | {eob:~ }|*2 | ]]) end) it('clears OSC 8 hyperlink markup from text', function() feed( [[ ithis ]8;;http://example.com\Link Title]8;;\]] ) screen:expect { grid = [=[ this {c:^[}]8;;http://example.com{c:^[}\Link Title{c:^[}]8;;{c:^[}^\ | {eob:~ }|*3 | ]=], } exec_lua [[require'man'.init_pager()]] screen:expect([[ ^this Link Title | {eob:~ }|*3 | ]]) end) it('highlights multibyte text', function() feed( [[ ithis iiss ああ test with _ö_v_e_r_s_t_r_u_̃_c_k te[3mxt¶[0m]] ) exec_lua [[require'man'.init_pager()]] screen:expect([[ ^this {b:is} {b:あ} test | with {i:överstrũck} te{i:xt¶} | {eob:~ }|*2 | ]]) end) it('highlights underscores based on context', function() feed( [[ i__bbeeggiinnss mmiidd__ddllee _m_i_d___d_l_e]] ) exec_lua [[require'man'.init_pager()]] screen:expect([[ {b:^_begins} | {b:mid_dle} | {i:mid_dle} | {eob:~ }| | ]]) end) it('highlights various bullet formats', function() feed([[ i· ·· +o ++oo double]]) exec_lua [[require'man'.init_pager()]] screen:expect([[ ^· {b:·} | {b:·} | {b:·} double | {eob:~ }| | ]]) end) it('handles : characters in input', function() feed([[ i[40m 0 [41m 1 [42m 2 [43m 3 [44m 4 [45m 5 [46m 6 [47m 7 [100m 8 [101m 9 [102m 10 [103m 11 [104m 12 [105m 13 [106m 14 [107m 15 [48:5:16m 16 ]]) exec_lua [[require'man'.init_pager()]] screen:expect([[ ^ 0 1 2 3 | 4 5 6 7 8 9 | 10 11 12 13 14 15 | 16 | | ]]) end) end) it('q quits in "$MANPAGER mode" (:Man!) #18281', function() -- This will hang if #18281 regresses. local args = { nvim_prog, '--headless', '+autocmd VimLeave * echo "quit works!!"', '+Man!', '+tag ls', '+call nvim_input("q")', } matches('quit works!!', fn.system(args, { 'manpage contents' })) end) it('reports non-existent man pages for absolute paths', function() skip(is_ci('cirrus')) local actual_file = tmpname() -- actual_file must be an absolute path to an existent file for us to test against it matches('^/.+', actual_file) local args = { nvim_prog, '--headless', '+:Man ' .. actual_file, '+q' } matches( ('Error detected while processing command line:\r\n' .. 'man.lua: "no manual entry for %s"'):format( pesc(actual_file) ), fn.system(args, { '' }) ) os.remove(actual_file) end) it('tries variants with spaces, underscores #22503', function() eq({ { '', 'NAME WITH SPACES' }, { '', 'NAME_WITH_SPACES' }, }, get_search_history('NAME WITH SPACES')) eq({ { '3', 'some other man' }, { '3', 'some_other_man' }, }, get_search_history('3 some other man')) eq({ { '3x', 'some other man' }, { '3x', 'some_other_man' }, }, get_search_history('3X some other man')) eq({ { '3tcl', 'some other man' }, { '3tcl', 'some_other_man' }, }, get_search_history('3tcl some other man')) eq({ { 'n', 'some other man' }, { 'n', 'some_other_man' }, }, get_search_history('n some other man')) eq({ { '', '123some other man' }, { '', '123some_other_man' }, }, get_search_history('123some other man')) eq({ { '1', 'other_man' }, { '1', 'other_man' }, }, get_search_history('other_man(1)')) end) it('can complete', function() t.skip(t.is_os('mac') and t.is_arch('x86_64'), 'not supported on intel mac') eq( true, exec_lua(function() return #require('man').man_complete('f', 'Man g') > 0 end) ) end) end)