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 {
|
func (v KeyValueAssignmentValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem {
|
||||||
if cursor == 0 {
|
if cursor == 0 || line == "" {
|
||||||
return v.Key.FetchCompletions(line, cursor)
|
return v.Key.FetchCompletions(line, cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,26 @@
|
|||||||
package analyzer
|
package analyzer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
"config-lsp/handlers/sshd_config"
|
"config-lsp/handlers/sshd_config"
|
||||||
|
"config-lsp/utils"
|
||||||
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Analyze(
|
func Analyze(
|
||||||
d *sshdconfig.SSHDocument,
|
d *sshdconfig.SSHDocument,
|
||||||
) []protocol.Diagnostic {
|
) []protocol.Diagnostic {
|
||||||
|
errors := analyzeOptionsAreValid(d)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return utils.Map(
|
||||||
|
errors,
|
||||||
|
func(err common.LSPError) protocol.Diagnostic {
|
||||||
|
return err.ToDiagnostic()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
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 {
|
type SSHEntry interface {
|
||||||
GetType() SSHEntryType
|
GetType() SSHEntryType
|
||||||
|
GetOption() SSHOption
|
||||||
}
|
}
|
||||||
|
|
||||||
type SSHSeparator struct {
|
type SSHSeparator struct {
|
||||||
@ -44,6 +45,10 @@ func (o SSHOption) GetType() SSHEntryType {
|
|||||||
return SSHEntryTypeOption
|
return SSHEntryTypeOption
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o SSHOption) GetOption() SSHOption {
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
type SSHMatchBlock struct {
|
type SSHMatchBlock struct {
|
||||||
common.LocationRange
|
common.LocationRange
|
||||||
MatchEntry *SSHOption
|
MatchEntry *SSHOption
|
||||||
@ -56,6 +61,10 @@ func (m SSHMatchBlock) GetType() SSHEntryType {
|
|||||||
return SSHEntryTypeMatchBlock
|
return SSHEntryTypeMatchBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m SSHMatchBlock) GetOption() SSHOption {
|
||||||
|
return *m.MatchEntry
|
||||||
|
}
|
||||||
|
|
||||||
type SSHConfig struct {
|
type SSHConfig struct {
|
||||||
// [uint32]SSHOption -> line number -> *SSHEntry
|
// [uint32]SSHOption -> line number -> *SSHEntry
|
||||||
Options *treemap.Map
|
Options *treemap.Map
|
||||||
|
@ -13,23 +13,51 @@ import (
|
|||||||
func GetRootCompletions(
|
func GetRootCompletions(
|
||||||
d *sshdconfig.SSHDocument,
|
d *sshdconfig.SSHDocument,
|
||||||
parentMatchBlock *ast.SSHMatchBlock,
|
parentMatchBlock *ast.SSHMatchBlock,
|
||||||
|
suggestValue bool,
|
||||||
) ([]protocol.CompletionItem, error) {
|
) ([]protocol.CompletionItem, error) {
|
||||||
kind := protocol.CompletionItemKindField
|
kind := protocol.CompletionItemKindField
|
||||||
format := protocol.InsertTextFormatSnippet
|
|
||||||
|
|
||||||
return utils.MapMapToSlice(
|
return utils.MapMapToSlice(
|
||||||
fields.Options,
|
fields.Options,
|
||||||
func(name string, rawValue docvalues.Value) protocol.CompletionItem {
|
func(name string, rawValue docvalues.Value) protocol.CompletionItem {
|
||||||
doc := rawValue.(docvalues.DocumentationValue)
|
doc := rawValue.(docvalues.DocumentationValue)
|
||||||
|
|
||||||
insertText := name + " " + "${1:value}"
|
completion := &protocol.CompletionItem{
|
||||||
return protocol.CompletionItem{
|
Label: name,
|
||||||
Label: name,
|
Kind: &kind,
|
||||||
Kind: &kind,
|
Documentation: doc.Documentation,
|
||||||
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
|
), 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
|
package lsp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
sshdconfig "config-lsp/handlers/sshd_config"
|
sshdconfig "config-lsp/handlers/sshd_config"
|
||||||
"config-lsp/handlers/sshd_config/handlers"
|
"config-lsp/handlers/sshd_config/handlers"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -14,7 +15,6 @@ var containsSeparatorPattern = regexp.MustCompile(`\s+$`)
|
|||||||
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
|
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
|
||||||
line := params.Position.Line
|
line := params.Position.Line
|
||||||
cursor := params.Position.Character
|
cursor := params.Position.Character
|
||||||
_ = cursor
|
|
||||||
|
|
||||||
d := sshdconfig.DocumentParserMap[params.TextDocument.URI]
|
d := sshdconfig.DocumentParserMap[params.TextDocument.URI]
|
||||||
|
|
||||||
@ -24,11 +24,23 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa
|
|||||||
|
|
||||||
entry, matchBlock := d.Config.FindOption(line)
|
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
|
// Empty line
|
||||||
return handlers.GetRootCompletions(
|
return handlers.GetRootCompletions(
|
||||||
d,
|
d,
|
||||||
matchBlock,
|
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