build: enable lintlua for scripts/ dir #26391

Problem:
We don't enable stylua for many Lua scripts. Automating code-style is an
important tool for reducing time spent on accidental (non-essential)
complexity.

Solution:
- Enable lintlua for `scripts/` directory.
- Specify `call_parentheses = "Input"`, we should allow kwargs-style
  function invocations.
This commit is contained in:
Justin M. Keyes 2023-12-04 12:38:31 -08:00 committed by GitHub
parent e5d7003b02
commit 517f0cc634
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 415 additions and 292 deletions

View File

@ -3,4 +3,4 @@ line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferSingle"
call_parentheses = "Always"
call_parentheses = "Input"

View File

@ -2,6 +2,5 @@
/runtime/lua/coxpcall.lua
/runtime/lua/vim/_meta
/runtime/lua/vim/re.lua
/scripts
/src
/test

View File

@ -233,7 +233,7 @@ add_glob_target(
TARGET lintlua-stylua
COMMAND ${STYLUA_PRG}
FLAGS --color=always --check --respect-ignores
GLOB_DIRS runtime/
GLOB_DIRS runtime/ scripts/
GLOB_PAT *.lua
TOUCH_STRATEGY SINGLE)
@ -260,7 +260,7 @@ add_glob_target(
TARGET formatlua
COMMAND ${STYLUA_PRG}
FLAGS --respect-ignores
GLOB_DIRS runtime
GLOB_DIRS runtime/ scripts/
GLOB_PAT *.lua)
add_custom_target(format)

View File

@ -138,9 +138,10 @@ local function get_archive_info(repo, ref)
'Failed to download archive from GitHub'
)
local shacmd = (vim.fn.executable('sha256sum') == 1
and{ 'sha256sum', archive_path }
or { 'shasum', '-a', '256', archive_path })
local shacmd = (
vim.fn.executable('sha256sum') == 1 and { 'sha256sum', archive_path }
or { 'shasum', '-a', '256', archive_path }
)
local archive_sha = run(shacmd):gmatch('%w+')()
return { url = archive_url, sha = archive_sha }
end
@ -152,18 +153,7 @@ local function write_cmakelists_line(symbol, kind, value)
'sed',
'-i',
'-e',
's/'
.. symbol
.. '_'
.. kind
.. '.*$'
.. '/'
.. symbol
.. '_'
.. kind
.. ' '
.. value
.. '/',
's/' .. symbol .. '_' .. kind .. '.*$' .. '/' .. symbol .. '_' .. kind .. ' ' .. value .. '/',
deps_file,
}, 'Failed to write ' .. deps_file)
end
@ -203,16 +193,13 @@ local function update_cmakelists(dependency, archive, comment)
p('Updating ' .. dependency.name .. ' to ' .. archive.url .. '\n')
write_cmakelists_line(dependency.symbol, 'URL', archive.url:gsub('/', '\\/'))
write_cmakelists_line(dependency.symbol, 'SHA256', archive.sha)
run_die(
{
'git',
'commit',
deps_file,
'-m',
commit_prefix .. 'bump ' .. dependency.name .. ' to ' .. comment,
},
'git failed to commit'
)
run_die({
'git',
'commit',
deps_file,
'-m',
commit_prefix .. 'bump ' .. dependency.name .. ' to ' .. comment,
}, 'git failed to commit')
end
local function verify_cmakelists_committed()
@ -318,9 +305,9 @@ function M.commit(dependency_name, commit)
end
function M.version(dependency_name, version)
vim.validate{
dependency_name={dependency_name,'s'},
version={version,'s'},
vim.validate {
dependency_name = { dependency_name, 's' },
version = { version, 's' },
}
local dependency = assert(get_dependency(dependency_name))
verify_cmakelists_committed()
@ -384,7 +371,7 @@ function M.submit_pr()
end
local function usage()
local this_script = _G.arg[0]:match("[^/]*.lua$")
local this_script = _G.arg[0]:match('[^/]*.lua$')
print(([=[
Bump Nvim dependencies
@ -421,13 +408,13 @@ local function parseargs()
elseif _G.arg[i] == '--pr' then
args.pr = true
elseif _G.arg[i] == '--branch' then
args.branch = _G.arg[i+1]
args.branch = _G.arg[i + 1]
elseif _G.arg[i] == '--dep' then
args.dep = _G.arg[i+1]
args.dep = _G.arg[i + 1]
elseif _G.arg[i] == '--version' then
args.version = _G.arg[i+1]
args.version = _G.arg[i + 1]
elseif _G.arg[i] == '--commit' then
args.commit = _G.arg[i+1]
args.commit = _G.arg[i + 1]
elseif _G.arg[i] == '--head' then
args.head = true
end

View File

@ -1,7 +1,7 @@
-- Generator for various vimdoc and Lua type files
local DEP_API_METADATA = 'build/api_metadata.mpack'
local DEP_API_DOC = 'runtime/doc/api.mpack'
local DEP_API_DOC = 'runtime/doc/api.mpack'
--- @class vim.api.metadata
--- @field name string
@ -302,7 +302,7 @@ local function get_api_keysets_meta()
for _, k in ipairs(keysets) do
local params = {}
for _, key in ipairs(k.keys) do
table.insert(params, {key..'?', api_type(k.types[key] or 'any')})
table.insert(params, { key .. '?', api_type(k.types[key] or 'any') })
end
ret[k.name] = {
signature = 'NA',
@ -396,7 +396,7 @@ local function render_sig_and_tag(name, fun, write)
local tag = table.concat(tags, ' ')
local siglen = #fun.signature
local conceal_offset = 2*(#tags - 1)
local conceal_offset = 2 * (#tags - 1)
local tag_pad_len = math.max(1, 80 - #tag + conceal_offset)
if siglen + #tag > 80 then
@ -473,7 +473,7 @@ local function render_option_default(d, vimdoc)
end
end
if dt == "" or dt == nil or type(dt) == 'function' then
if dt == '' or dt == nil or type(dt) == 'function' then
dt = d.meta
end
@ -481,22 +481,22 @@ local function render_option_default(d, vimdoc)
if not vimdoc then
v = vim.inspect(dt) --[[@as string]]
else
v = type(dt) == 'string' and '"'..dt..'"' or tostring(dt)
v = type(dt) == 'string' and '"' .. dt .. '"' or tostring(dt)
end
--- @type table<string, string|false>
local envvars = {
TMPDIR = false,
VIMRUNTIME = false,
XDG_CONFIG_HOME = vim.env.HOME..'/.local/config',
XDG_DATA_HOME = vim.env.HOME..'/.local/share',
XDG_STATE_HOME = vim.env.HOME..'/.local/state',
XDG_CONFIG_HOME = vim.env.HOME .. '/.local/config',
XDG_DATA_HOME = vim.env.HOME .. '/.local/share',
XDG_STATE_HOME = vim.env.HOME .. '/.local/state',
}
for name, default in pairs(envvars) do
local value = vim.env[name] or default
if value then
v = v:gsub(vim.pesc(value), '$'..name)
v = v:gsub(vim.pesc(value), '$' .. name)
end
end
@ -509,26 +509,26 @@ end
local function render_option_meta(_f, opt, write)
write('')
for _, l in ipairs(split(norm_text(opt.desc))) do
write('--- '..l)
write('--- ' .. l)
end
write('--- @type '..OPTION_TYPES[opt.type])
write('vim.o.'..opt.full_name..' = '..render_option_default(opt.defaults))
write('--- @type ' .. OPTION_TYPES[opt.type])
write('vim.o.' .. opt.full_name .. ' = ' .. render_option_default(opt.defaults))
if opt.abbreviation then
write('vim.o.'..opt.abbreviation..' = vim.o.'..opt.full_name)
write('vim.o.' .. opt.abbreviation .. ' = vim.o.' .. opt.full_name)
end
for _, s in pairs {
{'wo', 'window'},
{'bo', 'buffer'},
{'go', 'global'},
{ 'wo', 'window' },
{ 'bo', 'buffer' },
{ 'go', 'global' },
} do
local id, scope = s[1], s[2]
if vim.list_contains(opt.scope, scope) or (id == 'go' and #opt.scope > 1) then
local pfx = 'vim.'..id..'.'
write(pfx..opt.full_name..' = vim.o.'..opt.full_name)
local pfx = 'vim.' .. id .. '.'
write(pfx .. opt.full_name .. ' = vim.o.' .. opt.full_name)
if opt.abbreviation then
write(pfx..opt.abbreviation..' = '..pfx..opt.full_name)
write(pfx .. opt.abbreviation .. ' = ' .. pfx .. opt.full_name)
end
end
end
@ -541,14 +541,14 @@ local function scope_to_doc(s)
global = 'global',
buffer = 'local to buffer',
window = 'local to window',
tab = 'local to tab page'
tab = 'local to tab page',
}
if #s == 1 then
return m[s[1]]
end
assert(s[1] == 'global')
return 'global or '..m[s[2]]..' |global-local|'
return 'global or ' .. m[s[2]] .. ' |global-local|'
end
-- @param o vim.option_meta
@ -602,23 +602,23 @@ local function build_option_tags(opt)
--- @type string[]
local tags = { opt.full_name }
tags[#tags+1] = opt.abbreviation
tags[#tags + 1] = opt.abbreviation
if opt.type == 'bool' then
for i = 1, #tags do
tags[#tags+1] = 'no'..tags[i]
tags[#tags + 1] = 'no' .. tags[i]
end
end
for i, t in ipairs(tags) do
tags[i] = "'"..t.."'"
tags[i] = "'" .. t .. "'"
end
for _, t in ipairs(opt.tags or {}) do
tags[#tags+1] = t
tags[#tags + 1] = t
end
for i, t in ipairs(tags) do
tags[i] = "*"..t.."*"
tags[i] = '*' .. t .. '*'
end
return tags
@ -630,10 +630,10 @@ end
local function render_option_doc(_f, opt, write)
local tags = build_option_tags(opt)
local tag_str = table.concat(tags, ' ')
local conceal_offset = 2*(#tags - 1)
local conceal_offset = 2 * (#tags - 1)
local tag_pad = string.rep('\t', math.ceil((64 - #tag_str + conceal_offset) / 8))
-- local pad = string.rep(' ', 80 - #tag_str + conceal_offset)
write(tag_pad..tag_str)
write(tag_pad .. tag_str)
local name_str --- @type string
if opt.abbreviation then
@ -649,19 +649,19 @@ local function render_option_doc(_f, opt, write)
if opt.defaults.doc then
local deflen = #string.format('%s%s%s (', name_str, pad, otype)
--- @type string
v = v:gsub('\n', '\n'..string.rep(' ', deflen - 2))
v = v:gsub('\n', '\n' .. string.rep(' ', deflen - 2))
end
write(string.format('%s%s%s\t(default %s)', name_str, pad, otype, v))
else
write(string.format('%s\t%s', name_str, otype))
end
write('\t\t\t'..scope_to_doc(opt.scope)..scope_more_doc(opt))
write('\t\t\t' .. scope_to_doc(opt.scope) .. scope_more_doc(opt))
for _, l in ipairs(split(opt.desc)) do
if l == '<' or l:match('^<%s') then
write(l)
else
write('\t'..l:gsub('\\<', '<'))
write('\t' .. l:gsub('\\<', '<'))
end
end
end
@ -751,21 +751,21 @@ local CONFIG = {
header = { '' },
from = 'A jump table for the options with a short description can be found at |Q_op|.',
footer = {
' vim:tw=78:ts=8:noet:ft=help:norl:'
' vim:tw=78:ts=8:noet:ft=help:norl:',
},
funcs = get_option_meta,
render = render_option_doc,
}
},
}
--- @param elem nvim.gen_eval_files.elem
local function render(elem)
print('Rendering '..elem.path)
local from_lines = {} --- @type string[]
print('Rendering ' .. elem.path)
local from_lines = {} --- @type string[]
local from = elem.from
if from then
for line in io.lines(elem.path) do
from_lines[#from_lines+1] = line
from_lines[#from_lines + 1] = line
if line:match(from) then
break
end

View File

@ -8,18 +8,18 @@ if do_not_run then
return
end
local filetype_vim = "runtime/filetype.vim"
local filetype_lua = "runtime/lua/vim/filetype.lua"
local filetype_vim = 'runtime/filetype.vim'
local filetype_lua = 'runtime/lua/vim/filetype.lua'
local keywords = {
["for"] = true,
["or"] = true,
["and"] = true,
["end"] = true,
["do"] = true,
["if"] = true,
["while"] = true,
["repeat"] = true,
['for'] = true,
['or'] = true,
['and'] = true,
['end'] = true,
['do'] = true,
['if'] = true,
['while'] = true,
['repeat'] = true,
}
local sections = {
@ -28,42 +28,42 @@ local sections = {
pattern = { str = {}, func = {} },
}
local specialchars = "%*%?\\%$%[%]%{%}"
local specialchars = '%*%?\\%$%[%]%{%}'
local function add_pattern(pat, ft)
local ok = true
-- Patterns that start or end with { or } confuse splitting on commas and make parsing harder, so just skip those
if not string.find(pat, "^%{") and not string.find(pat, "%}$") then
for part in string.gmatch(pat, "[^,]+") do
if not string.find(part, "[" .. specialchars .. "]") then
if type(ft) == "string" then
if not string.find(pat, '^%{') and not string.find(pat, '%}$') then
for part in string.gmatch(pat, '[^,]+') do
if not string.find(part, '[' .. specialchars .. ']') then
if type(ft) == 'string' then
sections.filename.str[part] = ft
else
sections.filename.func[part] = ft
end
elseif string.match(part, "^%*%.[^%./" .. specialchars .. "]+$") then
if type(ft) == "string" then
elseif string.match(part, '^%*%.[^%./' .. specialchars .. ']+$') then
if type(ft) == 'string' then
sections.extension.str[part:sub(3)] = ft
else
sections.extension.func[part:sub(3)] = ft
end
else
if string.match(part, "^%*/[^" .. specialchars .. "]+$") then
if string.match(part, '^%*/[^' .. specialchars .. ']+$') then
-- For patterns matching */some/pattern we want to easily match files
-- with path /some/pattern, so include those in filename detection
if type(ft) == "string" then
if type(ft) == 'string' then
sections.filename.str[part:sub(2)] = ft
else
sections.filename.func[part:sub(2)] = ft
end
end
if string.find(part, "^[%w-_.*?%[%]/]+$") then
local p = part:gsub("%.", "%%."):gsub("%*", ".*"):gsub("%?", ".")
if string.find(part, '^[%w-_.*?%[%]/]+$') then
local p = part:gsub('%.', '%%.'):gsub('%*', '.*'):gsub('%?', '.')
-- Insert into array to maintain order rather than setting
-- key-value directly
if type(ft) == "string" then
if type(ft) == 'string' then
sections.pattern.str[p] = ft
else
sections.pattern.func[p] = ft
@ -80,14 +80,16 @@ end
local function parse_line(line)
local pat, ft
pat, ft = line:match("^%s*au%a* Buf[%a,]+%s+(%S+)%s+setf%s+(%S+)")
pat, ft = line:match('^%s*au%a* Buf[%a,]+%s+(%S+)%s+setf%s+(%S+)')
if pat then
return add_pattern(pat, ft)
else
local func
pat, func = line:match("^%s*au%a* Buf[%a,]+%s+(%S+)%s+call%s+(%S+)")
pat, func = line:match('^%s*au%a* Buf[%a,]+%s+(%S+)%s+call%s+(%S+)')
if pat then
return add_pattern(pat, function() return func end)
return add_pattern(pat, function()
return func
end)
end
end
end
@ -95,12 +97,12 @@ end
local unparsed = {}
local full_line
for line in io.lines(filetype_vim) do
local cont = string.match(line, "^%s*\\%s*(.*)$")
local cont = string.match(line, '^%s*\\%s*(.*)$')
if cont then
full_line = full_line .. " " .. cont
full_line = full_line .. ' ' .. cont
else
if full_line then
if not parse_line(full_line) and string.find(full_line, "^%s*au%a* Buf") then
if not parse_line(full_line) and string.find(full_line, '^%s*au%a* Buf') then
table.insert(unparsed, full_line)
end
end
@ -109,40 +111,46 @@ for line in io.lines(filetype_vim) do
end
if #unparsed > 0 then
print("Failed to parse the following patterns:")
print('Failed to parse the following patterns:')
for _, v in ipairs(unparsed) do
print(v)
end
end
local function add_item(indent, key, ft)
if type(ft) == "string" then
if string.find(key, "%A") or keywords[key] then
key = string.format("[\"%s\"]", key)
if type(ft) == 'string' then
if string.find(key, '%A') or keywords[key] then
key = string.format('["%s"]', key)
end
return string.format([[%s%s = "%s",]], indent, key, ft)
elseif type(ft) == "function" then
elseif type(ft) == 'function' then
local func = ft()
if string.find(key, "%A") or keywords[key] then
key = string.format("[\"%s\"]", key)
if string.find(key, '%A') or keywords[key] then
key = string.format('["%s"]', key)
end
-- Right now only a single argument is supported, which covers
-- everything in filetype.vim as of this writing
local arg = string.match(func, "%((.*)%)$")
func = string.gsub(func, "%(.*$", "")
if arg == "" then
local arg = string.match(func, '%((.*)%)$')
func = string.gsub(func, '%(.*$', '')
if arg == '' then
-- Function with no arguments, call the function directly
return string.format([[%s%s = function() vim.fn["%s"]() end,]], indent, key, func)
elseif string.match(arg, [[^(["']).*%1$]]) then
-- String argument
if func == "s:StarSetf" then
if func == 's:StarSetf' then
return string.format([[%s%s = starsetf(%s),]], indent, key, arg)
else
return string.format([[%s%s = function() vim.fn["%s"](%s) end,]], indent, key, func, arg)
end
elseif string.find(arg, "%(") then
elseif string.find(arg, '%(') then
-- Function argument
return string.format([[%s%s = function() vim.fn["%s"](vim.fn.%s) end,]], indent, key, func, arg)
return string.format(
[[%s%s = function() vim.fn["%s"](vim.fn.%s) end,]],
indent,
key,
func,
arg
)
else
assert(false, arg)
end
@ -153,7 +161,7 @@ do
local lines = {}
local start = false
for line in io.lines(filetype_lua) do
if line:match("^%s+-- END [A-Z]+$") then
if line:match('^%s+-- END [A-Z]+$') then
start = false
end
@ -161,14 +169,14 @@ do
table.insert(lines, line)
end
local indent, section = line:match("^(%s+)-- BEGIN ([A-Z]+)$")
local indent, section = line:match('^(%s+)-- BEGIN ([A-Z]+)$')
if section then
start = true
local t = sections[string.lower(section)]
local sorted = {}
for k, v in pairs(t.str) do
table.insert(sorted, {[k] = v})
table.insert(sorted, { [k] = v })
end
table.sort(sorted, function(a, b)
@ -182,7 +190,7 @@ do
sorted = {}
for k, v in pairs(t.func) do
table.insert(sorted, {[k] = v})
table.insert(sorted, { [k] = v })
end
table.sort(sorted, function(a, b)
@ -195,7 +203,7 @@ do
end
end
end
local f = io.open(filetype_lua, "w")
f:write(table.concat(lines, "\n") .. "\n")
local f = io.open(filetype_lua, 'w')
f:write(table.concat(lines, '\n') .. '\n')
f:close()
end

View File

@ -60,26 +60,26 @@ local new_layout = {
-- TODO: These known invalid |links| require an update to the relevant docs.
local exclude_invalid = {
["'string'"] = "eval.txt",
["'string'"] = 'eval.txt',
Query = 'treesitter.txt',
matchit = 'vim_diff.txt',
["set!"] = "treesitter.txt",
['set!'] = 'treesitter.txt',
}
-- False-positive "invalid URLs".
local exclude_invalid_urls = {
["http://"] = "usr_23.txt",
["http://."] = "usr_23.txt",
["http://aspell.net/man-html/Affix-Compression.html"] = "spell.txt",
["http://aspell.net/man-html/Phonetic-Code.html"] = "spell.txt",
["http://canna.sourceforge.jp/"] = "mbyte.txt",
["http://gnuada.sourceforge.net"] = "ft_ada.txt",
["http://lua-users.org/wiki/StringLibraryTutorial"] = "lua.txt",
["http://michael.toren.net/code/"] = "pi_tar.txt",
["http://papp.plan9.de"] = "syntax.txt",
["http://wiki.services.openoffice.org/wiki/Dictionaries"] = "spell.txt",
["http://www.adapower.com"] = "ft_ada.txt",
["http://www.jclark.com/"] = "quickfix.txt",
['http://'] = 'usr_23.txt',
['http://.'] = 'usr_23.txt',
['http://aspell.net/man-html/Affix-Compression.html'] = 'spell.txt',
['http://aspell.net/man-html/Phonetic-Code.html'] = 'spell.txt',
['http://canna.sourceforge.jp/'] = 'mbyte.txt',
['http://gnuada.sourceforge.net'] = 'ft_ada.txt',
['http://lua-users.org/wiki/StringLibraryTutorial'] = 'lua.txt',
['http://michael.toren.net/code/'] = 'pi_tar.txt',
['http://papp.plan9.de'] = 'syntax.txt',
['http://wiki.services.openoffice.org/wiki/Dictionaries'] = 'spell.txt',
['http://www.adapower.com'] = 'ft_ada.txt',
['http://www.jclark.com/'] = 'quickfix.txt',
}
-- Deprecated, brain-damaged files that I don't care about.
@ -98,19 +98,18 @@ local function tofile(fname, text)
end
local function html_esc(s)
return s:gsub(
'&', '&amp;'):gsub(
'<', '&lt;'):gsub(
'>', '&gt;')
return s:gsub('&', '&amp;'):gsub('<', '&lt;'):gsub('>', '&gt;')
end
local function url_encode(s)
-- Credit: tpope / vim-unimpaired
-- NOTE: these chars intentionally *not* escaped: ' ( )
return vim.fn.substitute(vim.fn.iconv(s, 'latin1', 'utf-8'),
return vim.fn.substitute(
vim.fn.iconv(s, 'latin1', 'utf-8'),
[=[[^A-Za-z0-9()'_.~-]]=],
[=[\="%".printf("%02X",char2nr(submatch(0)))]=],
'g')
'g'
)
end
local function expandtabs(s)
@ -131,7 +130,7 @@ local function to_heading_tag(text)
end
local function basename_noext(f)
return vim.fs.basename(f:gsub('%.txt', ''))
return vim.fs.basename(f:gsub('%.txt', ''))
end
local function is_blank(s)
@ -151,7 +150,7 @@ local function fix_url(url)
local removed_chars = ''
local fixed_url = url
-- Remove up to one of each char from end of the URL, in this order.
for _, c in ipairs({ '.', ')', }) do
for _, c in ipairs({ '.', ')' }) do
if fixed_url:sub(-1) == c then
removed_chars = c .. removed_chars
fixed_url = fixed_url:sub(1, -2)
@ -162,7 +161,7 @@ end
--- Checks if a given line is a "noise" line that doesn't look good in HTML form.
local function is_noise(line, noise_lines)
if (
if
-- First line is always noise.
(noise_lines ~= nil and vim.tbl_count(noise_lines) == 0)
or line:find('Type .*gO.* to see the table of contents')
@ -177,7 +176,7 @@ local function is_noise(line, noise_lines)
or line:find('^%s*vim?%:.*ft=help')
or line:find('^%s*vim?%:.*filetype=help')
or line:find('[*>]local%-additions[*<]')
) then
then
-- table.insert(stats.noise_lines, getbuflinestr(root, opt.buf, 0))
table.insert(noise_lines or {}, line)
return true
@ -188,28 +187,32 @@ end
--- Creates a github issue URL at neovim/tree-sitter-vimdoc with prefilled content.
local function get_bug_url_vimdoc(fname, to_fname, sample_text)
local this_url = string.format('https://neovim.io/doc/user/%s', vim.fs.basename(to_fname))
local bug_url = ('https://github.com/neovim/tree-sitter-vimdoc/issues/new?labels=bug&title=parse+error%3A+'
..vim.fs.basename(fname)
..'+&body=Found+%60tree-sitter-vimdoc%60+parse+error+at%3A+'
..this_url
..'%0D%0DContext%3A%0D%0D%60%60%60%0D'
..url_encode(sample_text)
..'%0D%60%60%60')
local bug_url = (
'https://github.com/neovim/tree-sitter-vimdoc/issues/new?labels=bug&title=parse+error%3A+'
.. vim.fs.basename(fname)
.. '+&body=Found+%60tree-sitter-vimdoc%60+parse+error+at%3A+'
.. this_url
.. '%0D%0DContext%3A%0D%0D%60%60%60%0D'
.. url_encode(sample_text)
.. '%0D%60%60%60'
)
return bug_url
end
--- Creates a github issue URL at neovim/neovim with prefilled content.
local function get_bug_url_nvim(fname, to_fname, sample_text, token_name)
local this_url = string.format('https://neovim.io/doc/user/%s', vim.fs.basename(to_fname))
local bug_url = ('https://github.com/neovim/neovim/issues/new?labels=bug&title=user+docs+HTML%3A+'
..vim.fs.basename(fname)
..'+&body=%60gen_help_html.lua%60+problem+at%3A+'
..this_url
..'%0D'
..(token_name and '+unhandled+token%3A+%60'..token_name..'%60' or '')
..'%0DContext%3A%0D%0D%60%60%60%0D'
..url_encode(sample_text)
..'%0D%60%60%60')
local bug_url = (
'https://github.com/neovim/neovim/issues/new?labels=bug&title=user+docs+HTML%3A+'
.. vim.fs.basename(fname)
.. '+&body=%60gen_help_html.lua%60+problem+at%3A+'
.. this_url
.. '%0D'
.. (token_name and '+unhandled+token%3A+%60' .. token_name .. '%60' or '')
.. '%0DContext%3A%0D%0D%60%60%60%0D'
.. url_encode(sample_text)
.. '%0D%60%60%60'
)
return bug_url
end
@ -274,9 +277,11 @@ end
local function get_tagname(node, bufnr)
local text = vim.treesitter.get_node_text(node, bufnr)
local tag = (node:type() == 'optionlink' or node:parent():type() == 'optionlink') and ("'%s'"):format(text) or text
local helpfile = vim.fs.basename(tagmap[tag]) or nil -- "api.txt"
local helppage = get_helppage(helpfile) -- "api.html"
local tag = (node:type() == 'optionlink' or node:parent():type() == 'optionlink')
and ("'%s'"):format(text)
or text
local helpfile = vim.fs.basename(tagmap[tag]) or nil -- "api.txt"
local helppage = get_helppage(helpfile) -- "api.html"
return helppage, tag
end
@ -295,11 +300,9 @@ local function ignore_parse_error(fname, s)
if ignore_errors[vim.fs.basename(fname)] then
return true
end
return (
-- Ignore parse errors for unclosed tag.
-- This is common in vimdocs and is treated as plaintext by :help.
s:find("^[`'|*]")
)
-- Ignore parse errors for unclosed tag.
-- This is common in vimdocs and is treated as plaintext by :help.
return s:find("^[`'|*]")
end
local function has_ancestor(node, ancestor_name)
@ -377,10 +380,11 @@ local function visit_validate(root, level, lang_tree, opt, stats)
-- Flatten the sample text to a single, truncated line.
sample_text = vim.trim(sample_text):gsub('[\t\n]', ' '):sub(1, 80)
table.insert(stats.parse_errors, sample_text)
elseif (node_name == 'word' or node_name == 'uppercase_name')
and (not vim.tbl_contains({'codespan', 'taglink', 'tag'}, parent))
elseif
(node_name == 'word' or node_name == 'uppercase_name')
and (not vim.tbl_contains({ 'codespan', 'taglink', 'tag' }, parent))
then
local text_nopunct = vim.fn.trim(text, '.,', 0) -- Ignore some punctuation.
local text_nopunct = vim.fn.trim(text, '.,', 0) -- Ignore some punctuation.
if spell_dict[text_nopunct] then
invalid_spelling[text_nopunct] = invalid_spelling[text_nopunct] or {}
invalid_spelling[text_nopunct][vim.fs.basename(opt.fname)] = node_text(root:parent())
@ -399,7 +403,7 @@ local function fix_tab_after_conceal(text, next_node_text)
-- Vim tabs take into account the two concealed characters even though they
-- are invisible, so we need to add back in the two spaces if this is
-- followed by a tab to make the tab alignment to match Vim's behavior.
if string.sub(next_node_text,1,1) == '\t' then
if string.sub(next_node_text, 1, 1) == '\t' then
text = text .. ' '
end
return text
@ -411,9 +415,15 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
local node_name = (root.named and root:named()) and root:type() or nil
-- Previous sibling kind (string).
local prev = root:prev_sibling() and (root:prev_sibling().named and root:prev_sibling():named()) and root:prev_sibling():type() or nil
local prev = root:prev_sibling()
and (root:prev_sibling().named and root:prev_sibling():named())
and root:prev_sibling():type()
or nil
-- Next sibling kind (string).
local next_ = root:next_sibling() and (root:next_sibling().named and root:next_sibling():named()) and root:next_sibling():type() or nil
local next_ = root:next_sibling()
and (root:next_sibling().named and root:next_sibling():named())
and root:next_sibling():type()
or nil
-- Parent kind (string).
local parent = root:parent() and root:parent():type() or nil
local text = ''
@ -450,7 +460,7 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
trimmed = trim(text)
end
if node_name == 'help_file' then -- root node
if node_name == 'help_file' then -- root node
return text
elseif node_name == 'url' then
local fixed_url, removed_chars = fix_url(trimmed)
@ -459,18 +469,22 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
return text
elseif node_name == 'h1' or node_name == 'h2' or node_name == 'h3' then
if is_noise(text, stats.noise_lines) then
return '' -- Discard common "noise" lines.
return '' -- Discard common "noise" lines.
end
-- Remove "===" and tags from ToC text.
local hname = (node_text():gsub('%-%-%-%-+', ''):gsub('%=%=%=%=+', ''):gsub('%*.*%*', ''))
-- Use the first *tag* node as the heading anchor, if any.
local tagnode = first(root, 'tag')
-- Use the *tag* as the heading anchor id, if possible.
local tagname = tagnode and url_encode(node_text(tagnode:child(1), false)) or to_heading_tag(hname)
local tagname = tagnode and url_encode(node_text(tagnode:child(1), false))
or to_heading_tag(hname)
if node_name == 'h1' or #headings == 0 then
table.insert(headings, { name = hname, subheadings = {}, tag = tagname })
else
table.insert(headings[#headings].subheadings, { name = hname, subheadings = {}, tag = tagname })
table.insert(
headings[#headings].subheadings,
{ name = hname, subheadings = {}, tag = tagname }
)
end
local el = node_name == 'h1' and 'h2' or 'h3'
return ('<%s id="%s" class="help-heading">%s</%s>\n'):format(el, tagname, text, el)
@ -490,11 +504,16 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
end
return string.format('<div class="help-para">\n%s\n</div>\n', text)
elseif node_name == 'line' then
if (parent ~= 'codeblock' or parent ~= 'code') and (is_blank(text) or is_noise(text, stats.noise_lines)) then
return '' -- Discard common "noise" lines.
if
(parent ~= 'codeblock' or parent ~= 'code')
and (is_blank(text) or is_noise(text, stats.noise_lines))
then
return '' -- Discard common "noise" lines.
end
-- XXX: Avoid newlines (too much whitespace) after block elements in old (preformatted) layout.
local div = opt.old and root:child(0) and vim.list_contains({'column_heading', 'h1', 'h2', 'h3'}, root:child(0):type())
local div = opt.old
and root:child(0)
and vim.list_contains({ 'column_heading', 'h1', 'h2', 'h3' }, root:child(0):type())
return string.format('%s%s', div and trim(text) or text, div and '' or '\n')
elseif node_name == 'line_li' then
local sib = root:prev_sibling()
@ -520,12 +539,17 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
if ignored then
return text
end
local s = ('%s<a href="%s#%s">%s</a>'):format(ws(), helppage, url_encode(tagname), html_esc(tagname))
local s = ('%s<a href="%s#%s">%s</a>'):format(
ws(),
helppage,
url_encode(tagname),
html_esc(tagname)
)
if opt.old and node_name == 'taglink' then
s = fix_tab_after_conceal(s, node_text(root:next_sibling()))
end
return s
elseif vim.list_contains({'codespan', 'keycode'}, node_name) then
elseif vim.list_contains({ 'codespan', 'keycode' }, node_name) then
if root:has_error() then
return text
end
@ -541,24 +565,28 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
elseif node_name == 'language' then
language = node_text(root)
return ''
elseif node_name == 'code' then -- Highlighted codeblock (child).
elseif node_name == 'code' then -- Highlighted codeblock (child).
if is_blank(text) then
return ''
end
local code
if language then
code = ('<pre><code class="language-%s">%s</code></pre>'):format(language,trim(trim_indent(text), 2))
code = ('<pre><code class="language-%s">%s</code></pre>'):format(
language,
trim(trim_indent(text), 2)
)
language = nil
else
code = ('<pre>%s</pre>'):format(trim(trim_indent(text), 2))
end
return code
elseif node_name == 'tag' then -- anchor
elseif node_name == 'tag' then -- anchor
if root:has_error() then
return text
end
local in_heading = vim.list_contains({'h1', 'h2', 'h3'}, parent)
local cssclass = (not in_heading and get_indent(node_text()) > 8) and 'help-tag-right' or 'help-tag'
local in_heading = vim.list_contains({ 'h1', 'h2', 'h3' }, parent)
local cssclass = (not in_heading and get_indent(node_text()) > 8) and 'help-tag-right'
or 'help-tag'
local tagname = node_text(root:child(1), false)
if vim.tbl_count(stats.first_tags) < 2 then
-- Force the first 2 tags in the doc to be anchored at the main heading.
@ -567,14 +595,29 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
end
local el = in_heading and 'span' or 'code'
local encoded_tagname = url_encode(tagname)
local s = ('%s<%s id="%s" class="%s"><a href="#%s">%s</a></%s>'):format(ws(), el, encoded_tagname, cssclass, encoded_tagname, trimmed, el)
local s = ('%s<%s id="%s" class="%s"><a href="#%s">%s</a></%s>'):format(
ws(),
el,
encoded_tagname,
cssclass,
encoded_tagname,
trimmed,
el
)
if opt.old then
s = fix_tab_after_conceal(s, node_text(root:next_sibling()))
s = fix_tab_after_conceal(s, node_text(root:next_sibling()))
end
if in_heading and prev ~= 'tag' then
-- Don't set "id", let the heading use the tag as its "id" (used by search engines).
s = ('%s<%s class="%s"><a href="#%s">%s</a></%s>'):format(ws(), el, cssclass, encoded_tagname, trimmed, el)
s = ('%s<%s class="%s"><a href="#%s">%s</a></%s>'):format(
ws(),
el,
cssclass,
encoded_tagname,
trimmed,
el
)
-- Start the <span> container for tags in a heading.
-- This makes "justify-content:space-between" right-align the tags.
-- <h2>foo bar<span>tag1 tag2</span></h2>
@ -593,11 +636,17 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
local sample_text = level > 0 and getbuflinestr(root, opt.buf, 3) or '[top level!]'
table.insert(stats.parse_errors, sample_text)
return ('<a class="parse-error" target="_blank" title="Report bug... (parse error)" href="%s">%s</a>'):format(
get_bug_url_vimdoc(opt.fname, opt.to_fname, sample_text), trimmed)
else -- Unknown token.
get_bug_url_vimdoc(opt.fname, opt.to_fname, sample_text),
trimmed
)
else -- Unknown token.
local sample_text = level > 0 and getbuflinestr(root, opt.buf, 3) or '[top level!]'
return ('<a class="unknown-token" target="_blank" title="Report bug... (unhandled token "%s")" href="%s">%s</a>'):format(
node_name, get_bug_url_nvim(opt.fname, opt.to_fname, sample_text, node_name), trimmed), ('unknown-token:"%s"'):format(node_name)
node_name,
get_bug_url_nvim(opt.fname, opt.to_fname, sample_text, node_name),
trimmed
),
('unknown-token:"%s"'):format(node_name)
end
end
@ -605,9 +654,11 @@ local function get_helpfiles(include)
local dir = './build/runtime/doc'
local rv = {}
for f, type in vim.fs.dir(dir) do
if (vim.endswith(f, '.txt')
and type == 'file'
and (not include or vim.list_contains(include, f))) then
if
vim.endswith(f, '.txt')
and type == 'file'
and (not include or vim.list_contains(include, f))
then
local fullpath = vim.fn.fnamemodify(('%s/%s'):format(dir, f), ':p')
table.insert(rv, fullpath)
end
@ -633,7 +684,7 @@ end
--- Use the vimdoc parser defined in the build, not whatever happens to be installed on the system.
local function ensure_runtimepath()
if not vim.o.runtimepath:find('build/lib/nvim/') then
vim.cmd[[set runtimepath^=./build/lib/nvim/]]
vim.cmd [[set runtimepath^=./build/lib/nvim/]]
end
end
@ -645,11 +696,11 @@ end
local function parse_buf(fname, parser_path)
local buf
if type(fname) == 'string' then
vim.cmd('split '..vim.fn.fnameescape(fname)) -- Filename.
vim.cmd('split ' .. vim.fn.fnameescape(fname)) -- Filename.
buf = vim.api.nvim_get_current_buf()
else
buf = fname
vim.cmd('sbuffer '..tostring(fname)) -- Buffer number.
vim.cmd('sbuffer ' .. tostring(fname)) -- Buffer number.
end
if parser_path then
vim.treesitter.language.add('vimdoc', { path = parser_path })
@ -671,7 +722,7 @@ local function validate_one(fname, parser_path)
}
local lang_tree, buf = parse_buf(fname, parser_path)
for _, tree in ipairs(lang_tree:trees()) do
visit_validate(tree:root(), 0, tree, { buf = buf, fname = fname, }, stats)
visit_validate(tree:root(), 0, tree, { buf = buf, fname = fname }, stats)
end
lang_tree:destroy()
vim.cmd.close()
@ -690,10 +741,10 @@ local function gen_one(fname, to_fname, old, commit, parser_path)
local stats = {
noise_lines = {},
parse_errors = {},
first_tags = {}, -- Track the first few tags in doc.
first_tags = {}, -- Track the first few tags in doc.
}
local lang_tree, buf = parse_buf(fname, parser_path)
local headings = {} -- Headings (for ToC). 2-dimensional: h1 contains h2/h3.
local headings = {} -- Headings (for ToC). 2-dimensional: h1 contains h2/h3.
local title = to_titlecase(basename_noext(fname))
local html = ([[
@ -777,9 +828,17 @@ local function gen_one(fname, to_fname, old, commit, parser_path)
local main = ''
for _, tree in ipairs(lang_tree:trees()) do
main = main .. (visit_node(tree:root(), 0, tree, headings,
{ buf = buf, old = old, fname = fname, to_fname = to_fname, indent = 1, },
stats))
main = main
.. (
visit_node(
tree:root(),
0,
tree,
headings,
{ buf = buf, old = old, fname = fname, to_fname = to_fname, indent = 1 },
stats
)
)
end
main = ([[
@ -809,7 +868,14 @@ local function gen_one(fname, to_fname, old, commit, parser_path)
<hr/>
%s
</div>
]]):format(logo_svg, stats.first_tags[2] or '', stats.first_tags[1] or '', title, vim.fs.basename(fname), main)
]]):format(
logo_svg,
stats.first_tags[2] or '',
stats.first_tags[1] or '',
title,
vim.fs.basename(fname),
main
)
local toc = [[
<div class="col-narrow toc">
@ -819,13 +885,16 @@ local function gen_one(fname, to_fname, old, commit, parser_path)
<hr/>
]]
local n = 0 -- Count of all headings + subheadings.
for _, h1 in ipairs(headings) do n = n + 1 + #h1.subheadings end
local n = 0 -- Count of all headings + subheadings.
for _, h1 in ipairs(headings) do
n = n + 1 + #h1.subheadings
end
for _, h1 in ipairs(headings) do
toc = toc .. ('<div class="help-toc-h1"><a href="#%s">%s</a>\n'):format(h1.tag, h1.name)
if n < 30 or #headings < 10 then -- Show subheadings only if there aren't too many.
if n < 30 or #headings < 10 then -- Show subheadings only if there aren't too many.
for _, h2 in ipairs(h1.subheadings) do
toc = toc .. ('<div class="help-toc-h2"><a href="#%s">%s</a></div>\n'):format(h2.tag, h2.name)
toc = toc
.. ('<div class="help-toc-h2"><a href="#%s">%s</a></div>\n'):format(h2.tag, h2.name)
end
end
toc = toc .. '</div>'
@ -859,11 +928,16 @@ local function gen_one(fname, to_fname, old, commit, parser_path)
</footer>
]]):format(
os.date('%Y-%m-%d %H:%M'), commit, commit:sub(1, 7), #stats.parse_errors, bug_link,
html_esc(table.concat(stats.noise_lines, '\n')), #stats.noise_lines)
os.date('%Y-%m-%d %H:%M'),
commit,
commit:sub(1, 7),
#stats.parse_errors,
bug_link,
html_esc(table.concat(stats.noise_lines, '\n')),
#stats.noise_lines
)
html = ('%s%s%s</div>\n%s</body>\n</html>\n'):format(
html, main, toc, footer)
html = ('%s%s%s</div>\n%s</body>\n</html>\n'):format(html, main, toc, footer)
vim.cmd('q!')
lang_tree:destroy()
return html, stats
@ -1038,9 +1112,15 @@ function M._test()
helpfiles = get_helpfiles()
local function ok(cond, expected, actual)
assert((not expected and not actual) or (expected and actual), 'if "expected" is given, "actual" is also required')
assert(
(not expected and not actual) or (expected and actual),
'if "expected" is given, "actual" is also required'
)
if expected then
return assert(cond, ('expected %s, got: %s'):format(vim.inspect(expected), vim.inspect(actual)))
return assert(
cond,
('expected %s, got: %s'):format(vim.inspect(expected), vim.inspect(actual))
)
else
return assert(cond)
end
@ -1050,7 +1130,11 @@ function M._test()
end
ok(vim.tbl_count(tagmap) > 3000, '>3000', vim.tbl_count(tagmap))
ok(vim.endswith(tagmap['vim.diagnostic.set()'], 'diagnostic.txt'), tagmap['vim.diagnostic.set()'], 'diagnostic.txt')
ok(
vim.endswith(tagmap['vim.diagnostic.set()'], 'diagnostic.txt'),
tagmap['vim.diagnostic.set()'],
'diagnostic.txt'
)
ok(vim.endswith(tagmap['%:s'], 'cmdline.txt'), tagmap['%:s'], 'cmdline.txt')
ok(is_noise([[vim:tw=78:isk=!-~,^*,^\|,^\":ts=8:noet:ft=help:norl:]]))
ok(is_noise([[ NVIM REFERENCE MANUAL by Thiago de Arruda ]]))
@ -1060,7 +1144,10 @@ function M._test()
eq(1, get_indent(' a'))
eq(2, get_indent(' a\n b\n c\n'))
eq(5, get_indent(' a\n \n b\n c\n d\n e\n'))
eq('a\n \n b\n c\n d\n e\n', trim_indent(' a\n \n b\n c\n d\n e\n'))
eq(
'a\n \n b\n c\n d\n e\n',
trim_indent(' a\n \n b\n c\n d\n e\n')
)
local fixed_url, removed_chars = fix_url('https://example.com).')
eq('https://example.com', fixed_url)
@ -1093,12 +1180,24 @@ end
---
--- @returns info dict
function M.gen(help_dir, to_dir, include, commit, parser_path)
vim.validate{
help_dir={help_dir, function(d) return vim.fn.isdirectory(vim.fn.expand(d)) == 1 end, 'valid directory'},
to_dir={to_dir, 's'},
include={include, 't', true},
commit={commit, 's', true},
parser_path={parser_path, function(f) return f == nil or vim.fn.filereadable(vim.fn.expand(f)) == 1 end, 'valid vimdoc.{so,dll} filepath'},
vim.validate {
help_dir = {
help_dir,
function(d)
return vim.fn.isdirectory(vim.fn.expand(d)) == 1
end,
'valid directory',
},
to_dir = { to_dir, 's' },
include = { include, 't', true },
commit = { commit, 's', true },
parser_path = {
parser_path,
function(f)
return f == nil or vim.fn.filereadable(vim.fn.expand(f)) == 1
end,
'valid vimdoc.{so,dll} filepath',
},
}
local err_count = 0
@ -1117,7 +1216,13 @@ function M.gen(help_dir, to_dir, include, commit, parser_path)
local to_fname = ('%s/%s'):format(to_dir, get_helppage(helpfile))
local html, stats = gen_one(f, to_fname, not new_layout[helpfile], commit or '?', parser_path)
tofile(to_fname, html)
print(('generated (%-4s errors): %-15s => %s'):format(#stats.parse_errors, helpfile, vim.fs.basename(to_fname)))
print(
('generated (%-4s errors): %-15s => %s'):format(
#stats.parse_errors,
helpfile,
vim.fs.basename(to_fname)
)
)
err_count = err_count + #stats.parse_errors
end
print(('generated %d html pages'):format(#helpfiles))
@ -1139,10 +1244,22 @@ end
--
-- @returns results dict
function M.validate(help_dir, include, parser_path)
vim.validate{
help_dir={help_dir, function(d) return vim.fn.isdirectory(vim.fn.expand(d)) == 1 end, 'valid directory'},
include={include, 't', true},
parser_path={parser_path, function(f) return f == nil or vim.fn.filereadable(vim.fn.expand(f)) == 1 end, 'valid vimdoc.{so,dll} filepath'},
vim.validate {
help_dir = {
help_dir,
function(d)
return vim.fn.isdirectory(vim.fn.expand(d)) == 1
end,
'valid directory',
},
include = { include, 't', true },
parser_path = {
parser_path,
function(f)
return f == nil or vim.fn.filereadable(vim.fn.expand(f)) == 1
end,
'valid vimdoc.{so,dll} filepath',
},
}
local err_count = 0
local files_to_errors = {}
@ -1157,7 +1274,9 @@ function M.validate(help_dir, include, parser_path)
print(('validated (%-4s errors): %s'):format(#rv.parse_errors, helpfile))
if #rv.parse_errors > 0 then
files_to_errors[helpfile] = rv.parse_errors
vim.print(('%s'):format(vim.iter(rv.parse_errors):fold('', function(s, v) return s..'\n '..v end)))
vim.print(('%s'):format(vim.iter(rv.parse_errors):fold('', function(s, v)
return s .. '\n ' .. v
end)))
end
err_count = err_count + #rv.parse_errors
end

View File

@ -16,7 +16,7 @@ local _trace = false
-- Print message
local function p(s)
vim.cmd('set verbose=1')
vim.api.nvim_echo({{s, ''}}, false, {})
vim.api.nvim_echo({ { s, '' } }, false, {})
vim.cmd('set verbose=0')
end
@ -25,7 +25,7 @@ end
-- Prints `cmd` if `trace` is enabled.
local function run(cmd, or_die)
if _trace then
p('run: '..vim.inspect(cmd))
p('run: ' .. vim.inspect(cmd))
end
local rv = vim.trim(vim.fn.system(cmd)) or ''
if vim.v.shell_error ~= 0 then
@ -43,14 +43,14 @@ end
local function validate_commit(commit_message)
-- Return nil if the commit message starts with "fixup" as it signifies it's
-- a work in progress and shouldn't be linted yet.
if vim.startswith(commit_message, "fixup") then
if vim.startswith(commit_message, 'fixup') then
return nil
end
local commit_split = vim.split(commit_message, ":", {plain = true})
local commit_split = vim.split(commit_message, ':', { plain = true })
-- Return nil if the type is vim-patch since most of the normal rules don't
-- apply.
if commit_split[1] == "vim-patch" then
if commit_split[1] == 'vim-patch' then
return nil
end
@ -81,32 +81,34 @@ local function validate_commit(commit_message)
end
-- Check if commit introduces a breaking change.
if vim.endswith(before_colon, "!") then
if vim.endswith(before_colon, '!') then
before_colon = before_colon:sub(1, -2)
end
-- Check if type is correct
local type = vim.split(before_colon, "(", {plain = true})[1]
local allowed_types = {'build', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'test', 'vim-patch'}
local type = vim.split(before_colon, '(', { plain = true })[1]
local allowed_types =
{ 'build', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'test', 'vim-patch' }
if not vim.tbl_contains(allowed_types, type) then
return string.format(
[[Invalid commit type "%s". Allowed types are:
%s.
If none of these seem appropriate then use "fix"]],
type,
vim.inspect(allowed_types))
vim.inspect(allowed_types)
)
end
-- Check if scope is appropriate
if before_colon:match("%(") then
local scope = vim.trim(commit_message:match("%((.-)%)"))
if before_colon:match('%(') then
local scope = vim.trim(commit_message:match('%((.-)%)'))
if scope == '' then
return [[Scope can't be empty]]
end
if vim.startswith(scope, "nvim_") then
return [[Scope should be "api" instead of "nvim_..."]]
if vim.startswith(scope, 'nvim_') then
return [[Scope should be "api" instead of "nvim_..."]]
end
local alternative_scope = {
@ -123,17 +125,17 @@ local function validate_commit(commit_message)
end
-- Check that description doesn't end with a period
if vim.endswith(after_colon, ".") then
if vim.endswith(after_colon, '.') then
return [[Description ends with a period (".").]]
end
-- Check that description starts with a whitespace.
if after_colon:sub(1,1) ~= " " then
if after_colon:sub(1, 1) ~= ' ' then
return [[There should be a whitespace after the colon.]]
end
-- Check that description doesn't start with multiple whitespaces.
if after_colon:sub(1,2) == " " then
if after_colon:sub(1, 2) == ' ' then
return [[There should only be one whitespace after the colon.]]
end
@ -143,7 +145,7 @@ local function validate_commit(commit_message)
end
-- Check that description isn't just whitespaces
if vim.trim(after_colon) == "" then
if vim.trim(after_colon) == '' then
return [[Description shouldn't be empty.]]
end
@ -154,25 +156,25 @@ end
function M.main(opt)
_trace = not opt or not not opt.trace
local branch = run({'git', 'rev-parse', '--abbrev-ref', 'HEAD'}, true)
local branch = run({ 'git', 'rev-parse', '--abbrev-ref', 'HEAD' }, true)
-- TODO(justinmk): check $GITHUB_REF
local ancestor = run({'git', 'merge-base', 'origin/master', branch})
local ancestor = run({ 'git', 'merge-base', 'origin/master', branch })
if not ancestor then
ancestor = run({'git', 'merge-base', 'upstream/master', branch})
ancestor = run({ 'git', 'merge-base', 'upstream/master', branch })
end
local commits_str = run({'git', 'rev-list', ancestor..'..'..branch}, true)
local commits_str = run({ 'git', 'rev-list', ancestor .. '..' .. branch }, true)
assert(commits_str)
local commits = {} --- @type string[]
for substring in commits_str:gmatch("%S+") do
table.insert(commits, substring)
for substring in commits_str:gmatch('%S+') do
table.insert(commits, substring)
end
local failed = 0
for _, commit_id in ipairs(commits) do
local msg = run({'git', 'show', '-s', '--format=%s' , commit_id})
local msg = run({ 'git', 'show', '-s', '--format=%s', commit_id })
if vim.v.shell_error ~= 0 then
p('Invalid commit-id: '..commit_id..'"')
p('Invalid commit-id: ' .. commit_id .. '"')
else
local invalid_msg = validate_commit(msg)
if invalid_msg then
@ -183,20 +185,22 @@ function M.main(opt)
p('\n')
end
p(string.format([[
p(string.format(
[[
Invalid commit message: "%s"
Commit: %s
%s
]],
msg,
commit_id,
invalid_msg))
invalid_msg
))
end
end
end
if failed > 0 then
p([[
p([[
See also:
https://github.com/neovim/neovim/blob/master/CONTRIBUTING.md#commit-messages
@ -259,7 +263,7 @@ function M._test()
['feat(:grep/:make)'] = false,
['feat(:grep'] = false,
['feat(:grep/:make'] = false,
['ci: you\'re saying this commit message just goes on and on and on and on and on and on for way too long?'] = false,
["ci: you're saying this commit message just goes on and on and on and on and on and on for way too long?"] = false,
}
local failed = 0
@ -267,14 +271,15 @@ function M._test()
local is_valid = (nil == validate_commit(message))
if is_valid ~= expected then
failed = failed + 1
p(string.format('[ FAIL ]: expected=%s, got=%s\n input: "%s"', expected, is_valid, message))
p(
string.format('[ FAIL ]: expected=%s, got=%s\n input: "%s"', expected, is_valid, message)
)
end
end
if failed > 0 then
os.exit(1)
end
end
--- @class LintcommitOptions

View File

@ -59,9 +59,12 @@ local TAGGED_TYPES = { 'TSNode', 'LanguageTree' }
-- Document these as 'table'
local ALIAS_TYPES = {
'Range', 'Range4', 'Range6', 'TSMetadata',
'Range',
'Range4',
'Range6',
'TSMetadata',
'vim.filetype.add.filetypes',
'vim.filetype.match.args'
'vim.filetype.match.args',
}
local debug_outfile = nil --- @type string?
@ -103,7 +106,7 @@ function StreamRead.new(filename)
-- syphon lines to our table
local filecontents = {} --- @type string[]
for line in io.lines(filename) do
filecontents[#filecontents+1] = line
filecontents[#filecontents + 1] = line
end
return setmetatable({
@ -176,9 +179,15 @@ local function process_magic(line, generics)
local magic_split = vim.split(magic, ' ', { plain = true })
local directive = magic_split[1]
if vim.list_contains({
'cast', 'diagnostic', 'overload', 'meta', 'type'
}, directive) then
if
vim.list_contains({
'cast',
'diagnostic',
'overload',
'meta',
'type',
}, directive)
then
-- Ignore LSP directives
return '// gg:"' .. line .. '"'
end
@ -202,8 +211,7 @@ local function process_magic(line, generics)
if directive == 'param' then
for _, type in ipairs(TYPES) do
magic = magic:gsub('^param%s+([a-zA-Z_?]+)%s+.*%((' .. type .. ')%)', 'param %1 %2')
magic =
magic:gsub('^param%s+([a-zA-Z_?]+)%s+.*%((' .. type .. '|nil)%)', 'param %1 %2')
magic = magic:gsub('^param%s+([a-zA-Z_?]+)%s+.*%((' .. type .. '|nil)%)', 'param %1 %2')
end
magic_split = vim.split(magic, ' ', { plain = true })
type_index = 3
@ -225,7 +233,7 @@ local function process_magic(line, generics)
-- fix optional parameters
if magic_split[2]:find('%?$') then
if not ty:find('nil') then
ty = ty .. '|nil'
ty = ty .. '|nil'
end
magic_split[2] = magic_split[2]:sub(1, -2)
end
@ -240,18 +248,15 @@ local function process_magic(line, generics)
end
for _, type in ipairs(ALIAS_TYPES) do
ty = ty:gsub('^'..type..'$', 'table') --- @type string
ty = ty:gsub('^' .. type .. '$', 'table') --- @type string
end
-- surround some types by ()
for _, type in ipairs(TYPES) do
ty = ty
:gsub('^(' .. type .. '|nil):?$', '(%1)')
:gsub('^(' .. type .. '):?$', '(%1)')
ty = ty:gsub('^(' .. type .. '|nil):?$', '(%1)'):gsub('^(' .. type .. '):?$', '(%1)')
end
magic_split[type_index] = ty
end
magic = table.concat(magic_split, ' ')
@ -281,7 +286,7 @@ local function process_block_comment(line, in_stream)
-- easier to program
in_stream:ungetLine(vim.trim(line:sub(closeSquare + 2)))
end
comment_parts[#comment_parts+1] = thisComment
comment_parts[#comment_parts + 1] = thisComment
end
local comment = table.concat(comment_parts)
@ -303,7 +308,7 @@ local function process_function_header(line)
if fn:sub(1, 1) == '(' then
-- it's an anonymous function
return '// ZZ: '..line
return '// ZZ: ' .. line
end
-- fn has a name, so is interesting
@ -330,10 +335,7 @@ local function process_function_header(line)
comma = ', '
end
fn = fn:sub(1, paren_start)
.. 'self'
.. comma
.. fn:sub(paren_start + 1)
fn = fn:sub(1, paren_start) .. 'self' .. comma .. fn:sub(paren_start + 1)
end
if line:match('local') then
@ -357,7 +359,7 @@ local function process_line(line, in_stream, generics)
return process_magic(line:sub(4), generics)
end
if vim.startswith(line, '--'..'[[') then -- it's a long comment
if vim.startswith(line, '--' .. '[[') then -- it's a long comment
return process_block_comment(line:sub(5), in_stream)
end
@ -375,7 +377,7 @@ local function process_line(line, in_stream, generics)
local v = line_raw:match('^([A-Za-z][.a-zA-Z_]*)%s+%=')
if v and v:match('%.') then
-- Special: this lets gen_vimdoc.py handle tables.
return 'table '..v..'() {}'
return 'table ' .. v .. '() {}'
end
end
@ -418,7 +420,7 @@ local TApp = {
timestamp = os.date('%c %Z', os.time()),
name = 'Lua2DoX',
version = '0.2 20130128',
copyright = 'Copyright (c) Simon Dales 2012-13'
copyright = 'Copyright (c) Simon Dales 2012-13',
}
setmetatable(TApp, { __index = TApp })
@ -447,12 +449,15 @@ if arg[1] == '--help' then
elseif arg[1] == '--version' then
writeln(TApp:getVersion())
writeln(TApp.copyright)
else -- It's a filter.
else -- It's a filter.
local filename = arg[1]
if arg[2] == '--outdir' then
local outdir = arg[3]
if type(outdir) ~= 'string' or (0 ~= vim.fn.filereadable(outdir) and 0 == vim.fn.isdirectory(outdir)) then
if
type(outdir) ~= 'string'
or (0 ~= vim.fn.filereadable(outdir) and 0 == vim.fn.isdirectory(outdir))
then
error(('invalid --outdir: "%s"'):format(tostring(outdir)))
end
vim.fn.mkdir(outdir, 'p')

View File

@ -10,13 +10,13 @@ local function systemlist(...)
local err = nvim.nvim_get_vvar('shell_error')
local args_str = nvim.nvim_call_function('string', ...)
if 0 ~= err then
error('command failed: '..args_str)
error('command failed: ' .. args_str)
end
return rv
end
local function vimpatch_sh_list_numbers()
return systemlist( { { 'bash', '-c', 'scripts/vim-patch.sh -M', } } )
return systemlist({ { 'bash', '-c', 'scripts/vim-patch.sh -M' } })
end
-- Generates the lines to be inserted into the src/version.c
@ -55,9 +55,9 @@ local function patch_version_c()
nvim.nvim_command('silent normal! j0d/};\rk')
-- Insert the lines.
nvim.nvim_call_function('append', {
nvim.nvim_eval('line(".")'),
lines,
})
nvim.nvim_eval('line(".")'),
lines,
})
nvim.nvim_command('silent write')
end