mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
feat: Add tag import analyzer; Improvements
This commit is contained in:
parent
8517a722d1
commit
05335d42d0
@ -42,6 +42,8 @@ func Analyze(
|
|||||||
analyzeMatchBlocks(ctx)
|
analyzeMatchBlocks(ctx)
|
||||||
analyzeHostBlock(ctx)
|
analyzeHostBlock(ctx)
|
||||||
analyzeBlocks(ctx)
|
analyzeBlocks(ctx)
|
||||||
|
analyzeTagOptions(ctx)
|
||||||
|
analyzeTagImports(ctx)
|
||||||
|
|
||||||
return ctx.diagnostics
|
return ctx.diagnostics
|
||||||
}
|
}
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
package analyzer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"config-lsp/common"
|
|
||||||
"config-lsp/handlers/ssh_config/fields"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
|
||||||
)
|
|
||||||
|
|
||||||
var tagOption = fields.CreateNormalizedName("Tag")
|
|
||||||
|
|
||||||
func analyzeTags(
|
|
||||||
ctx *analyzerContext,
|
|
||||||
) {
|
|
||||||
// Check if the specified tags actually exist
|
|
||||||
for _, options := range ctx.document.Indexes.AllOptionsPerName[tagOption] {
|
|
||||||
for _, option := range options {
|
|
||||||
if _, found := ctx.document.Indexes.Tags[option.OptionValue.Value.Value]; !found {
|
|
||||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
|
||||||
Range: option.OptionValue.ToLSPRange(),
|
|
||||||
Message: fmt.Sprintf("Unknown tag: %s", option.OptionValue.Value.Value),
|
|
||||||
Severity: &common.SeverityError,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
33
server/handlers/ssh_config/analyzer/tag_imports.go
Normal file
33
server/handlers/ssh_config/analyzer/tag_imports.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package analyzer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func analyzeTagImports(
|
||||||
|
ctx *analyzerContext,
|
||||||
|
) {
|
||||||
|
for name, info := range ctx.document.Indexes.Tags {
|
||||||
|
if _, found := ctx.document.Indexes.TagImports[name]; !found {
|
||||||
|
var diagnosticRange protocol.Range
|
||||||
|
|
||||||
|
if len(info.Block.MatchValue.Entries) == 1 {
|
||||||
|
diagnosticRange = info.Block.MatchOption.ToLSPRange()
|
||||||
|
} else {
|
||||||
|
diagnosticRange = info.EntryValue.ToLSPRange()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
|
Range: diagnosticRange,
|
||||||
|
Message: fmt.Sprintf("Tag %s is not used", name),
|
||||||
|
Severity: &common.SeverityWarning,
|
||||||
|
Tags: []protocol.DiagnosticTag{
|
||||||
|
protocol.DiagnosticTagUnnecessary,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
server/handlers/ssh_config/analyzer/tag_imports_test.go
Normal file
49
server/handlers/ssh_config/analyzer/tag_imports_test.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package analyzer
|
||||||
|
|
||||||
|
import (
|
||||||
|
testutils_test "config-lsp/handlers/ssh_config/test_utils"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUsedTagImportsExample(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
d := testutils_test.DocumentFromInput(t, `
|
||||||
|
Host test.com
|
||||||
|
Tag auth
|
||||||
|
|
||||||
|
Match tagged auth
|
||||||
|
User root
|
||||||
|
`)
|
||||||
|
ctx := &analyzerContext{
|
||||||
|
document: d,
|
||||||
|
diagnostics: make([]protocol.Diagnostic, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
analyzeTagImports(ctx)
|
||||||
|
|
||||||
|
if len(ctx.diagnostics) > 0 {
|
||||||
|
t.Errorf("Expected no errors, got %v", len(ctx.diagnostics))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnusedTagImportsExample(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
d := testutils_test.DocumentFromInput(t, `
|
||||||
|
Match tagged auth
|
||||||
|
User root
|
||||||
|
`)
|
||||||
|
ctx := &analyzerContext{
|
||||||
|
document: d,
|
||||||
|
diagnostics: make([]protocol.Diagnostic, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
analyzeTagImports(ctx)
|
||||||
|
|
||||||
|
if !(len(ctx.diagnostics) == 1) {
|
||||||
|
t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
|
||||||
|
}
|
||||||
|
}
|
32
server/handlers/ssh_config/analyzer/tag_options.go
Normal file
32
server/handlers/ssh_config/analyzer/tag_options.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package analyzer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
"config-lsp/handlers/ssh_config/fields"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tagOption = fields.CreateNormalizedName("Tag")
|
||||||
|
|
||||||
|
func analyzeTagOptions(
|
||||||
|
ctx *analyzerContext,
|
||||||
|
) {
|
||||||
|
// Check if the specified tags actually exist
|
||||||
|
for _, options := range ctx.document.Indexes.AllOptionsPerName[tagOption] {
|
||||||
|
for _, option := range options {
|
||||||
|
tag, found := ctx.document.Indexes.Tags[option.OptionValue.Value.Value]
|
||||||
|
|
||||||
|
if found && tag.Block.Start.Line > option.Start.Line {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
|
Range: option.OptionValue.ToLSPRange(),
|
||||||
|
Message: fmt.Sprintf("Unknown tag: %s", option.OptionValue.Value.Value),
|
||||||
|
Severity: &common.SeverityError,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
server/handlers/ssh_config/analyzer/tag_options_test.go
Normal file
52
server/handlers/ssh_config/analyzer/tag_options_test.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package analyzer
|
||||||
|
|
||||||
|
import (
|
||||||
|
testutils_test "config-lsp/handlers/ssh_config/test_utils"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidTagExample(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
d := testutils_test.DocumentFromInput(t, `
|
||||||
|
Host test.com
|
||||||
|
Tag auth
|
||||||
|
|
||||||
|
Match tagged auth
|
||||||
|
User root
|
||||||
|
`)
|
||||||
|
ctx := &analyzerContext{
|
||||||
|
document: d,
|
||||||
|
diagnostics: make([]protocol.Diagnostic, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
analyzeTagOptions(ctx)
|
||||||
|
|
||||||
|
if len(ctx.diagnostics) > 0 {
|
||||||
|
t.Errorf("Expected no errors, got %v", len(ctx.diagnostics))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTagBlockBeforeExample(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
d := testutils_test.DocumentFromInput(t, `
|
||||||
|
Match tagged auth
|
||||||
|
User root
|
||||||
|
|
||||||
|
Host test.com
|
||||||
|
Tag auth
|
||||||
|
`)
|
||||||
|
ctx := &analyzerContext{
|
||||||
|
document: d,
|
||||||
|
diagnostics: make([]protocol.Diagnostic, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
analyzeTagOptions(ctx)
|
||||||
|
|
||||||
|
if !(len(ctx.diagnostics) == 1) {
|
||||||
|
t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
|
||||||
|
}
|
||||||
|
}
|
@ -65,6 +65,7 @@ func GetOptionCompletions(
|
|||||||
d *sshconfig.SSHDocument,
|
d *sshconfig.SSHDocument,
|
||||||
entry *ast.SSHOption,
|
entry *ast.SSHOption,
|
||||||
block ast.SSHBlock,
|
block ast.SSHBlock,
|
||||||
|
line uint32,
|
||||||
cursor common.CursorPosition,
|
cursor common.CursorPosition,
|
||||||
) ([]protocol.CompletionItem, error) {
|
) ([]protocol.CompletionItem, error) {
|
||||||
option, found := fields.Options[entry.Key.Key]
|
option, found := fields.Options[entry.Key.Key]
|
||||||
@ -84,6 +85,7 @@ func GetOptionCompletions(
|
|||||||
if entry.Key.Key == tagOption {
|
if entry.Key.Key == tagOption {
|
||||||
return getTagCompletions(
|
return getTagCompletions(
|
||||||
d,
|
d,
|
||||||
|
line,
|
||||||
cursor,
|
cursor,
|
||||||
entry,
|
entry,
|
||||||
)
|
)
|
||||||
@ -94,13 +96,13 @@ func GetOptionCompletions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Hello wo|rld
|
// Hello wo|rld
|
||||||
line := entry.OptionValue.Value.Raw
|
lineValue := entry.OptionValue.Value.Raw
|
||||||
// NEW: docvalues index
|
// NEW: docvalues index
|
||||||
return option.DeprecatedFetchCompletions(
|
return option.DeprecatedFetchCompletions(
|
||||||
line,
|
lineValue,
|
||||||
common.DeprecatedImprovedCursorToIndex(
|
common.DeprecatedImprovedCursorToIndex(
|
||||||
cursor,
|
cursor,
|
||||||
line,
|
lineValue,
|
||||||
entry.OptionValue.Start.Character,
|
entry.OptionValue.Start.Character,
|
||||||
),
|
),
|
||||||
), nil
|
), nil
|
||||||
|
@ -5,8 +5,6 @@ import (
|
|||||||
"config-lsp/common/formatting"
|
"config-lsp/common/formatting"
|
||||||
sshconfig "config-lsp/handlers/ssh_config"
|
sshconfig "config-lsp/handlers/ssh_config"
|
||||||
"config-lsp/handlers/ssh_config/ast"
|
"config-lsp/handlers/ssh_config/ast"
|
||||||
"config-lsp/handlers/ssh_config/indexes"
|
|
||||||
"config-lsp/utils"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
@ -14,24 +12,30 @@ import (
|
|||||||
|
|
||||||
func getTagCompletions(
|
func getTagCompletions(
|
||||||
d *sshconfig.SSHDocument,
|
d *sshconfig.SSHDocument,
|
||||||
|
line uint32,
|
||||||
cursor common.CursorPosition,
|
cursor common.CursorPosition,
|
||||||
entry *ast.SSHOption,
|
entry *ast.SSHOption,
|
||||||
) ([]protocol.CompletionItem, error) {
|
) ([]protocol.CompletionItem, error) {
|
||||||
return utils.MapMapToSlice(
|
completions := make([]protocol.CompletionItem, 0)
|
||||||
d.Indexes.Tags,
|
|
||||||
func(name string, info indexes.SSHIndexTagInfo) protocol.CompletionItem {
|
for name, info := range d.Indexes.Tags {
|
||||||
kind := protocol.CompletionItemKindModule
|
if info.Block.Start.Line < line {
|
||||||
text := renderMatchBlock(info.Block)
|
continue
|
||||||
return protocol.CompletionItem{
|
}
|
||||||
Label: name,
|
|
||||||
Kind: &kind,
|
kind := protocol.CompletionItemKindModule
|
||||||
Documentation: protocol.MarkupContent{
|
text := renderMatchBlock(info.Block)
|
||||||
Kind: protocol.MarkupKindMarkdown,
|
completions = append(completions, protocol.CompletionItem{
|
||||||
Value: fmt.Sprintf("```sshconfig\n%s\n```", text),
|
Label: name,
|
||||||
},
|
Kind: &kind,
|
||||||
}
|
Documentation: protocol.MarkupContent{
|
||||||
},
|
Kind: protocol.MarkupKindMarkdown,
|
||||||
), nil
|
Value: fmt.Sprintf("```sshconfig\n%s\n```", text),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return completions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderMatchBlock(
|
func renderMatchBlock(
|
||||||
|
@ -42,6 +42,7 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa
|
|||||||
d,
|
d,
|
||||||
option,
|
option,
|
||||||
block,
|
block,
|
||||||
|
line,
|
||||||
cursor,
|
cursor,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user