refactor(server): ssh_config improvements

Signed-off-by: Myzel394 <github.7a2op@simplelogin.co>
This commit is contained in:
Myzel394 2025-03-15 22:40:31 +01:00 committed by Myzel394
parent 0b2690910f
commit 1cfb9bbfba
No known key found for this signature in database
GPG Key ID: B603E877F73D4ABB
7 changed files with 132 additions and 102 deletions

View File

@ -54,6 +54,7 @@ func Analyze(
} }
} }
analyzeValuesAreValid(ctx)
analyzeTokens(ctx) analyzeTokens(ctx)
analyzeIgnoreUnknownHasNoUnnecessary(ctx) analyzeIgnoreUnknownHasNoUnnecessary(ctx)
analyzeDependents(ctx) analyzeDependents(ctx)

View File

@ -4,7 +4,6 @@ import (
"config-lsp/common" "config-lsp/common"
docvalues "config-lsp/doc-values" docvalues "config-lsp/doc-values"
"config-lsp/handlers/ssh_config/ast" "config-lsp/handlers/ssh_config/ast"
"config-lsp/handlers/ssh_config/diagnostics"
"config-lsp/handlers/ssh_config/fields" "config-lsp/handlers/ssh_config/fields"
"config-lsp/utils" "config-lsp/utils"
"fmt" "fmt"
@ -63,23 +62,9 @@ func checkOption(
if !optionFound { if !optionFound {
// Diagnostics will be handled by `values.go` // Diagnostics will be handled by `values.go`
if !ctx.document.Indexes.CanOptionBeIgnored(option, block) { return
ctx.diagnostics = append(
ctx.diagnostics,
diagnostics.GenerateUnknownOption(
option.Key.ToLSPRange(),
option.Key.Value.Value,
),
)
ctx.document.Indexes.UnknownOptions[option.Start.Line] = ast.AllOptionInfo{
Option: option,
Block: block,
}
} }
// Since we don't know the option, we can't verify the value
return
} else {
// Check for values that are not allowed in Host blocks // Check for values that are not allowed in Host blocks
if block != nil && block.GetBlockType() == ast.SSHBlockTypeHost && utils.KeyExists(fields.HostDisallowedOptions, option.Key.Key) { if block != nil && block.GetBlockType() == ast.SSHBlockTypeHost && utils.KeyExists(fields.HostDisallowedOptions, option.Key.Key) {
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
@ -88,7 +73,6 @@ func checkOption(
Severity: &common.SeverityError, Severity: &common.SeverityError,
}) })
} }
}
///// Check if the value is valid ///// Check if the value is valid
if option.OptionValue != nil { if option.OptionValue != nil {

View File

@ -112,79 +112,3 @@ Match
t.Fatalf("Expected 1 error, got %v", ctx.diagnostics) t.Fatalf("Expected 1 error, got %v", ctx.diagnostics)
} }
} }
func TestUnknownOptionExample(
t *testing.T,
) {
d := testutils_test.DocumentFromInput(t, `
ThisOptionDoesNotExist okay
`)
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
analyzeStructureIsValid(ctx)
if !(len(ctx.diagnostics) == 1) {
t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
}
if !(len(ctx.document.Indexes.UnknownOptions) == 1) {
t.Errorf("Expected 1 unknown option, got %v", len(ctx.document.Indexes.UnknownOptions))
}
if !(ctx.document.Indexes.UnknownOptions[0].Option.Key.Value.Value == "ThisOptionDoesNotExist") {
t.Errorf("Expected 'ThisOptionDoesNotExist', got %v", ctx.document.Indexes.UnknownOptions[0].Option.Key.Value.Value)
}
}
func TestUnknownOptionButIgnoredExample(
t *testing.T,
) {
d := testutils_test.DocumentFromInput(t, `
IgnoreUnknown ThisOptionDoesNotExist
ThisOptionDoesNotExist okay
`)
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
analyzeStructureIsValid(ctx)
if len(ctx.diagnostics) > 0 {
t.Fatalf("Expected no errors, but got %v", len(ctx.diagnostics))
}
if !(len(ctx.document.Indexes.UnknownOptions) == 0) {
t.Errorf("Expected 0 unknown options, got %v", len(ctx.document.Indexes.UnknownOptions))
}
}
func TestUnknownOptionIgnoredIsAfterDefinitionExample(
t *testing.T,
) {
d := testutils_test.DocumentFromInput(t, `
ThisOptionDoesNotExist okay
IgnoreUnknown ThisOptionDoesNotExist
`)
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
analyzeStructureIsValid(ctx)
if !(len(ctx.diagnostics) == 1) {
t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
}
if !(len(ctx.document.Indexes.UnknownOptions) == 1) {
t.Errorf("Expected 1 unknown option, got %v", len(ctx.document.Indexes.UnknownOptions))
}
if !(ctx.document.Indexes.UnknownOptions[0].Option.Key.Value.Value == "ThisOptionDoesNotExist") {
t.Errorf("Expected 'ThisOptionDoesNotExist', got %v", ctx.document.Indexes.UnknownOptions[0].Option.Key.Value.Value)
}
}

View File

@ -0,0 +1,36 @@
package analyzer
import (
"config-lsp/handlers/ssh_config/diagnostics"
"config-lsp/handlers/ssh_config/fields"
)
func analyzeValuesAreValid(
ctx *analyzerContext,
) {
// Check if there are unknown options
for _, info := range ctx.document.Config.GetAllOptions() {
option := info.Option
block := info.Block
_, found := fields.Options[option.Key.Key]
if !found {
if ctx.document.Indexes.CanOptionBeIgnored(option, block) {
// Skip
continue
}
ctx.diagnostics = append(
ctx.diagnostics,
diagnostics.GenerateUnknownOption(
option.Key.ToLSPRange(),
option.Key.Value.Value,
),
)
ctx.document.Indexes.UnknownOptions[info.Option.Start.Line] = info
continue
}
}
}

View File

@ -0,0 +1,84 @@
package analyzer
import (
testutils_test "config-lsp/handlers/ssh_config/test_utils"
"testing"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func TestUnknownOptionExample(
t *testing.T,
) {
d := testutils_test.DocumentFromInput(t, `
ThisOptionDoesNotExist okay
`)
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
analyzeValuesAreValid(ctx)
if !(len(ctx.diagnostics) == 1) {
t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
}
if !(len(ctx.document.Indexes.UnknownOptions) == 1) {
t.Errorf("Expected 1 unknown option, got %v", len(ctx.document.Indexes.UnknownOptions))
}
if !(ctx.document.Indexes.UnknownOptions[0].Option.Key.Value.Value == "ThisOptionDoesNotExist") {
t.Errorf("Expected 'ThisOptionDoesNotExist', got %v", ctx.document.Indexes.UnknownOptions[0].Option.Key.Value.Value)
}
}
func TestUnknownOptionButIgnoredExample(
t *testing.T,
) {
d := testutils_test.DocumentFromInput(t, `
IgnoreUnknown ThisOptionDoesNotExist
ThisOptionDoesNotExist okay
`)
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
analyzeValuesAreValid(ctx)
if len(ctx.diagnostics) > 0 {
t.Fatalf("Expected no errors, but got %v", len(ctx.diagnostics))
}
if !(len(ctx.document.Indexes.UnknownOptions) == 0) {
t.Errorf("Expected 0 unknown options, got %v", len(ctx.document.Indexes.UnknownOptions))
}
}
func TestUnknownOptionIgnoredIsAfterDefinitionExample(
t *testing.T,
) {
d := testutils_test.DocumentFromInput(t, `
ThisOptionDoesNotExist okay
IgnoreUnknown ThisOptionDoesNotExist
`)
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
analyzeValuesAreValid(ctx)
if !(len(ctx.diagnostics) == 1) {
t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
}
if !(len(ctx.document.Indexes.UnknownOptions) == 1) {
t.Errorf("Expected 1 unknown option, got %v", len(ctx.document.Indexes.UnknownOptions))
}
if !(ctx.document.Indexes.UnknownOptions[0].Option.Key.Value.Value == "ThisOptionDoesNotExist") {
t.Errorf("Expected 'ThisOptionDoesNotExist', got %v", ctx.document.Indexes.UnknownOptions[0].Option.Key.Value.Value)
}
}

View File

@ -33,7 +33,7 @@ func FormatDocument(
// it := d.Config.Options.Iterator() // it := d.Config.Options.Iterator()
// for it.Next() { // for it.Next() {
// line := it.Key().(uint32) // line := it.Key().(uint32)
// entry := it.Value().(ast.SSHEntry) // entry := it.Name().(ast.SSHEntry)
// //
// if !(line >= textRange.Start.Line && line <= textRange.End.Line) { // if !(line >= textRange.Start.Line && line <= textRange.End.Line) {
// continue // continue

View File

@ -20,6 +20,7 @@ func TextDocumentDidChange(
document := sshconfig.DocumentParserMap[params.TextDocument.URI] document := sshconfig.DocumentParserMap[params.TextDocument.URI]
document.Config.Clear() document.Config.Clear()
println("reparsing everything")
diagnostics := make([]protocol.Diagnostic, 0) diagnostics := make([]protocol.Diagnostic, 0)
errors := document.Config.Parse(content) errors := document.Config.Parse(content)