mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
fix(server): Improve sshd_config
This commit is contained in:
parent
0c827b04cd
commit
e569516aae
@ -54,7 +54,6 @@ func Analyze(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzeValuesAreValid(ctx)
|
|
||||||
analyzeMatchBlocks(ctx)
|
analyzeMatchBlocks(ctx)
|
||||||
analyzeTokens(ctx)
|
analyzeTokens(ctx)
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
docvalues "config-lsp/doc-values"
|
docvalues "config-lsp/doc-values"
|
||||||
"config-lsp/handlers/sshd_config/ast"
|
"config-lsp/handlers/sshd_config/ast"
|
||||||
|
"config-lsp/handlers/sshd_config/diagnostics"
|
||||||
"config-lsp/handlers/sshd_config/fields"
|
"config-lsp/handlers/sshd_config/fields"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ func analyzeStructureIsValid(
|
|||||||
|
|
||||||
switch entry.(type) {
|
switch entry.(type) {
|
||||||
case *ast.SSHDOption:
|
case *ast.SSHDOption:
|
||||||
checkOption(ctx, entry.(*ast.SSHDOption), false)
|
checkOption(ctx, entry.(*ast.SSHDOption), nil)
|
||||||
case *ast.SSHDMatchBlock:
|
case *ast.SSHDMatchBlock:
|
||||||
matchBlock := entry.(*ast.SSHDMatchBlock)
|
matchBlock := entry.(*ast.SSHDMatchBlock)
|
||||||
checkMatchBlock(ctx, matchBlock)
|
checkMatchBlock(ctx, matchBlock)
|
||||||
@ -31,7 +32,7 @@ func analyzeStructureIsValid(
|
|||||||
func checkOption(
|
func checkOption(
|
||||||
ctx *analyzerContext,
|
ctx *analyzerContext,
|
||||||
option *ast.SSHDOption,
|
option *ast.SSHDOption,
|
||||||
isInMatchBlock bool,
|
matchBlock *ast.SSHDMatchBlock,
|
||||||
) {
|
) {
|
||||||
if option.Key == nil {
|
if option.Key == nil {
|
||||||
return
|
return
|
||||||
@ -44,16 +45,19 @@ func checkOption(
|
|||||||
docOption, found := fields.Options[key]
|
docOption, found := fields.Options[key]
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
ctx.diagnostics = append(ctx.diagnostics, diagnostics.GenerateUnknownOption(
|
||||||
Range: option.Key.ToLSPRange(),
|
option.Key.ToLSPRange(),
|
||||||
Message: fmt.Sprintf("Unknown option: %s", option.Key.Key),
|
option.Key.Value.Value,
|
||||||
Severity: &common.SeverityError,
|
))
|
||||||
})
|
ctx.document.Indexes.UnknownOptions[option.Start.Line] = ast.SSHDOptionInfo{
|
||||||
|
Option: option,
|
||||||
|
MatchBlock: matchBlock,
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, found := fields.MatchAllowedOptions[key]; !found && isInMatchBlock {
|
if _, found := fields.MatchAllowedOptions[key]; !found && matchBlock != nil {
|
||||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
Range: option.Key.ToLSPRange(),
|
Range: option.Key.ToLSPRange(),
|
||||||
Message: fmt.Sprintf("Option '%s' is not allowed inside Match blocks", option.Key.Key),
|
Message: fmt.Sprintf("Option '%s' is not allowed inside Match blocks", option.Key.Key),
|
||||||
@ -99,6 +103,6 @@ func checkMatchBlock(
|
|||||||
for it.Next() {
|
for it.Next() {
|
||||||
option := it.Value().(*ast.SSHDOption)
|
option := it.Value().(*ast.SSHDOption)
|
||||||
|
|
||||||
checkOption(ctx, option, true)
|
checkOption(ctx, option, matchBlock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ ThisOptionDoesNotExist okay
|
|||||||
diagnostics: make([]protocol.Diagnostic, 0),
|
diagnostics: make([]protocol.Diagnostic, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzeValuesAreValid(ctx)
|
analyzeStructureIsValid(ctx)
|
||||||
|
|
||||||
if !(len(ctx.diagnostics) == 1) {
|
if !(len(ctx.diagnostics) == 1) {
|
||||||
t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
|
t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
|
@ -1,39 +0,0 @@
|
|||||||
package analyzer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"config-lsp/handlers/sshd_config/diagnostics"
|
|
||||||
"config-lsp/handlers/sshd_config/fields"
|
|
||||||
)
|
|
||||||
|
|
||||||
func analyzeValuesAreValid(
|
|
||||||
ctx *analyzerContext,
|
|
||||||
) {
|
|
||||||
// Check if there are unknown options
|
|
||||||
for _, info := range ctx.document.Config.GetAllOptions() {
|
|
||||||
normalizedName := fields.CreateNormalizedName(info.Option.Key.Value.Value)
|
|
||||||
|
|
||||||
var isUnknown bool = true
|
|
||||||
|
|
||||||
// Check if the option is unknown
|
|
||||||
if info.MatchBlock == nil {
|
|
||||||
// All options are allowed
|
|
||||||
if _, found := fields.Options[normalizedName]; found {
|
|
||||||
isUnknown = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Only `MatchAllowedOptions` are allowed
|
|
||||||
if _, found := fields.MatchAllowedOptions[normalizedName]; found {
|
|
||||||
isUnknown = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isUnknown {
|
|
||||||
ctx.diagnostics = append(ctx.diagnostics, diagnostics.GenerateUnknownOption(
|
|
||||||
info.Option.Key.ToLSPRange(),
|
|
||||||
info.Option.Key.Value.Value,
|
|
||||||
))
|
|
||||||
|
|
||||||
ctx.document.Indexes.UnknownOptions[info.Option.Start.Line] = info
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
20
server/handlers/sshd_config/handlers/fetch-code-actions.go
Normal file
20
server/handlers/sshd_config/handlers/fetch-code-actions.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
sshdconfig "config-lsp/handlers/sshd_config"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FetchCodeActions(
|
||||||
|
d *sshdconfig.SSHDDocument,
|
||||||
|
params *protocol.CodeActionParams,
|
||||||
|
) []protocol.CodeAction {
|
||||||
|
if d.Indexes == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
actions := getKeywordTypoFixes(d, params)
|
||||||
|
|
||||||
|
return actions
|
||||||
|
}
|
110
server/handlers/sshd_config/handlers/fetch-code-actions_typos.go
Normal file
110
server/handlers/sshd_config/handlers/fetch-code-actions_typos.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
sshdconfig "config-lsp/handlers/sshd_config"
|
||||||
|
"config-lsp/handlers/sshd_config/diagnostics"
|
||||||
|
"config-lsp/handlers/sshd_config/fields"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hbollon/go-edlib"
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getKeywordTypoFixes(
|
||||||
|
d *sshdconfig.SSHDDocument,
|
||||||
|
params *protocol.CodeActionParams,
|
||||||
|
) []protocol.CodeAction {
|
||||||
|
if common.ServerOptions.NoTypoSuggestions {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
line := params.Range.Start.Line
|
||||||
|
|
||||||
|
if typoOption, found := d.Indexes.UnknownOptions[line]; found {
|
||||||
|
name := typoOption.Option.Key.Value.Value
|
||||||
|
|
||||||
|
suggestedOptions := findSimilarOptions(name, typoOption.MatchBlock != nil)
|
||||||
|
|
||||||
|
actions := make([]protocol.CodeAction, 0, len(suggestedOptions))
|
||||||
|
|
||||||
|
kind := protocol.CodeActionKindQuickFix
|
||||||
|
for index, normalizedOptionName := range suggestedOptions {
|
||||||
|
isPreferred := index == 0
|
||||||
|
optionName := fields.FieldsNameFormattedMap[normalizedOptionName]
|
||||||
|
|
||||||
|
actions = append(actions, protocol.CodeAction{
|
||||||
|
Title: fmt.Sprintf("Typo Fix: %s", optionName),
|
||||||
|
IsPreferred: &isPreferred,
|
||||||
|
Kind: &kind,
|
||||||
|
Diagnostics: []protocol.Diagnostic{
|
||||||
|
diagnostics.GenerateUnknownOption(
|
||||||
|
typoOption.Option.Key.ToLSPRange(),
|
||||||
|
typoOption.Option.Key.Value.Value,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Edit: &protocol.WorkspaceEdit{
|
||||||
|
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
||||||
|
params.TextDocument.URI: {
|
||||||
|
{
|
||||||
|
Range: typoOption.Option.Key.ToLSPRange(),
|
||||||
|
NewText: optionName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find options that are similar to the given option name.
|
||||||
|
// This is used to find typos & suggest the correct option name.
|
||||||
|
// Once an option is found that has a Damerau-Levenshtein distance of 1, it is immediately returned.
|
||||||
|
// If not, then the next 2 options of similarity 2, or 3 options of similarity 3 are returned.
|
||||||
|
// If no options with similarity <= 3 are found, then an empty slice is returned.
|
||||||
|
func findSimilarOptions(
|
||||||
|
optionName string,
|
||||||
|
restrictToMatchOptions bool,
|
||||||
|
) []fields.NormalizedOptionName {
|
||||||
|
normalizedOptionName := string(fields.CreateNormalizedName(optionName))
|
||||||
|
|
||||||
|
optionsPerSimilarity := map[uint8][]fields.NormalizedOptionName{
|
||||||
|
2: make([]fields.NormalizedOptionName, 0, 2),
|
||||||
|
3: make([]fields.NormalizedOptionName, 0, 3),
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range fields.Options {
|
||||||
|
if restrictToMatchOptions {
|
||||||
|
if _, found := fields.MatchAllowedOptions[name]; !found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizedName := string(name)
|
||||||
|
similarity := edlib.DamerauLevenshteinDistance(normalizedName, normalizedOptionName)
|
||||||
|
|
||||||
|
switch similarity {
|
||||||
|
case 1:
|
||||||
|
return []fields.NormalizedOptionName{name}
|
||||||
|
case 2:
|
||||||
|
optionsPerSimilarity[2] = append(optionsPerSimilarity[2], name)
|
||||||
|
|
||||||
|
if len(optionsPerSimilarity[2]) >= 2 {
|
||||||
|
return optionsPerSimilarity[2]
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
optionsPerSimilarity[3] = append(optionsPerSimilarity[3], name)
|
||||||
|
|
||||||
|
if len(optionsPerSimilarity[3]) >= 3 {
|
||||||
|
return optionsPerSimilarity[3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(optionsPerSimilarity[2], optionsPerSimilarity[3]...)
|
||||||
|
}
|
16
server/handlers/sshd_config/lsp/text-document-code-action.go
Normal file
16
server/handlers/sshd_config/lsp/text-document-code-action.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package lsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
sshdconfig "config-lsp/handlers/sshd_config"
|
||||||
|
"config-lsp/handlers/sshd_config/handlers"
|
||||||
|
|
||||||
|
"github.com/tliron/glsp"
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
|
||||||
|
d := sshdconfig.DocumentParserMap[params.TextDocument.URI]
|
||||||
|
actions := handlers.FetchCodeActions(d, params)
|
||||||
|
|
||||||
|
return actions, nil
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user