feat(server): Add typo fix suggestion to ssh_config

This commit is contained in:
Myzel394 2025-02-16 14:43:14 +01:00
parent 67c7f7f4b7
commit 026e0349a1
No known key found for this signature in database
GPG Key ID: ED20A1D1D423AF3F
7 changed files with 123 additions and 9 deletions

View File

@ -27,6 +27,9 @@ type ServerOptionsType struct {
var ServerOptions = new(ServerOptionsType)
func InitServerOptions() {
ServerOptions.NoUndetectableErrors = false
ServerOptions.NoTypoSuggestions = false
if slices.Contains(os.Args, "--no-undetectable-errors") {
Log.Info("config-lsp will not return errors for undetectable files")
ServerOptions.NoUndetectableErrors = true

View File

@ -15,6 +15,7 @@ require (
require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/hbollon/go-edlib v1.6.0 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect

View File

@ -9,6 +9,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hbollon/go-edlib v1.6.0 h1:ga7AwwVIvP8mHm9GsPueC0d71cfRU/52hmPJ7Tprv4E=
github.com/hbollon/go-edlib v1.6.0/go.mod h1:wnt6o6EIVEzUfgbUZY7BerzQ2uvzp354qmS2xaLkrhM=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=

View File

@ -29,8 +29,6 @@ func analyzeValuesAreValid(
),
)
ctx.document.Indexes.UnknownOptions[info.Option.Start.Line] = info
continue
}
}
}

View File

@ -0,0 +1,103 @@
package handlers
import (
"config-lsp/common"
sshconfig "config-lsp/handlers/ssh_config"
"config-lsp/handlers/ssh_config/diagnostics"
"config-lsp/handlers/ssh_config/fields"
"fmt"
"github.com/hbollon/go-edlib"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func getKeywordTypoFixes(
d *sshconfig.SSHDocument,
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)
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,
) []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 {
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]...)
}

View File

@ -12,12 +12,22 @@ func FetchCodeActions(
d *sshconfig.SSHDocument,
params *protocol.CodeActionParams,
) []protocol.CodeAction {
line := params.Range.Start.Line
if d.Indexes == nil {
return nil
}
actions := getAddToUnknownCodeAction(d, params)
actions = append(actions, getKeywordTypoFixes(d, params)...)
return actions
}
func getAddToUnknownCodeAction(
d *sshconfig.SSHDocument,
params *protocol.CodeActionParams,
) []protocol.CodeAction {
line := params.Range.Start.Line
if unknownOption, found := d.Indexes.UnknownOptions[line]; found {
var blockLine *uint32
@ -39,7 +49,7 @@ func FetchCodeActions(
},
}
kind := protocol.CodeActionKindQuickFix
codeAction := &protocol.CodeAction{
codeAction := protocol.CodeAction{
Title: fmt.Sprintf("Add %s to unknown options", unknownOption.Option.Key.Key),
Command: &command,
Kind: &kind,
@ -52,7 +62,7 @@ func FetchCodeActions(
}
return []protocol.CodeAction{
*codeAction,
codeAction,
}
}

View File

@ -61,9 +61,6 @@ func initFile(
uri protocol.DocumentUri,
advertisedLanguage string,
) (*shared.SupportedLanguage, error) {
println("Initializing the file")
println(advertisedLanguage)
println(uri)
language, err := utils.DetectLanguage(content, advertisedLanguage, uri)
if err != nil {