mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
feat(sshd_config): Add analyzer to check whether values are valid
This commit is contained in:
parent
ea6fdd8cda
commit
47c511996b
@ -104,7 +104,7 @@ func (v KeyValueAssignmentValue) CheckIsValid(value string) []*InvalidValue {
|
||||
}
|
||||
|
||||
func (v KeyValueAssignmentValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem {
|
||||
if cursor == 0 {
|
||||
if cursor == 0 || line == "" {
|
||||
return v.Key.FetchCompletions(line, cursor)
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,26 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/handlers/sshd_config"
|
||||
"config-lsp/utils"
|
||||
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
func Analyze(
|
||||
d *sshdconfig.SSHDocument,
|
||||
) []protocol.Diagnostic {
|
||||
errors := analyzeOptionsAreValid(d)
|
||||
|
||||
if len(errors) > 0 {
|
||||
return utils.Map(
|
||||
errors,
|
||||
func(err common.LSPError) protocol.Diagnostic {
|
||||
return err.ToDiagnostic()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
60
handlers/sshd_config/analyzer/options.go
Normal file
60
handlers/sshd_config/analyzer/options.go
Normal file
@ -0,0 +1,60 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
docvalues "config-lsp/doc-values"
|
||||
sshdconfig "config-lsp/handlers/sshd_config"
|
||||
"config-lsp/handlers/sshd_config/ast"
|
||||
"config-lsp/handlers/sshd_config/fields"
|
||||
"config-lsp/utils"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func analyzeOptionsAreValid(
|
||||
d *sshdconfig.SSHDocument,
|
||||
) []common.LSPError {
|
||||
errs := make([]common.LSPError, 0)
|
||||
it := d.Config.Options.Iterator()
|
||||
|
||||
for it.Next() {
|
||||
line := it.Key().(uint32)
|
||||
entry := it.Value().(ast.SSHEntry)
|
||||
|
||||
option := entry.GetOption()
|
||||
|
||||
if option.Key != nil {
|
||||
docOption, found := fields.Options[option.Key.Value]
|
||||
|
||||
if !found {
|
||||
errs = append(errs, common.LSPError{
|
||||
Range: option.Key.LocationRange,
|
||||
Err: errors.New(fmt.Sprintf("Unknown option: %s", option.Key.Value)),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if option.OptionValue == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
invalidValues := docOption.CheckIsValid(option.OptionValue.Value)
|
||||
|
||||
errs = append(
|
||||
errs,
|
||||
utils.Map(
|
||||
invalidValues,
|
||||
func(invalidValue *docvalues.InvalidValue) common.LSPError {
|
||||
err := docvalues.LSPErrorFromInvalidValue(line, *invalidValue)
|
||||
err.ShiftCharacter(option.OptionValue.Start.Character)
|
||||
|
||||
return err
|
||||
},
|
||||
)...,
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
@ -25,6 +25,7 @@ const (
|
||||
|
||||
type SSHEntry interface {
|
||||
GetType() SSHEntryType
|
||||
GetOption() SSHOption
|
||||
}
|
||||
|
||||
type SSHSeparator struct {
|
||||
@ -44,6 +45,10 @@ func (o SSHOption) GetType() SSHEntryType {
|
||||
return SSHEntryTypeOption
|
||||
}
|
||||
|
||||
func (o SSHOption) GetOption() SSHOption {
|
||||
return o
|
||||
}
|
||||
|
||||
type SSHMatchBlock struct {
|
||||
common.LocationRange
|
||||
MatchEntry *SSHOption
|
||||
@ -56,6 +61,10 @@ func (m SSHMatchBlock) GetType() SSHEntryType {
|
||||
return SSHEntryTypeMatchBlock
|
||||
}
|
||||
|
||||
func (m SSHMatchBlock) GetOption() SSHOption {
|
||||
return *m.MatchEntry
|
||||
}
|
||||
|
||||
type SSHConfig struct {
|
||||
// [uint32]SSHOption -> line number -> *SSHEntry
|
||||
Options *treemap.Map
|
||||
|
@ -13,23 +13,51 @@ import (
|
||||
func GetRootCompletions(
|
||||
d *sshdconfig.SSHDocument,
|
||||
parentMatchBlock *ast.SSHMatchBlock,
|
||||
suggestValue bool,
|
||||
) ([]protocol.CompletionItem, error) {
|
||||
kind := protocol.CompletionItemKindField
|
||||
format := protocol.InsertTextFormatSnippet
|
||||
|
||||
return utils.MapMapToSlice(
|
||||
fields.Options,
|
||||
func(name string, rawValue docvalues.Value) protocol.CompletionItem {
|
||||
doc := rawValue.(docvalues.DocumentationValue)
|
||||
|
||||
insertText := name + " " + "${1:value}"
|
||||
return protocol.CompletionItem{
|
||||
completion := &protocol.CompletionItem{
|
||||
Label: name,
|
||||
Kind: &kind,
|
||||
Documentation: doc.Documentation,
|
||||
InsertText: &insertText,
|
||||
InsertTextFormat: &format,
|
||||
}
|
||||
|
||||
if suggestValue {
|
||||
format := protocol.InsertTextFormatSnippet
|
||||
insertText := name + " " + "${1:value}"
|
||||
|
||||
completion.InsertTextFormat = &format
|
||||
completion.InsertText = &insertText
|
||||
}
|
||||
|
||||
return *completion
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
func GetOptionCompletions(
|
||||
d *sshdconfig.SSHDocument,
|
||||
entry *ast.SSHOption,
|
||||
cursor uint32,
|
||||
) ([]protocol.CompletionItem, error) {
|
||||
option, found := fields.Options[entry.Key.Value]
|
||||
|
||||
if !found {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if entry.OptionValue == nil {
|
||||
return option.FetchCompletions("", 0), nil
|
||||
}
|
||||
|
||||
relativeCursor := cursor - entry.OptionValue.Start.Character
|
||||
line := entry.OptionValue.Value
|
||||
|
||||
return option.FetchCompletions(line, relativeCursor), nil
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package lsp
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
sshdconfig "config-lsp/handlers/sshd_config"
|
||||
"config-lsp/handlers/sshd_config/handlers"
|
||||
"regexp"
|
||||
@ -14,7 +15,6 @@ var containsSeparatorPattern = regexp.MustCompile(`\s+$`)
|
||||
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
|
||||
line := params.Position.Line
|
||||
cursor := params.Position.Character
|
||||
_ = cursor
|
||||
|
||||
d := sshdconfig.DocumentParserMap[params.TextDocument.URI]
|
||||
|
||||
@ -24,11 +24,23 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa
|
||||
|
||||
entry, matchBlock := d.Config.FindOption(line)
|
||||
|
||||
if entry == nil || entry.Separator == nil {
|
||||
if entry == nil ||
|
||||
entry.Separator == nil ||
|
||||
entry.Key == nil ||
|
||||
(common.CursorToCharacterIndex(cursor)) <= entry.Key.End.Character {
|
||||
// Empty line
|
||||
return handlers.GetRootCompletions(
|
||||
d,
|
||||
matchBlock,
|
||||
entry == nil || containsSeparatorPattern.Match([]byte(entry.Value)),
|
||||
)
|
||||
}
|
||||
|
||||
if entry.Separator != nil && cursor > entry.Separator.End.Character {
|
||||
return handlers.GetOptionCompletions(
|
||||
d,
|
||||
entry,
|
||||
cursor,
|
||||
)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user