Compare commits

..

No commits in common. "refactor/derive-enums-from-man" and "main" have entirely different histories.

2 changed files with 61 additions and 143 deletions

View file

@ -18,131 +18,60 @@ function M.enabled()
return vim.bo.filetype == 'sshconfig' return vim.bo.filetype == 'sshconfig'
end end
---@param fragment string ---@type table<string, string[]>
---@return string[]? local static_enums = {
local function parse_value_list(fragment) AddKeysToAgent = { 'yes', 'no', 'ask', 'confirm' },
fragment = fragment:gsub('%b()', '') AddressFamily = { 'any', 'inet', 'inet6' },
fragment = fragment:gsub('"', '') BatchMode = { 'yes', 'no' },
fragment = fragment:gsub(' or ', ', ') CanonicalizeHostname = { 'yes', 'no', 'always' },
fragment = fragment:gsub(' and ', ', ') CanonicalizeFallbackLocal = { 'yes', 'no' },
local vals = {} CheckHostIP = { 'yes', 'no' },
local seen = {} ClearAllForwardings = { 'yes', 'no' },
for piece in (fragment .. ','):gmatch('%s*(.-),%s*') do Compression = { 'yes', 'no' },
piece = vim.trim(piece) ControlMaster = { 'yes', 'no', 'ask', 'auto', 'autoask' },
if piece ~= '' and not piece:find('%s') then EnableEscapeCommandline = { 'yes', 'no' },
local val = piece:match('^([%a][%a%d-]+)$') EnableSSHKeysign = { 'yes', 'no' },
if val then ExitOnForwardFailure = { 'yes', 'no' },
if not seen[val] then FingerprintHash = { 'md5', 'sha256' },
seen[val] = true ForkAfterAuthentication = { 'yes', 'no' },
vals[#vals + 1] = val ForwardAgent = { 'yes', 'no' },
end ForwardX11 = { 'yes', 'no' },
end ForwardX11Trusted = { 'yes', 'no' },
end GatewayPorts = { 'yes', 'no' },
end GSSAPIAuthentication = { 'yes', 'no' },
return #vals >= 2 and vals or nil GSSAPIDelegateCredentials = { 'yes', 'no' },
end HashKnownHosts = { 'yes', 'no' },
HostbasedAuthentication = { 'yes', 'no' },
---@param man_stdout string IdentitiesOnly = { 'yes', 'no' },
---@return table<string, string[]> KbdInteractiveAuthentication = { 'yes', 'no' },
local function extract_enums_from_man(man_stdout) LogLevel = {
local lines = {} 'QUIET',
for line in (man_stdout .. '\n'):gmatch('(.-)\n') do 'FATAL',
lines[#lines + 1] = line 'ERROR',
end 'INFO',
'VERBOSE',
local defs = {} 'DEBUG',
for i, line in ipairs(lines) do 'DEBUG1',
local kw = line:match('^ (%u[%a%d]+)%s*$') 'DEBUG2',
or line:match('^ (%u[%a%d]+) ') 'DEBUG3',
or line:match('^ (%u[%a%d]+) %u') },
if kw then NoHostAuthenticationForLocalhost = { 'yes', 'no' },
defs[#defs + 1] = { line = i, keyword = kw } PasswordAuthentication = { 'yes', 'no' },
end PermitLocalCommand = { 'yes', 'no' },
end PermitRemoteOpen = { 'any', 'none' },
ProxyUseFdpass = { 'yes', 'no' },
local enums = {} PubkeyAuthentication = { 'yes', 'no', 'unbound', 'host-bound' },
for idx, def in ipairs(defs) do RequestTTY = { 'yes', 'no', 'force', 'auto' },
local block_end = (defs[idx + 1] and defs[idx + 1].line or #lines) - 1 SessionType = { 'none', 'subsystem', 'default' },
local parts = {} StdinNull = { 'yes', 'no' },
for k = def.line + 1, block_end do StreamLocalBindUnlink = { 'yes', 'no' },
parts[#parts + 1] = lines[k] StrictHostKeyChecking = { 'yes', 'no', 'ask', 'accept-new', 'off' },
end TCPKeepAlive = { 'yes', 'no' },
local text = table.concat(parts, ' ') Tunnel = { 'yes', 'no', 'point-to-point', 'ethernet' },
text = text:gsub(string.char(0xe2, 0x80, 0x90) .. '%s+', '') UpdateHostKeys = { 'yes', 'no', 'ask' },
text = text:gsub('%s+', ' ') VerifyHostKeyDNS = { 'yes', 'no', 'ask' },
VisualHostKey = { 'yes', 'no' },
local list = text:match('[Tt]he argument must be (.-)%.') }
or text:match('[Tt]he argument to this keyword must be (.-)%.')
or text:match('[Tt]he argument may be one of:? (.-)%.')
or text:match('[Tt]he argument may be (.-)%.')
or text:match('[Tt]he possible values are:? (.-)%.')
or text:match('[Vv]alid arguments are (.-)%.')
or text:match('[Vv]alid options are:? (.-)%.')
or text:match('[Aa]ccepted values are (.-)%.')
local vals = list and parse_value_list(list)
if not vals then
local fvals = {}
local fseen = {}
local function add(v)
if v and #v >= 2 and not fseen[v] then
fseen[v] = true
fvals[#fvals + 1] = v
end
end
for v1, v2 in text:gmatch(' is set to "?([%a][%a%d-]+)"? or "?([%a][%a%d-]+)"?') do
add(v1)
add(v2)
end
for v in text:gmatch(' is set to "?([%a][%a%d-]+)"?') do
add(v)
end
for v in text:gmatch('%u[%a%d]+ set to "?([%a][%a%d-]+)"?') do
add(v)
end
for v in text:gmatch('When set to "?([%a][%a%d-]+)"?') do
add(v)
end
for v in text:gmatch('[Ss]etting %S+ to "?([%a][%a%d-]+)"?') do
add(v)
end
for v in text:gmatch('value %S+ be set to "?([%a][%a%d-]+)"?') do
add(v)
end
local these = text:match('[Tt]hese options are:? (.-)%.')
if these then
local tv = parse_value_list(these)
if tv then
for _, v in ipairs(tv) do
add(v)
end
end
end
for v in text:gmatch('[Tt]he default is "?([%a][%a%d-]+)"?') do
if v ~= 'to' then
add(v)
end
end
for v in text:gmatch('[Tt]he default, "?([%a][%a%d-]+)"?') do
add(v)
end
for v in text:gmatch('[Aa]n argument of "?([%a][%a%d-]+)"?') do
add(v)
end
for v in text:gmatch('[Aa] value of "?([%a][%a%d-]+)"?') do
add(v)
end
if #fvals >= 2 then
vals = fvals
end
end
if vals then
enums[def.keyword:lower()] = vals
end
end
return enums
end
---@type table<string, string[]> ---@type table<string, string[]>
local query_to_keywords = { local query_to_keywords = {
@ -165,11 +94,9 @@ local function parse_keywords(stdout)
local defs = {} local defs = {}
for i, line in ipairs(lines) do for i, line in ipairs(lines) do
local kw = line:match('^ (%u[%a%d]+)%s*$') local kw = line:match('^ (%u%a+)%s*$') or line:match('^ (%u%a+) ')
or line:match('^ (%u[%a%d]+) ')
or line:match('^ (%u[%a%d]+) %u')
if kw then if kw then
local inline = line:match('^ %u[%a%d]+%s%s+(.+)') local inline = line:match('^ %u%a+%s%s%s+(.+)')
defs[#defs + 1] = { line = i, keyword = kw, inline = inline } defs[#defs + 1] = { line = i, keyword = kw, inline = inline }
end end
end end
@ -220,12 +147,11 @@ local function parse_keywords(stdout)
end end
---@param stdout string ---@param stdout string
---@param man_enums table<string, string[]>
---@return table<string, string[]> ---@return table<string, string[]>
local function parse_enums(stdout, man_enums) local function parse_enums(stdout)
local enums = {} local enums = {}
for k, v in pairs(man_enums) do for k, v in pairs(static_enums) do
enums[k] = v enums[k:lower()] = v
end end
local current_query = nil local current_query = nil
@ -323,11 +249,7 @@ function M:get_completions(ctx, callback)
kw = {} kw = {}
end end
keywords_cache = kw keywords_cache = kw
local ok_me, me = pcall(extract_enums_from_man, man_out) local ok_en, en = pcall(parse_enums, enums_out)
if not ok_me then
me = {}
end
local ok_en, en = pcall(parse_enums, enums_out, me)
if not ok_en then if not ok_en then
en = {} en = {}
end end

View file

@ -12,11 +12,7 @@ local MAN_PAGE = table.concat({
' StrictHostKeyChecking', ' StrictHostKeyChecking',
' If this flag is set to yes, ssh(1) will never automatically add', ' If this flag is set to yes, ssh(1) will never automatically add',
' host keys to the ~/.ssh/known_hosts file, and refuses to connect', ' host keys to the ~/.ssh/known_hosts file, and refuses to connect',
' to hosts whose host key has changed. If this flag is set to', ' to hosts whose host key has changed.',
' accept-new then ssh will automatically add new host keys. If',
' this flag is set to no or off, ssh will automatically add new',
' host keys. If this flag is set to ask (the default), new host',
' keys will be added only after the user has confirmed.',
'', '',
' Hostname', ' Hostname',
' Specifies the real host name to log into.', ' Specifies the real host name to log into.',