feat(ssh_config): Add completions support for tokens

This commit is contained in:
Myzel394 2024-10-14 12:51:19 +02:00
parent 3caaf6aec4
commit b4aaa2cc3a
No known key found for this signature in database
GPG Key ID: ED20A1D1D423AF3F
6 changed files with 69 additions and 37 deletions

View File

@ -28,7 +28,7 @@ func analyzeTokens(
tokens = []string{}
}
disallowedTokens := utils.Without(fields.AvailableTokens, tokens)
disallowedTokens := utils.Without(utils.KeysOfMap(fields.AvailableTokens), tokens)
for _, token := range disallowedTokens {
if strings.Contains(text, token) {

View File

@ -1,25 +1,27 @@
package fields
var AvailableTokens = []string{
"%%",
"%C",
"%d",
"%f",
"%H",
"%h",
"%l",
"%i",
"%j",
"%K",
"%k",
"%L",
"%l",
"%n",
"%p",
"%r",
"%T",
"%t",
"%u",
import "config-lsp/utils"
var AvailableTokens = map[string]string{
"%%": "A literal %.",
"%C": "Hash of %l%h%p%r%j.",
"%d": "Local user's home directory.",
"%f": "The fingerprint of the server's host key.",
"%H": "The known_hosts hostname or address that is being searched for.",
"%h": "The remote hostname.",
"%I": "A string describing the reason for a KnownHostsCommand execution: either ADDRESS when looking up a host by address (only when CheckHostIP is enabled), HOSTNAME when searching by hostname, or ORDER when preparing the host key algorithm preference list to use for the destination host.",
"%i": "The local user ID.",
"%j": "The contents of the ProxyJump option, or the empty string if this option is unset.",
"%K": "The base64 encoded host key.",
"%k": "The host key alias if specified, otherwise the original remote hostname given on the command line.",
"%L": "The local hostname.",
"%l": "The local hostname, including the domain name.",
"%n": "The original remote hostname, as given on the command line.",
"%p": "The remote port.",
"%r": "The remote username.",
"%T": "The local tun(4) or tap(4) network interface assigned if tunnel forwarding was requested, or \"NONE\" otherwise.",
"%t": "The type of the server host key, e.g. ssh-ed25519.",
"%u": "The local username.",
}
// A map of <option name> to <list of supported tokens>
@ -47,7 +49,7 @@ var OptionsTokensMap = map[NormalizedOptionName][]string{
"%h",
},
"localcommand": AvailableTokens,
"localcommand": utils.KeysOfMap(AvailableTokens),
"proxycommand": {
"%%", "%h", "%n", "%p", "%r",

View File

@ -15,7 +15,7 @@ func GetRootCompletions(
d *sshconfig.SSHDocument,
parentBlock ast.SSHBlock,
suggestValue bool,
) ([]protocol.CompletionItem, error) {
) []protocol.CompletionItem {
kind := protocol.CompletionItemKindField
availableOptions := make(map[fields.NormalizedOptionName]docvalues.DocumentationValue, 0)
@ -58,7 +58,7 @@ func GetRootCompletions(
return *completion
},
), nil
)
}
func GetOptionCompletions(
@ -67,11 +67,11 @@ func GetOptionCompletions(
block ast.SSHBlock,
line uint32,
cursor common.CursorPosition,
) ([]protocol.CompletionItem, error) {
) []protocol.CompletionItem {
option, found := fields.Options[entry.Key.Key]
if !found {
return nil, nil
return nil
}
if entry.Key.Key == matchOption {
@ -92,18 +92,48 @@ func GetOptionCompletions(
}
if entry.OptionValue == nil {
return option.DeprecatedFetchCompletions("", 0), nil
return option.DeprecatedFetchCompletions("", 0)
}
// token completions
completions := getTokenCompletions(entry, cursor)
// Hello wo|rld
lineValue := entry.OptionValue.Value.Raw
// NEW: docvalues index
return option.DeprecatedFetchCompletions(
completions = append(completions, option.DeprecatedFetchCompletions(
lineValue,
common.DeprecatedImprovedCursorToIndex(
cursor,
lineValue,
entry.OptionValue.Start.Character,
),
), nil
)...)
return completions
}
func getTokenCompletions(
entry *ast.SSHOption,
cursor common.CursorPosition,
) []protocol.CompletionItem {
completions := make([]protocol.CompletionItem, 0)
index := common.CursorToCharacterIndex(uint32(cursor))
if entry.Value.Raw[index] == '%' {
if tokens, found := fields.OptionsTokensMap[entry.Key.Key]; found {
for _, token := range tokens {
description := fields.AvailableTokens[token]
kind := protocol.CompletionItemKindConstant
completions = append(completions, protocol.CompletionItem{
Label: token,
Kind: &kind,
Documentation: description,
})
}
}
}
return completions
}

View File

@ -14,12 +14,12 @@ func getMatchCompletions(
d *sshconfig.SSHDocument,
cursor common.CursorPosition,
match *matchparser.Match,
) ([]protocol.CompletionItem, error) {
) []protocol.CompletionItem {
if match == nil || len(match.Entries) == 0 {
completions := getMatchCriteriaCompletions()
completions = append(completions, getMatchAllKeywordCompletion())
return completions, nil
return completions
}
entry := match.GetEntryAtPosition(cursor)
@ -39,10 +39,10 @@ func getMatchCompletions(
completions = append(completions, getMatchAllKeywordCompletion())
}
return completions, nil
return completions
}
return getMatchValueCompletions(entry, cursor), nil
return getMatchValueCompletions(entry, cursor)
}
func getMatchCriteriaCompletions() []protocol.CompletionItem {

View File

@ -15,7 +15,7 @@ func getTagCompletions(
line uint32,
cursor common.CursorPosition,
entry *ast.SSHOption,
) ([]protocol.CompletionItem, error) {
) []protocol.CompletionItem {
completions := make([]protocol.CompletionItem, 0)
for name, info := range d.Indexes.Tags {
@ -35,7 +35,7 @@ func getTagCompletions(
})
}
return completions, nil
return completions
}
func renderMatchBlock(

View File

@ -34,7 +34,7 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa
block,
// Empty line, or currently typing a new key
option == nil || isEmptyPattern.Match([]byte(option.Value.Raw[cursor:])),
)
), nil
}
if option.Separator != nil && option.OptionValue.IsPositionAfterStart(cursor) {
@ -44,7 +44,7 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa
block,
line,
cursor,
)
), nil
}
return nil, nil