Compare commits
No commits in common. "refactor/derive-enums-from-man" and "main" have entirely different histories.
refactor/d
...
main
2 changed files with 61 additions and 143 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue