mirror of
https://github.com/neovim/neovim.git
synced 2026-09-13 17:41:10 +00:00
Problem: Generating helptags builds and queries a full vimdoc syntax tree. This is much slower than the previous scanner and also happens before discovering that the tags file cannot be written. Solution: Extract tags with an LPeg grammar, reading each file once and skipping example blocks and ordinary text. Open the output first so unwritable directories can return immediately. This restores legacy handling of tags inside inline code spans. On an arm64 Mac, the 5.36 MB runtime corpus takes 24 ms instead of 604 ms, with identical generated tags. AI-assisted
524 lines
17 KiB
Lua
524 lines
17 KiB
Lua
local t = require('test.testutil')
|
|
local n = require('test.functional.testnvim')()
|
|
|
|
local describe, it, before_each, after_each, finally =
|
|
t.describe, t.it, t.before_each, t.after_each, t.finally
|
|
local clear = n.clear
|
|
local eval = n.eval
|
|
local command = n.command
|
|
local eq = t.eq
|
|
local fn = n.fn
|
|
local api = n.api
|
|
local mkdir = t.mkdir
|
|
local rmdir = n.rmdir
|
|
local write_file = t.write_file
|
|
|
|
local cursor = n.api.nvim_win_set_cursor
|
|
|
|
local function buf_word()
|
|
local word = n.fn.expand('<cWORD>')
|
|
local bufname = n.fn.fnamemodify(n.fn.bufname('%'), ':t')
|
|
return { word, bufname }
|
|
end
|
|
|
|
local function open_helptag()
|
|
-- n.exec [[:normal! K]]
|
|
n.exec [[:help!]]
|
|
local rv = buf_word()
|
|
if n.fn.winnr('$') > 1 then
|
|
n.command('close')
|
|
end
|
|
return rv
|
|
end
|
|
|
|
local function set_lines(text)
|
|
n.exec_lua(
|
|
[[
|
|
vim.cmd'%delete _'
|
|
vim.api.nvim_paste(vim.text.indent(-1, ...), false, -1)
|
|
]],
|
|
text
|
|
)
|
|
end
|
|
|
|
describe(':help', function()
|
|
before_each(clear)
|
|
|
|
it('{subject}', function()
|
|
n.command('helptags ++t $VIMRUNTIME/doc')
|
|
local function check_tag(cmd, tag)
|
|
local cmd_ok = t.pcall(n.command, cmd)
|
|
local found = n.api.nvim_get_current_line():find(tag, 1, true)
|
|
local errmsg = (not cmd_ok and 'command failed') or (not found and 'tag not found') or '?'
|
|
assert(
|
|
cmd_ok and found,
|
|
string.format('Expected `:%s` to jump to tag `%s`, but %s', cmd, tag, errmsg)
|
|
)
|
|
n.command('helpclose')
|
|
end
|
|
|
|
check_tag('help', '*help.txt*')
|
|
check_tag('help |', '*bar*')
|
|
check_tag('help "*', '*quotestar*')
|
|
check_tag('help ch??khealth', '*:checkhealth*')
|
|
|
|
check_tag([[help \\star]], [[*/\star*]])
|
|
check_tag('help /*', [[*/\star*]])
|
|
check_tag('help ?', '*?*')
|
|
check_tag('help ??', '*??*')
|
|
check_tag('help expr-!=?', '*expr-!=?*')
|
|
|
|
check_tag('help /<cr>', '*/<CR>*')
|
|
check_tag([[help %(\\)]], [[*/\%(\)*]])
|
|
check_tag('help %^', [[/\%^]])
|
|
check_tag('help /_^G', '/_CTRL-G')
|
|
check_tag([[help \0]], [[\0]])
|
|
|
|
check_tag('help !', '*!*')
|
|
check_tag('help #{}', '*#{}*')
|
|
check_tag('help %:8', '*%:8*')
|
|
check_tag('help &', '*&*')
|
|
check_tag([[help '']], [[*''*]])
|
|
check_tag([[help '(]], [[*'(*]])
|
|
check_tag([[help '0]], [[*'0*]])
|
|
check_tag([[help 'ac']], [[*'ac'*]])
|
|
check_tag([[help '{]], [[*'{*]])
|
|
check_tag('help )', '*)*')
|
|
check_tag('help +', '*+*')
|
|
|
|
check_tag('help +opt', '*++opt*')
|
|
check_tag('help --', '*--*')
|
|
check_tag('help -?', '*-?*')
|
|
check_tag('help .', '*.*')
|
|
check_tag('help :', '*:*')
|
|
check_tag([[help :'}]], [[*:'}*]])
|
|
check_tag('help :,', '*:,*')
|
|
check_tag('help :<abuf>', '*:<abuf>*')
|
|
check_tag([[help :\|]], [[*:\bar*]])
|
|
check_tag([[help :\\|]], [[*:\bar*]])
|
|
check_tag('help _', '*_*')
|
|
check_tag('help `', '*`*')
|
|
check_tag('help `(', '*`(*')
|
|
check_tag([[help `:ls`.]], [[*:ls*]])
|
|
|
|
check_tag('help [', '*[*')
|
|
check_tag('help [#', '*[#*')
|
|
check_tag([[help [']], [[*['*]])
|
|
check_tag('help [(', '*[(*')
|
|
check_tag('help [++opt]', '*[++opt]*')
|
|
check_tag('help [:tab:]', '*[:tab:]*')
|
|
check_tag('help [count]', '*[count]*')
|
|
check_tag('help :[range]', '*:[range]*')
|
|
check_tag('help [<space>', '[<Space>')
|
|
check_tag('help ]_^D', ']_CTRL-D')
|
|
check_tag('help :set^=', '*:set^=*')
|
|
|
|
check_tag([[help $HOME]], [[*$HOME*]])
|
|
|
|
check_tag('help <C-pagedown>', '*CTRL-<PageDown>*')
|
|
check_tag('help ^A', '*CTRL-A*')
|
|
check_tag('help ^W_+', '*CTRL-W_+*')
|
|
check_tag('help ^W<up>', '*CTRL-W_<Up>*')
|
|
check_tag('help ^W>', '*CTRL-W_>*')
|
|
check_tag('help ^W^]', '*CTRL-W_CTRL-]*')
|
|
check_tag('help ^W^', '*CTRL-W_^*')
|
|
check_tag('help ^W|', '*CTRL-W_bar*')
|
|
check_tag('help ^Wg<tab>', '*CTRL-W_g<Tab>*')
|
|
check_tag('help ^]', '*CTRL-]*')
|
|
check_tag('help ^{char}', 'CTRL-{char}')
|
|
check_tag('help [^L', '[_CTRL-L')
|
|
check_tag('help <C-', '*<C-*')
|
|
check_tag('help <S-CR>', '*<S-CR>*')
|
|
check_tag('help <<', '*<<*')
|
|
check_tag('help <>', '*<>*')
|
|
check_tag([[help i^x^y]], '*i_CTRL-X_CTRL-Y*')
|
|
check_tag([[help CTRL-\_CTRL-N]], [[*CTRL-\_CTRL-N*]])
|
|
check_tag([[help i_CTRL-U-default]], [[*i_CTRL-U-default*]])
|
|
check_tag([[help expr-=~?]], [[*expr-=~?*]])
|
|
check_tag([[help map-CTRL-C]], '*map-CTRL-C*')
|
|
check_tag([=[help telnet-CTRL-]]=], '*telnet-CTRL-]*')
|
|
check_tag([[help c_CTRL-SHIFT-V]], '*c_CTRL-SHIFT-V*')
|
|
check_tag([[help i_CTRL-SHIFT-V]], '*i_CTRL-SHIFT-V*')
|
|
check_tag([[help c_CTRL-SHIFT-Q]], '*c_CTRL-SHIFT-Q*')
|
|
|
|
check_tag([[exe "help i\<C-\>\<C-G>"]], [[*i_CTRL-\_CTRL-G*]])
|
|
check_tag([[exe "help \<C-V>"]], '*CTRL-V*')
|
|
check_tag([[exe "help! arglistid([{winnr})"]], '*arglistid()*')
|
|
check_tag([[exe "help! 'autoindent'."]], [[*'autoindent'*]])
|
|
|
|
check_tag('exusage', '*:index*')
|
|
check_tag('viusage', '*normal-index*')
|
|
|
|
-- Test cases for removed exceptions
|
|
check_tag('help /\\(\\)', '*/\\(\\)*')
|
|
check_tag('help :s\\=', '*:s\\=*')
|
|
check_tag([[help expr-']], [[*expr-'*]])
|
|
check_tag('help expr-barbar', '*expr-barbar*')
|
|
check_tag([[help s/\9]], [[*s/\9*]])
|
|
check_tag([[help s/\U]], [[*s/\U*]])
|
|
check_tag([[help s/\~]], [[*s/\~*]])
|
|
check_tag([[help \|]], [[*/\bar*]])
|
|
end)
|
|
|
|
it('":help!" (bang + no args) guesses the best tag near cursor', function()
|
|
n.command('helptags ++t $VIMRUNTIME/doc')
|
|
-- n.command('enew')
|
|
-- n.command('set filetype=help')
|
|
-- n.command [[set keywordprg=:help]]
|
|
|
|
-- Failure modes:
|
|
set_lines ''
|
|
cursor(0, { 1, 1 })
|
|
t.matches('E349: No identifier under cursor', t.pcall_err(n.exec, [[:help!]]))
|
|
|
|
set_lines 'xxxxxxxxx'
|
|
cursor(0, { 1, 4 })
|
|
t.matches('E149: No help for xxxxxxxxx', t.pcall_err(n.exec, [[:help!]]))
|
|
|
|
-- Success:
|
|
|
|
set_lines 'some plain text'
|
|
cursor(0, { 1, 5 }) -- on 'plain'
|
|
eq({ '*ft-plaintex-syntax*', 'syntax.txt' }, open_helptag())
|
|
|
|
set_lines ':help command'
|
|
cursor(0, { 1, 4 })
|
|
eq({ '*:help*', 'helphelp.txt' }, open_helptag())
|
|
|
|
set_lines ' :help command'
|
|
cursor(0, { 1, 5 })
|
|
eq({ '*:command*', 'map.txt' }, open_helptag())
|
|
|
|
set_lines 'v:version name'
|
|
cursor(0, { 1, 5 })
|
|
eq({ '*v:version*', 'vvars.txt' }, open_helptag())
|
|
cursor(0, { 1, 2 })
|
|
eq({ '*v:version*', 'vvars.txt' }, open_helptag())
|
|
|
|
set_lines "See 'option' for more."
|
|
cursor(0, { 1, 6 }) -- on 'option'
|
|
eq({ "*'option'*", 'helphelp.txt' }, open_helptag())
|
|
|
|
set_lines ':command-nargs'
|
|
cursor(0, { 1, 7 }) -- on 'nargs'
|
|
eq({ '*:command-nargs*', 'map.txt' }, open_helptag())
|
|
|
|
set_lines '|("vim.lsp.foldtext()")|'
|
|
cursor(0, { 1, 10 })
|
|
eq({ '*vim.lsp.foldtext()*', 'lsp.txt' }, open_helptag())
|
|
|
|
set_lines 'nvim_buf_detach_event[{buf}]'
|
|
cursor(0, { 1, 10 })
|
|
eq({ '*nvim_buf_detach_event*', 'api.txt' }, open_helptag())
|
|
|
|
set_lines '{buf}'
|
|
cursor(0, { 1, 1 })
|
|
eq({ '*:buf*', 'windows.txt' }, open_helptag())
|
|
|
|
set_lines '(`vim.lsp.ClientConfig`)'
|
|
cursor(0, { 1, 1 })
|
|
eq({ '*vim.lsp.ClientConfig*', 'lsp.txt' }, open_helptag())
|
|
|
|
set_lines "vim.lsp.enable('clangd')"
|
|
cursor(0, { 1, 3 })
|
|
eq({ '*vim.lsp.enable()*', 'lsp.txt' }, open_helptag())
|
|
|
|
set_lines "vim.lsp.enable('clangd')"
|
|
cursor(0, { 1, 6 })
|
|
eq({ '*vim.lsp.enable()*', 'lsp.txt' }, open_helptag())
|
|
|
|
set_lines "vim.lsp.enable('clangd')"
|
|
cursor(0, { 1, 9 })
|
|
eq({ '*vim.lsp.enable()*', 'lsp.txt' }, open_helptag())
|
|
|
|
set_lines 'assert(vim.lsp.get_client_by_id(client_id))'
|
|
cursor(0, { 1, 12 })
|
|
eq({ '*vim.lsp.get_client_by_id()*', 'lsp.txt' }, open_helptag())
|
|
|
|
set_lines "vim.api.nvim_create_autocmd('LspAttach', {"
|
|
cursor(0, { 1, 7 })
|
|
eq({ '*nvim_create_autocmd()*', 'api.txt' }, open_helptag())
|
|
|
|
-- Falls back to <cword> when all trimming fails.
|
|
set_lines "'@lsp.type.function'"
|
|
cursor(0, { 1, 2 }) -- on 'lsp'
|
|
eq({ '*lsp*', 'lsp.txt' }, open_helptag())
|
|
set_lines "'@lsp.type.function'"
|
|
cursor(0, { 1, 14 }) -- on 'function'
|
|
eq({ '*:function*', 'userfunc.txt' }, open_helptag())
|
|
|
|
set_lines ' • `@lsp.type.<type>.<ft>` for the type'
|
|
cursor(0, { 1, 6 }) -- on backtick '`' (byte 6, after 2 spaces + 3-byte '•' + space)
|
|
eq({ '*lsp*', 'lsp.txt' }, open_helptag())
|
|
|
|
set_lines [[
|
|
- `root_dir` usages akin to >lua
|
|
root_dir = require'lspconfig.util'.root_pattern(...)
|
|
<
|
|
require'lspconfig.util'.root_pattern(...)
|
|
]]
|
|
cursor(0, { 2, 17 }) -- on "require"
|
|
eq({ '*require()*', 'luaref.txt' }, open_helptag())
|
|
|
|
set_lines '`:lsp restart`. You'
|
|
cursor(0, { 1, 6 }) -- on "restart"
|
|
eq({ '*:restart*', 'gui.txt' }, open_helptag())
|
|
|
|
--
|
|
-- Test with actual helpfiles. This affects getcompletion(…,'help') ...
|
|
--
|
|
|
|
n.command(':help lua')
|
|
n.feed('gg/package.searchpath<cr>')
|
|
eq({ "vim.cmd.edit(package.searchpath('jit.p',", 'lua.txt' }, buf_word())
|
|
-- ^ cursor on "package"
|
|
n.command(':help!')
|
|
eq({ '*packages*', 'pack.txt' }, buf_word())
|
|
|
|
n.command(':help lsp')
|
|
n.feed('gg/type.<lt>type><cr>')
|
|
eq({ '`@lsp.type.<type>.<ft>`', 'lsp.txt' }, buf_word())
|
|
-- ^ cursor on "type"
|
|
n.command(':help!')
|
|
eq({ '*type()*', 'vimfn.txt' }, buf_word())
|
|
n.feed('<c-o>f<lt>')
|
|
eq({ '`@lsp.type.<type>.<ft>`', 'lsp.txt' }, buf_word())
|
|
-- ^ cursor on "<"
|
|
n.command(':help!')
|
|
|
|
n.command(':help lsp')
|
|
n.feed('gg/codelens.run()|<cr>')
|
|
eq({ '|vim.lsp.codelens.run()|', 'lsp.txt' }, buf_word())
|
|
-- ^ cursor on "codelens"
|
|
n.command(':help!')
|
|
eq({ '*vim.lsp.codelens.run()*', 'lsp.txt' }, buf_word())
|
|
end)
|
|
|
|
it('K resolves generic type names in help', function()
|
|
command('helptags ++t $VIMRUNTIME/doc')
|
|
for _, ty in ipairs({
|
|
'vim.async.Task<R>',
|
|
'vim.async.Task<R...>',
|
|
'vim.async.Task<R>[]',
|
|
}) do
|
|
command('help lua-async')
|
|
command('setlocal keywordprg=:help!')
|
|
assert(fn.search('\\V(`' .. ty .. '`)', 'W') > 0)
|
|
command('normal! fTK')
|
|
eq({ '*vim.async.Task*', 'lua-async.txt' }, buf_word())
|
|
end
|
|
end)
|
|
|
|
it('K resolves nullable type names in help', function()
|
|
command('helptags ++t $VIMRUNTIME/doc')
|
|
for _, ref in ipairs({
|
|
{ 'lsp', 'vim.lsp.Client', '?' },
|
|
{ 'diagnostic', 'vim.Diagnostic', '?' },
|
|
{ 'diagnostic', 'vim.Diagnostic', '[]?' },
|
|
}) do
|
|
command('help ' .. ref[1])
|
|
command('setlocal keywordprg=:help!')
|
|
assert(fn.search('\\V(`' .. ref[2] .. ref[3] .. '`)', 'W') > 0)
|
|
command('normal! 2lK')
|
|
eq({ '*' .. ref[2] .. '*', ref[1] .. '.txt' }, buf_word())
|
|
end
|
|
end)
|
|
|
|
it('window closed makes cursor return to a valid win/buf #9773', function()
|
|
n.add_builddir_to_rtp()
|
|
command('help help')
|
|
eq(1001, fn.win_getid())
|
|
command('quit')
|
|
eq(1000, fn.win_getid())
|
|
|
|
command('autocmd WinNew * wincmd p')
|
|
|
|
command('help help')
|
|
-- Window 1002 is opened, but the autocmd switches back to 1000 and
|
|
-- creates the help buffer there instead.
|
|
eq(1000, fn.win_getid())
|
|
command('quit')
|
|
-- Before #9773, Nvim would crash on quitting the help window.
|
|
eq(1002, fn.win_getid())
|
|
end)
|
|
|
|
it('multibyte help tags work #23975', function()
|
|
mkdir('Xhelptags')
|
|
finally(function()
|
|
rmdir('Xhelptags')
|
|
end)
|
|
mkdir('Xhelptags/doc')
|
|
write_file('Xhelptags/doc/Xhelptags.txt', '*…*')
|
|
command('helptags Xhelptags/doc')
|
|
command('set rtp+=Xhelptags')
|
|
command('help …')
|
|
eq('*…*', api.nvim_get_current_line())
|
|
end)
|
|
end)
|
|
|
|
describe(':helptags', function()
|
|
before_each(function()
|
|
for _, sfx in ipairs({ '', '2' }) do
|
|
fn.mkdir(('Xhelptags%s/doc'):format(sfx), 'p')
|
|
for _, tag in ipairs({ 'Xa', 'Xb' }) do
|
|
write_file(('Xhelptags%s/doc/%s%s.txt'):format(sfx, tag, sfx), ('*%s%s*'):format(tag, sfx))
|
|
end
|
|
end
|
|
|
|
clear()
|
|
command('set rtp+=Xhelptags,Xhelptags2')
|
|
end)
|
|
|
|
after_each(function()
|
|
rmdir('Xhelptags')
|
|
rmdir('Xhelptags2')
|
|
end)
|
|
|
|
it('requires an argument', function()
|
|
local msg = t.pcall_err(command, 'helptags')
|
|
eq(true, msg:find('E471') ~= nil)
|
|
end)
|
|
|
|
it('{dir}', function()
|
|
-- Helpfiles in sub-directories are found and named relative to {dir}.
|
|
fn.mkdir('Xhelptags/doc/sub', 'p')
|
|
-- A "|" in a tag would break |links|, and "*Xd" is not closed, so neither is a tag.
|
|
write_file('Xhelptags/doc/sub/Xc.txt', '*Xc*\n*X|c*\n*Xd\n')
|
|
-- CRLF helpfile: "\r" must not confuse the parser into finding tags in an example.
|
|
write_file('Xhelptags/doc/Xe.txt', '*Xe*\r\n>\r\n\t+-----+\r\n\t|/* a.c */ |/* b.c */ |\r\n')
|
|
|
|
for _, dir in ipairs({
|
|
'Xhelptags/doc',
|
|
'./Xhelptags/doc',
|
|
fn.fnamemodify('Xhelptags/doc', ':p'),
|
|
}) do
|
|
command('helptags ' .. fn.fnameescape(dir))
|
|
eq({
|
|
'Xa\tXa.txt\t/*Xa*',
|
|
'Xb\tXb.txt\t/*Xb*',
|
|
'Xc\tsub/Xc.txt\t/*Xc*',
|
|
'Xe\tXe.txt\t/*Xe*',
|
|
}, fn.readfile('Xhelptags/doc/tags'))
|
|
end
|
|
|
|
command('help Xa')
|
|
eq('*Xa*', api.nvim_get_current_line())
|
|
|
|
command('help Xc')
|
|
eq('*Xc*', api.nvim_get_current_line())
|
|
end)
|
|
|
|
it('ignores tags in examples and resumes after unindented text', function()
|
|
for _, newline in ipairs({ '\n', '\r\n' }) do
|
|
write_file(
|
|
'Xhelptags/doc/Xa.txt',
|
|
table.concat({
|
|
'*Xa* *Xz*',
|
|
'Some ordinary prose.',
|
|
'prefix*invalid* *invalid*suffix *bad|tag* **',
|
|
'>lua',
|
|
'',
|
|
' *example*',
|
|
'End of example.',
|
|
'',
|
|
' *Xc*',
|
|
'More ordinary prose.',
|
|
'*Xd*', -- No final newline.
|
|
}, newline),
|
|
true
|
|
)
|
|
command('helptags Xhelptags/doc')
|
|
eq({
|
|
'Xa\tXa.txt\t/*Xa*',
|
|
'Xb\tXb.txt\t/*Xb*',
|
|
'Xc\tXa.txt\t/*Xc*',
|
|
'Xd\tXa.txt\t/*Xd*',
|
|
'Xz\tXa.txt\t/*Xz*',
|
|
}, fn.readfile('Xhelptags/doc/tags'))
|
|
end
|
|
end)
|
|
|
|
it('overwrites existing tags file', function()
|
|
write_file('Xhelptags/doc/tags', 'Xstale Xold.txt /*Xstale*', nil, true)
|
|
write_file('Xhelptags/doc/Xa.txt', 'no tags here', nil, true)
|
|
write_file('Xhelptags/doc/Xb.txt', 'no tags here', nil, true)
|
|
|
|
command('helptags Xhelptags/doc')
|
|
eq({}, eval("readfile('Xhelptags/doc/tags')"))
|
|
end)
|
|
|
|
it('reports E150 E151 E152', function()
|
|
eq(true, t.pcall_err(command, 'helptags Xhelptags/doc/Xa.txt'):find('E150') ~= nil)
|
|
|
|
fn.mkdir('Xhelptags/Xempty', 'p')
|
|
eq(true, t.pcall_err(command, 'helptags Xhelptags/Xempty'):find('E151') ~= nil)
|
|
|
|
-- A directory named "tags" cannot be opened for writing.
|
|
fn.mkdir('Xhelptags/doc/tags', 'p')
|
|
eq(true, t.pcall_err(command, 'helptags Xhelptags/doc'):find('E152') ~= nil)
|
|
end)
|
|
|
|
it('ALL', function()
|
|
command('helptags ALL')
|
|
|
|
eq(eval("['Xa Xa.txt /*Xa*','Xb Xb.txt /*Xb*']"), eval("readfile('Xhelptags/doc/tags')"))
|
|
eq(eval("['Xa2 Xa2.txt /*Xa2*','Xb2 Xb2.txt /*Xb2*']"), eval("readfile('Xhelptags2/doc/tags')"))
|
|
|
|
command('help Xa2')
|
|
eq('*Xa2*', api.nvim_get_current_line())
|
|
end)
|
|
|
|
it('++t', function()
|
|
write_file('Xhelptags/doc/Xa.nlx', '*Xa*', nil, true)
|
|
|
|
command('helptags ++t Xhelptags/doc')
|
|
eq('help-tags tags 1', eval("readfile('Xhelptags/doc/tags')[-1]"))
|
|
-- Each language gets a "help-tags" tag naming its own tags file.
|
|
eq('help-tags tags-nl 1', eval("readfile('Xhelptags/doc/tags-nl')[-1]"))
|
|
end)
|
|
|
|
it('generates help-tag tag for VIMRUNTIME', function()
|
|
command('let $VIMRUNTIME="Xhelptags"')
|
|
command('helptags Xhelptags/doc')
|
|
eq('help-tags tags 1', eval("readfile('Xhelptags/doc/tags')[-1]"))
|
|
end)
|
|
|
|
it('errors on duplicate tags', function()
|
|
-- duplicate tags in different files
|
|
write_file('Xhelptags/doc/Xd.txt', '*Xa*', nil, true)
|
|
local msg = t.pcall_err(command, 'helptags Xhelptags/doc')
|
|
eq(true, msg:find('E154: Duplicate tag "Xa" in Xd.txt and Xa.txt', 1, true) ~= nil)
|
|
|
|
-- tags file should still be generated
|
|
eq(1, eval("filereadable('Xhelptags/doc/tags')"))
|
|
|
|
os.remove('Xhelptags/doc/Xd.txt')
|
|
|
|
-- duplicate tags in same file
|
|
write_file('Xhelptags/doc/Xa.txt', '\n*Xa*', nil, true)
|
|
|
|
msg = t.pcall_err(command, 'helptags Xhelptags/doc')
|
|
eq(true, msg:find('E154: Duplicate tag "Xa" in Xa.txt', 1, true) ~= nil)
|
|
|
|
eq(1, eval("filereadable('Xhelptags/doc/tags')"))
|
|
|
|
-- Duplicates do not abort the run: other languages are still processed.
|
|
write_file('Xhelptags/doc/Xa.nlx', '*Xnl*', nil, true)
|
|
|
|
msg = t.pcall_err(command, 'helptags Xhelptags/doc')
|
|
eq(true, msg:find('E154') ~= nil)
|
|
eq(false, msg:find('E5108') ~= nil)
|
|
eq(1, eval("filereadable('Xhelptags/doc/tags-nl')"))
|
|
end)
|
|
|
|
it('with translated help files', function()
|
|
write_file('Xhelptags/doc/Xa.nlx', '*Xa*', nil, true)
|
|
-- The suffix is matched case-insensitively.
|
|
write_file('Xhelptags/doc/Xc.FRX', '*Xc*', nil, true)
|
|
|
|
command('helptags Xhelptags/doc')
|
|
eq(eval("['Xa Xa.nlx /*Xa*']"), eval("readfile('Xhelptags/doc/tags-nl')"))
|
|
eq(eval("['Xc Xc.FRX /*Xc*']"), eval("readfile('Xhelptags/doc/tags-fr')"))
|
|
end)
|
|
end)
|