man_spec.lua (10108B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 local Screen = require('test.functional.ui.screen') 4 5 local command, feed = n.command, n.feed 6 local dedent = t.dedent 7 local clear = n.clear 8 local exec_lua = n.exec_lua 9 local fn = n.fn 10 local nvim_prog = n.nvim_prog 11 local matches = t.matches 12 local tmpname = t.tmpname 13 local eq = t.eq 14 local pesc = vim.pesc 15 local skip = t.skip 16 local is_ci = t.is_ci 17 18 -- Collects all names passed to find_path() after attempting ":Man foo". 19 local function get_search_history(name) 20 return exec_lua(function() 21 local args = vim.split(name, ' ') 22 local man = require('man') 23 local res = {} 24 --- @diagnostic disable-next-line:duplicate-set-field 25 man._find_path = function(name0, sect) 26 table.insert(res, { sect, name0 }) 27 return nil 28 end 29 local err = man.open_page(-1, { tab = 0 }, args) 30 assert(err and err:match('no manual entry')) 31 return res 32 end) 33 end 34 35 clear() 36 if fn.executable('man') == 0 then 37 pending('missing "man" command', function() end) 38 return 39 end 40 41 describe(':Man', function() 42 before_each(function() 43 clear() 44 end) 45 46 describe('man.lua: highlight_line()', function() 47 local screen --- @type test.functional.ui.screen 48 49 before_each(function() 50 command('syntax on') 51 command('set filetype=man') 52 command('syntax off') -- Ignore syntax groups 53 screen = Screen.new(52, 5) 54 screen:set_default_attr_ids({ 55 b = { bold = true }, 56 i = { italic = true }, 57 u = { underline = true }, 58 bi = { bold = true, italic = true }, 59 biu = { bold = true, italic = true, underline = true }, 60 c = { foreground = Screen.colors.Blue }, -- control chars 61 eob = { bold = true, foreground = Screen.colors.Blue }, -- empty line '~'s 62 }) 63 end) 64 65 it('clears backspaces from text and adds highlights', function() 66 feed( 67 dedent( 68 [[ 69 ithis i<C-v><C-h>is<C-v><C-h>s a<C-v><C-h>a test 70 with _<C-v><C-h>o_<C-v><C-h>v_<C-v><C-h>e_<C-v><C-h>r_<C-v><C-h>s_<C-v><C-h>t_<C-v><C-h>r_<C-v><C-h>u_<C-v><C-h>c_<C-v><C-h>k text<ESC>]] 71 ) 72 ) 73 74 screen:expect { 75 grid = [[ 76 this i{c:^H}is{c:^H}s a{c:^H}a test | 77 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 | 78 {eob:~ }|*2 79 | 80 ]], 81 } 82 83 exec_lua [[require'man'.init_pager()]] 84 85 screen:expect([[ 86 ^this {b:is} {b:a} test | 87 with {i:overstruck} text | 88 {eob:~ }|*2 89 | 90 ]]) 91 end) 92 93 it('clears escape sequences from text and adds highlights', function() 94 feed( 95 dedent( 96 [[ 97 ithis <C-v><ESC>[1mis <C-v><ESC>[3ma <C-v><ESC>[4mtest<C-v><ESC>[0m 98 <C-v><ESC>[4mwith<C-v><ESC>[24m <C-v><ESC>[4mescaped<C-v><ESC>[24m <C-v><ESC>[4mtext<C-v><ESC>[24m<ESC>]] 99 ) 100 ) 101 102 screen:expect { 103 grid = [=[ 104 this {c:^[}[1mis {c:^[}[3ma {c:^[}[4mtest{c:^[}[0m | 105 {c:^[}[4mwith{c:^[}[24m {c:^[}[4mescaped{c:^[}[24m {c:^[}[4mtext{c:^[}[24^m | 106 {eob:~ }|*2 107 | 108 ]=], 109 } 110 111 exec_lua [[require'man'.init_pager()]] 112 113 screen:expect([[ 114 ^this {b:is }{bi:a }{biu:test} | 115 {u:with} {u:escaped} {u:text} | 116 {eob:~ }|*2 117 | 118 ]]) 119 end) 120 121 it('clears OSC 8 hyperlink markup from text', function() 122 feed( 123 dedent( 124 [[ 125 ithis <C-v><ESC>]8;;http://example.com<C-v><ESC>\Link Title<C-v><ESC>]8;;<C-v><ESC>\<ESC>]] 126 ) 127 ) 128 129 screen:expect { 130 grid = [=[ 131 this {c:^[}]8;;http://example.com{c:^[}\Link Title{c:^[}]8;;{c:^[}^\ | 132 {eob:~ }|*3 133 | 134 ]=], 135 } 136 137 exec_lua [[require'man'.init_pager()]] 138 139 screen:expect([[ 140 ^this Link Title | 141 {eob:~ }|*3 142 | 143 ]]) 144 end) 145 146 it('highlights multibyte text', function() 147 feed( 148 dedent( 149 [[ 150 ithis i<C-v><C-h>is<C-v><C-h>s あ<C-v><C-h>あ test 151 with _<C-v><C-h>ö_<C-v><C-h>v_<C-v><C-h>e_<C-v><C-h>r_<C-v><C-h>s_<C-v><C-h>t_<C-v><C-h>r_<C-v><C-h>u_<C-v><C-h>̃_<C-v><C-h>c_<C-v><C-h>k te<C-v><ESC>[3mxt¶<C-v><ESC>[0m<ESC>]] 152 ) 153 ) 154 exec_lua [[require'man'.init_pager()]] 155 156 screen:expect([[ 157 ^this {b:is} {b:あ} test | 158 with {i:överstrũck} te{i:xt¶} | 159 {eob:~ }|*2 160 | 161 ]]) 162 end) 163 164 it('highlights underscores based on context', function() 165 feed( 166 dedent( 167 [[ 168 i_<C-v><C-h>_b<C-v><C-h>be<C-v><C-h>eg<C-v><C-h>gi<C-v><C-h>in<C-v><C-h>ns<C-v><C-h>s 169 m<C-v><C-h>mi<C-v><C-h>id<C-v><C-h>d_<C-v><C-h>_d<C-v><C-h>dl<C-v><C-h>le<C-v><C-h>e 170 _<C-v><C-h>m_<C-v><C-h>i_<C-v><C-h>d_<C-v><C-h>__<C-v><C-h>d_<C-v><C-h>l_<C-v><C-h>e<ESC>]] 171 ) 172 ) 173 exec_lua [[require'man'.init_pager()]] 174 175 screen:expect([[ 176 {b:^_begins} | 177 {b:mid_dle} | 178 {i:mid_dle} | 179 {eob:~ }| 180 | 181 ]]) 182 end) 183 184 it('highlights various bullet formats', function() 185 feed(dedent([[ 186 i· ·<C-v><C-h>· 187 +<C-v><C-h>o 188 +<C-v><C-h>+<C-v><C-h>o<C-v><C-h>o double<ESC>]])) 189 exec_lua [[require'man'.init_pager()]] 190 191 screen:expect([[ 192 ^· {b:·} | 193 {b:·} | 194 {b:·} double | 195 {eob:~ }| 196 | 197 ]]) 198 end) 199 200 it('handles : characters in input', function() 201 feed(dedent([[ 202 i<C-v><C-[>[40m 0 <C-v><C-[>[41m 1 <C-v><C-[>[42m 2 <C-v><C-[>[43m 3 203 <C-v><C-[>[44m 4 <C-v><C-[>[45m 5 <C-v><C-[>[46m 6 <C-v><C-[>[47m 7 <C-v><C-[>[100m 8 <C-v><C-[>[101m 9 204 <C-v><C-[>[102m 10 <C-v><C-[>[103m 11 <C-v><C-[>[104m 12 <C-v><C-[>[105m 13 <C-v><C-[>[106m 14 <C-v><C-[>[107m 15 205 <C-v><C-[>[48:5:16m 16 <ESC>]])) 206 exec_lua [[require'man'.init_pager()]] 207 208 screen:expect([[ 209 ^ 0 1 2 3 | 210 4 5 6 7 8 9 | 211 10 11 12 13 14 15 | 212 16 | 213 | 214 ]]) 215 end) 216 end) 217 218 it('q quits in "$MANPAGER mode" (:Man!) #18281', function() 219 -- This will hang if #18281 regresses. 220 local args = { 221 nvim_prog, 222 '--headless', 223 '+autocmd VimLeave * echo "quit works!!"', 224 '+Man!', 225 '+tag ls', 226 '+call nvim_input("q")', 227 } 228 matches('quit works!!', fn.system(args, { 'manpage contents' })) 229 end) 230 231 it('raw manpage into (:Man!) creates a new buffer #30132', function() 232 local args = { 233 nvim_prog, 234 '--headless', 235 '+Man! foo', 236 '+echo bufname()', 237 '+enew', 238 '+Man! foo', 239 '+echo bufname()', 240 '+enew', 241 '+Man! foo', 242 '+echo bufname()', 243 '+q', 244 } 245 local out = fn.system(args, { 'manpage contents' }) 246 assert(out and out:match('man://%?new=%d')) 247 end) 248 249 it('reports non-existent man pages for absolute paths', function() 250 skip(is_ci('cirrus')) 251 local actual_file = tmpname() 252 -- actual_file must be an absolute path to an existent file for us to test against it 253 matches('^/.+', actual_file) 254 local args = { nvim_prog, '--headless', '+:Man ' .. actual_file, '+q' } 255 matches( 256 ('Error in command line:\r\n' .. 'man.lua: no manual entry for %s'):format(pesc(actual_file)), 257 fn.system(args, { '' }) 258 ) 259 os.remove(actual_file) 260 end) 261 262 it('tries variants with spaces, underscores #22503', function() 263 eq({ 264 { vim.NIL, 'NAME WITH SPACES' }, 265 { vim.NIL, 'NAME_WITH_SPACES' }, 266 }, get_search_history('NAME WITH SPACES')) 267 eq({ 268 { '3', 'some other man' }, 269 { '3', 'some_other_man' }, 270 }, get_search_history('3 some other man')) 271 eq({ 272 { '3x', 'some other man' }, 273 { '3x', 'some_other_man' }, 274 }, get_search_history('3X some other man')) 275 eq({ 276 { '3tcl', 'some other man' }, 277 { '3tcl', 'some_other_man' }, 278 }, get_search_history('3tcl some other man')) 279 eq({ 280 { 'n', 'some other man' }, 281 { 'n', 'some_other_man' }, 282 }, get_search_history('n some other man')) 283 eq({ 284 { vim.NIL, '123some other man' }, 285 { vim.NIL, '123some_other_man' }, 286 }, get_search_history('123some other man')) 287 eq({ 288 { '1', 'other_man' }, 289 { '1', 'other_man' }, 290 }, get_search_history('other_man(1)')) 291 end) 292 293 it('can complete', function() 294 eq( 295 true, 296 exec_lua(function() 297 return #require('man').man_complete('f', 'Man f') > 0 298 end) 299 ) 300 end) 301 end)