Compare commits
2 commits
main
...
refactor/d
| Author | SHA1 | Date | |
|---|---|---|---|
| a145b2648a | |||
| e9bb8de70f |
2 changed files with 143 additions and 61 deletions
|
|
@ -18,60 +18,131 @@ function M.enabled()
|
||||||
return vim.bo.filetype == 'sshconfig'
|
return vim.bo.filetype == 'sshconfig'
|
||||||
end
|
end
|
||||||
|
|
||||||
---@type table<string, string[]>
|
---@param fragment string
|
||||||
local static_enums = {
|
---@return string[]?
|
||||||
AddKeysToAgent = { 'yes', 'no', 'ask', 'confirm' },
|
local function parse_value_list(fragment)
|
||||||
AddressFamily = { 'any', 'inet', 'inet6' },
|
fragment = fragment:gsub('%b()', '')
|
||||||
BatchMode = { 'yes', 'no' },
|
fragment = fragment:gsub('"', '')
|
||||||
CanonicalizeHostname = { 'yes', 'no', 'always' },
|
fragment = fragment:gsub(' or ', ', ')
|
||||||
CanonicalizeFallbackLocal = { 'yes', 'no' },
|
fragment = fragment:gsub(' and ', ', ')
|
||||||
CheckHostIP = { 'yes', 'no' },
|
local vals = {}
|
||||||
ClearAllForwardings = { 'yes', 'no' },
|
local seen = {}
|
||||||
Compression = { 'yes', 'no' },
|
for piece in (fragment .. ','):gmatch('%s*(.-),%s*') do
|
||||||
ControlMaster = { 'yes', 'no', 'ask', 'auto', 'autoask' },
|
piece = vim.trim(piece)
|
||||||
EnableEscapeCommandline = { 'yes', 'no' },
|
if piece ~= '' and not piece:find('%s') then
|
||||||
EnableSSHKeysign = { 'yes', 'no' },
|
local val = piece:match('^([%a][%a%d-]+)$')
|
||||||
ExitOnForwardFailure = { 'yes', 'no' },
|
if val then
|
||||||
FingerprintHash = { 'md5', 'sha256' },
|
if not seen[val] then
|
||||||
ForkAfterAuthentication = { 'yes', 'no' },
|
seen[val] = true
|
||||||
ForwardAgent = { 'yes', 'no' },
|
vals[#vals + 1] = val
|
||||||
ForwardX11 = { 'yes', 'no' },
|
end
|
||||||
ForwardX11Trusted = { 'yes', 'no' },
|
end
|
||||||
GatewayPorts = { 'yes', 'no' },
|
end
|
||||||
GSSAPIAuthentication = { 'yes', 'no' },
|
end
|
||||||
GSSAPIDelegateCredentials = { 'yes', 'no' },
|
return #vals >= 2 and vals or nil
|
||||||
HashKnownHosts = { 'yes', 'no' },
|
end
|
||||||
HostbasedAuthentication = { 'yes', 'no' },
|
|
||||||
IdentitiesOnly = { 'yes', 'no' },
|
---@param man_stdout string
|
||||||
KbdInteractiveAuthentication = { 'yes', 'no' },
|
---@return table<string, string[]>
|
||||||
LogLevel = {
|
local function extract_enums_from_man(man_stdout)
|
||||||
'QUIET',
|
local lines = {}
|
||||||
'FATAL',
|
for line in (man_stdout .. '\n'):gmatch('(.-)\n') do
|
||||||
'ERROR',
|
lines[#lines + 1] = line
|
||||||
'INFO',
|
end
|
||||||
'VERBOSE',
|
|
||||||
'DEBUG',
|
local defs = {}
|
||||||
'DEBUG1',
|
for i, line in ipairs(lines) do
|
||||||
'DEBUG2',
|
local kw = line:match('^ (%u[%a%d]+)%s*$')
|
||||||
'DEBUG3',
|
or line:match('^ (%u[%a%d]+) ')
|
||||||
},
|
or line:match('^ (%u[%a%d]+) %u')
|
||||||
NoHostAuthenticationForLocalhost = { 'yes', 'no' },
|
if kw then
|
||||||
PasswordAuthentication = { 'yes', 'no' },
|
defs[#defs + 1] = { line = i, keyword = kw }
|
||||||
PermitLocalCommand = { 'yes', 'no' },
|
end
|
||||||
PermitRemoteOpen = { 'any', 'none' },
|
end
|
||||||
ProxyUseFdpass = { 'yes', 'no' },
|
|
||||||
PubkeyAuthentication = { 'yes', 'no', 'unbound', 'host-bound' },
|
local enums = {}
|
||||||
RequestTTY = { 'yes', 'no', 'force', 'auto' },
|
for idx, def in ipairs(defs) do
|
||||||
SessionType = { 'none', 'subsystem', 'default' },
|
local block_end = (defs[idx + 1] and defs[idx + 1].line or #lines) - 1
|
||||||
StdinNull = { 'yes', 'no' },
|
local parts = {}
|
||||||
StreamLocalBindUnlink = { 'yes', 'no' },
|
for k = def.line + 1, block_end do
|
||||||
StrictHostKeyChecking = { 'yes', 'no', 'ask', 'accept-new', 'off' },
|
parts[#parts + 1] = lines[k]
|
||||||
TCPKeepAlive = { 'yes', 'no' },
|
end
|
||||||
Tunnel = { 'yes', 'no', 'point-to-point', 'ethernet' },
|
local text = table.concat(parts, ' ')
|
||||||
UpdateHostKeys = { 'yes', 'no', 'ask' },
|
text = text:gsub(string.char(0xe2, 0x80, 0x90) .. '%s+', '')
|
||||||
VerifyHostKeyDNS = { 'yes', 'no', 'ask' },
|
text = text:gsub('%s+', ' ')
|
||||||
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 = {
|
||||||
|
|
@ -94,9 +165,11 @@ 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+)%s*$') or line:match('^ (%u%a+) ')
|
local kw = line:match('^ (%u[%a%d]+)%s*$')
|
||||||
|
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+%s%s%s+(.+)')
|
local inline = line:match('^ %u[%a%d]+%s%s+(.+)')
|
||||||
defs[#defs + 1] = { line = i, keyword = kw, inline = inline }
|
defs[#defs + 1] = { line = i, keyword = kw, inline = inline }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -147,11 +220,12 @@ 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)
|
local function parse_enums(stdout, man_enums)
|
||||||
local enums = {}
|
local enums = {}
|
||||||
for k, v in pairs(static_enums) do
|
for k, v in pairs(man_enums) do
|
||||||
enums[k:lower()] = v
|
enums[k] = v
|
||||||
end
|
end
|
||||||
|
|
||||||
local current_query = nil
|
local current_query = nil
|
||||||
|
|
@ -249,7 +323,11 @@ function M:get_completions(ctx, callback)
|
||||||
kw = {}
|
kw = {}
|
||||||
end
|
end
|
||||||
keywords_cache = kw
|
keywords_cache = kw
|
||||||
local ok_en, en = pcall(parse_enums, enums_out)
|
local ok_me, me = pcall(extract_enums_from_man, man_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,7 +12,11 @@ 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.',
|
' to hosts whose host key has changed. If this flag is set to',
|
||||||
|
' 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