mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
feat(sshd_config): Add support for token completions
This commit is contained in:
parent
b1498ffdc5
commit
ef737e194a
@ -40,7 +40,7 @@ func checkOption(
|
|||||||
checkIsUsingDoubleQuotes(ctx, option.Key.Value, option.Key.LocationRange)
|
checkIsUsingDoubleQuotes(ctx, option.Key.Value, option.Key.LocationRange)
|
||||||
checkQuotesAreClosed(ctx, option.Key.Value, option.Key.LocationRange)
|
checkQuotesAreClosed(ctx, option.Key.Value, option.Key.LocationRange)
|
||||||
|
|
||||||
key := fields.CreateNormalizedName(option.Key.Key)
|
key := option.Key.Key
|
||||||
docOption, found := fields.Options[key]
|
docOption, found := fields.Options[key]
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
commonparser "config-lsp/common/parser"
|
commonparser "config-lsp/common/parser"
|
||||||
"config-lsp/handlers/sshd_config/ast/parser"
|
"config-lsp/handlers/sshd_config/ast/parser"
|
||||||
|
"config-lsp/handlers/sshd_config/fields"
|
||||||
"config-lsp/handlers/sshd_config/match-parser"
|
"config-lsp/handlers/sshd_config/match-parser"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ func (s *sshdParserListener) EnterKey(ctx *parser.KeyContext) {
|
|||||||
s.sshdContext.currentOption.Key = &SSHDKey{
|
s.sshdContext.currentOption.Key = &SSHDKey{
|
||||||
LocationRange: location,
|
LocationRange: location,
|
||||||
Value: value,
|
Value: value,
|
||||||
Key: key,
|
Key: fields.CreateNormalizedName(key),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,14 +3,16 @@ package ast
|
|||||||
import (
|
import (
|
||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
commonparser "config-lsp/common/parser"
|
commonparser "config-lsp/common/parser"
|
||||||
|
"config-lsp/handlers/sshd_config/fields"
|
||||||
"config-lsp/handlers/sshd_config/match-parser"
|
"config-lsp/handlers/sshd_config/match-parser"
|
||||||
|
|
||||||
"github.com/emirpasic/gods/maps/treemap"
|
"github.com/emirpasic/gods/maps/treemap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SSHDKey struct {
|
type SSHDKey struct {
|
||||||
common.LocationRange
|
common.LocationRange
|
||||||
Value commonparser.ParsedString
|
Value commonparser.ParsedString
|
||||||
Key string
|
Key fields.NormalizedOptionName
|
||||||
}
|
}
|
||||||
|
|
||||||
type SSHDValue struct {
|
type SSHDValue struct {
|
||||||
|
71
server/handlers/sshd_config/fields/tokens.go
Normal file
71
server/handlers/sshd_config/fields/tokens.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package fields
|
||||||
|
|
||||||
|
var AvailableTokens = map[string]string{
|
||||||
|
"%%": "A literal ‘%’.",
|
||||||
|
"%C": "Identifies the connection endpoints, containing four space-separated values: client address, client port number, server address, and server port number.",
|
||||||
|
"%D": "The routing domain in which the incoming connection was received.",
|
||||||
|
"%F": "The fingerprint of the CA key.",
|
||||||
|
"%f": "The fingerprint of the key or certificate.",
|
||||||
|
"%h": "The home directory of the user.",
|
||||||
|
"%i": "The key ID in the certificate.",
|
||||||
|
"%K": "The base64-encoded CA key.",
|
||||||
|
"%k": "The base64-encoded key or certificate for authentication.",
|
||||||
|
"%s": "The serial number of the certificate.",
|
||||||
|
"%T": "The type of the CA key.",
|
||||||
|
"%t": "The key or certificate type.",
|
||||||
|
"%U": "The numeric user ID of the target user.",
|
||||||
|
"%u": "The username.",
|
||||||
|
}
|
||||||
|
|
||||||
|
// A map of <option name> to <list of supported tokens>
|
||||||
|
// This is derived from the TOKENS section of ssh_config
|
||||||
|
var OptionsTokensMap = map[NormalizedOptionName][]string{
|
||||||
|
"authorizedkeyscommand": {
|
||||||
|
"%%",
|
||||||
|
"%C",
|
||||||
|
"%D",
|
||||||
|
"%f",
|
||||||
|
"%h",
|
||||||
|
"%k",
|
||||||
|
"%t",
|
||||||
|
"%U",
|
||||||
|
"%u",
|
||||||
|
},
|
||||||
|
"authorizedkeysfile": {
|
||||||
|
"%%",
|
||||||
|
"%h",
|
||||||
|
"%U",
|
||||||
|
"%u",
|
||||||
|
},
|
||||||
|
"authorizedprincipalscommand": {
|
||||||
|
"%%",
|
||||||
|
"%C",
|
||||||
|
"%D",
|
||||||
|
"%F",
|
||||||
|
"%f",
|
||||||
|
"%h",
|
||||||
|
"%i",
|
||||||
|
"%K",
|
||||||
|
"%k",
|
||||||
|
"%s",
|
||||||
|
"%T",
|
||||||
|
"%t",
|
||||||
|
"%U",
|
||||||
|
"%u",
|
||||||
|
},
|
||||||
|
"authorizedprincipalsfile": {
|
||||||
|
"%%",
|
||||||
|
"%h",
|
||||||
|
"%U",
|
||||||
|
"%u",
|
||||||
|
},
|
||||||
|
"chrootdirectory": {
|
||||||
|
"%%",
|
||||||
|
"%h",
|
||||||
|
"%U",
|
||||||
|
"%u",
|
||||||
|
},
|
||||||
|
"routingdomain": {
|
||||||
|
"%D",
|
||||||
|
},
|
||||||
|
}
|
@ -69,12 +69,12 @@ func GetOptionCompletions(
|
|||||||
entry *ast.SSHDOption,
|
entry *ast.SSHDOption,
|
||||||
matchBlock *ast.SSHDMatchBlock,
|
matchBlock *ast.SSHDMatchBlock,
|
||||||
cursor common.CursorPosition,
|
cursor common.CursorPosition,
|
||||||
) ([]protocol.CompletionItem, error) {
|
) []protocol.CompletionItem {
|
||||||
key := fields.CreateNormalizedName(entry.Key.Key)
|
key := entry.Key.Key
|
||||||
option, found := fields.Options[key]
|
option, found := fields.Options[key]
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if entry.Key.Key == "Match" {
|
if entry.Key.Key == "Match" {
|
||||||
@ -86,18 +86,48 @@ func GetOptionCompletions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if entry.OptionValue == nil {
|
if entry.OptionValue == nil {
|
||||||
return option.DeprecatedFetchCompletions("", 0), nil
|
return option.DeprecatedFetchCompletions("", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// token completions
|
||||||
|
completions := getTokenCompletions(entry, cursor)
|
||||||
|
|
||||||
// Hello wo|rld
|
// Hello wo|rld
|
||||||
line := entry.OptionValue.Value.Raw
|
line := entry.OptionValue.Value.Raw
|
||||||
// NEW: docvalues index
|
// NEW: docvalues index
|
||||||
return option.DeprecatedFetchCompletions(
|
completions = append(completions, option.DeprecatedFetchCompletions(
|
||||||
line,
|
line,
|
||||||
common.DeprecatedImprovedCursorToIndex(
|
common.DeprecatedImprovedCursorToIndex(
|
||||||
cursor,
|
cursor,
|
||||||
line,
|
line,
|
||||||
entry.OptionValue.Start.Character,
|
entry.OptionValue.Start.Character,
|
||||||
),
|
),
|
||||||
), nil
|
)...)
|
||||||
|
|
||||||
|
return completions
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTokenCompletions(
|
||||||
|
entry *ast.SSHDOption,
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
@ -12,21 +12,21 @@ func getMatchCompletions(
|
|||||||
d *sshdconfig.SSHDDocument,
|
d *sshdconfig.SSHDDocument,
|
||||||
cursor common.CursorPosition,
|
cursor common.CursorPosition,
|
||||||
match *matchparser.Match,
|
match *matchparser.Match,
|
||||||
) ([]protocol.CompletionItem, error) {
|
) []protocol.CompletionItem {
|
||||||
if match == nil || len(match.Entries) == 0 {
|
if match == nil || len(match.Entries) == 0 {
|
||||||
completions := getMatchCriteriaCompletions()
|
completions := getMatchCriteriaCompletions()
|
||||||
completions = append(completions, getMatchAllKeywordCompletion())
|
completions = append(completions, getMatchAllKeywordCompletion())
|
||||||
|
|
||||||
return completions, nil
|
return completions
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := match.GetEntryAtPosition(cursor)
|
entry := match.GetEntryAtPosition(cursor)
|
||||||
|
|
||||||
if entry == nil || entry.Criteria.ContainsPosition(cursor) {
|
if entry == nil || entry.Criteria.ContainsPosition(cursor) {
|
||||||
return getMatchCriteriaCompletions(), nil
|
return getMatchCriteriaCompletions()
|
||||||
}
|
}
|
||||||
|
|
||||||
return getMatchValueCompletions(entry, cursor), nil
|
return getMatchValueCompletions(entry, cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMatchCriteriaCompletions() []protocol.CompletionItem {
|
func getMatchCriteriaCompletions() []protocol.CompletionItem {
|
||||||
|
@ -30,11 +30,10 @@ func formatSSHDOption(
|
|||||||
var key string
|
var key string
|
||||||
|
|
||||||
if option.Key != nil {
|
if option.Key != nil {
|
||||||
key = option.Key.Key
|
if formattedName, found := fields.FieldsNameFormattedMap[option.Key.Key]; found {
|
||||||
normalizedKey := fields.CreateNormalizedName(key)
|
|
||||||
|
|
||||||
if formattedName, found := fields.FieldsNameFormattedMap[normalizedKey]; found {
|
|
||||||
key = formattedName
|
key = formattedName
|
||||||
|
} else {
|
||||||
|
key = string(option.Key.Key)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
key = ""
|
key = ""
|
||||||
|
@ -17,7 +17,7 @@ func GetHoverInfoForOption(
|
|||||||
index common.IndexPosition,
|
index common.IndexPosition,
|
||||||
) (*protocol.Hover, error) {
|
) (*protocol.Hover, error) {
|
||||||
var docValue *docvalues.DocumentationValue
|
var docValue *docvalues.DocumentationValue
|
||||||
key := fields.CreateNormalizedName(option.Key.Key)
|
key := option.Key.Key
|
||||||
|
|
||||||
// Either root level or in the line of a match block
|
// Either root level or in the line of a match block
|
||||||
if matchBlock == nil || matchBlock.Start.Line == line {
|
if matchBlock == nil || matchBlock.Start.Line == line {
|
||||||
|
@ -89,7 +89,7 @@ func addOption(
|
|||||||
matchBlock *ast.SSHDMatchBlock,
|
matchBlock *ast.SSHDMatchBlock,
|
||||||
) []common.LSPError {
|
) []common.LSPError {
|
||||||
var errs []common.LSPError
|
var errs []common.LSPError
|
||||||
key := fields.CreateNormalizedName(option.Key.Key)
|
key := option.Key.Key
|
||||||
|
|
||||||
if optionsMap, found := i.AllOptionsPerName[key]; found {
|
if optionsMap, found := i.AllOptionsPerName[key]; found {
|
||||||
if options, found := optionsMap[matchBlock]; found {
|
if options, found := optionsMap[matchBlock]; found {
|
||||||
|
@ -43,7 +43,7 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa
|
|||||||
entry,
|
entry,
|
||||||
matchBlock,
|
matchBlock,
|
||||||
cursor,
|
cursor,
|
||||||
)
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
Loading…
x
Reference in New Issue
Block a user