mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
fix(aliases): Improve aliases
This commit is contained in:
parent
5cbf5be8ad
commit
31faa9a96d
1
go.mod
1
go.mod
@ -10,6 +10,7 @@ require (
|
||||
require (
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||
github.com/k0kubun/pp v3.0.1+incompatible // indirect
|
||||
|
2
go.sum
2
go.sum
@ -2,6 +2,8 @@ github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYW
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
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=
|
||||
|
13
handlers/aliases/analyzer/analyzer.go
Normal file
13
handlers/aliases/analyzer/analyzer.go
Normal file
@ -0,0 +1,13 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"config-lsp/handlers/hosts"
|
||||
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
func Analyze(
|
||||
d *hosts.HostsDocument,
|
||||
) []protocol.Diagnostic {
|
||||
return nil
|
||||
}
|
49
handlers/aliases/analyzer/parser.go
Normal file
49
handlers/aliases/analyzer/parser.go
Normal file
@ -0,0 +1,49 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/handlers/aliases/ast"
|
||||
|
||||
ers "errors"
|
||||
)
|
||||
|
||||
func analyzeValuesAreValid(
|
||||
p ast.AliasesParser,
|
||||
) []common.LSPError {
|
||||
errors := make([]common.LSPError, 0)
|
||||
|
||||
it := p.Aliases.Iterator()
|
||||
|
||||
for it.Next() {
|
||||
entry := it.Value().(*ast.AliasEntry)
|
||||
|
||||
if entry.Key == nil {
|
||||
errors = append(errors, common.LSPError{
|
||||
Range: entry.Location,
|
||||
Err: ers.New("A name is required"),
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if entry.Separator == nil {
|
||||
errors = append(errors, common.LSPError{
|
||||
Range: entry.Location,
|
||||
Err: ers.New("The separator is required"),
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if entry.Values == nil || len(entry.Values.Values) == 0 {
|
||||
errors = append(errors, common.LSPError{
|
||||
Range: entry.Location,
|
||||
Err: ers.New("A value is required"),
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
package tree
|
||||
package ast
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"github.com/emirpasic/gods/maps/treemap"
|
||||
)
|
||||
|
||||
// Procedure
|
||||
@ -34,6 +35,6 @@ type AliasEntry struct {
|
||||
}
|
||||
|
||||
type AliasesParser struct {
|
||||
Aliases map[uint32]*AliasEntry
|
||||
Aliases *treemap.Map
|
||||
CommentLines map[uint32]struct{}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
package tree
|
||||
package ast
|
||||
|
@ -1,4 +1,4 @@
|
||||
package tree
|
||||
package ast
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
@ -23,16 +23,21 @@ func (s *aliasesParserListener) EnterEntry(ctx *parser.EntryContext) {
|
||||
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||
location.ChangeBothLines(s.aliasContext.line)
|
||||
|
||||
s.Parser.Aliases[location.Start.Line] = &AliasEntry{
|
||||
s.Parser.Aliases.Put(
|
||||
location.Start.Line,
|
||||
&AliasEntry{
|
||||
Location: location,
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (s *aliasesParserListener) EnterSeparator(ctx *parser.SeparatorContext) {
|
||||
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||
location.ChangeBothLines(s.aliasContext.line)
|
||||
|
||||
entry := s.Parser.Aliases[location.Start.Line]
|
||||
rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line)
|
||||
entry := rawEntry.(*AliasEntry)
|
||||
|
||||
entry.Separator = &location
|
||||
}
|
||||
|
||||
@ -40,7 +45,9 @@ func (s *aliasesParserListener) EnterKey(ctx *parser.KeyContext) {
|
||||
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||
location.ChangeBothLines(s.aliasContext.line)
|
||||
|
||||
entry := s.Parser.Aliases[location.Start.Line]
|
||||
rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line)
|
||||
entry := rawEntry.(*AliasEntry)
|
||||
|
||||
entry.Key = &AliasKey{
|
||||
Location: location,
|
||||
Value: ctx.GetText(),
|
||||
@ -51,7 +58,9 @@ func (s *aliasesParserListener) EnterValues(ctx *parser.ValuesContext) {
|
||||
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||
location.ChangeBothLines(s.aliasContext.line)
|
||||
|
||||
entry := s.Parser.Aliases[location.Start.Line]
|
||||
rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line)
|
||||
entry := rawEntry.(*AliasEntry)
|
||||
|
||||
entry.Values = &AliasValues{
|
||||
Location: location,
|
||||
Values: make([]AliasValueInterface, 0, 5),
|
||||
@ -71,7 +80,9 @@ func (s *aliasesParserListener) EnterUser(ctx *parser.UserContext) {
|
||||
},
|
||||
}
|
||||
|
||||
entry := s.Parser.Aliases[location.Start.Line]
|
||||
rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line)
|
||||
entry := rawEntry.(*AliasEntry)
|
||||
|
||||
entry.Values.Values = append(entry.Values.Values, user)
|
||||
}
|
||||
|
||||
@ -79,9 +90,12 @@ func (s *aliasesParserListener) EnterFile(ctx *parser.FileContext) {
|
||||
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||
location.ChangeBothLines(s.aliasContext.line)
|
||||
|
||||
rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line)
|
||||
entry := rawEntry.(*AliasEntry)
|
||||
|
||||
if s.aliasContext.currentIncludeIndex != nil {
|
||||
// This `file` is inside an `include`, so we need to set the path on the include
|
||||
values := s.Parser.Aliases[location.Start.Line].Values
|
||||
values := entry.Values
|
||||
rawValue := values.Values[*s.aliasContext.currentIncludeIndex]
|
||||
|
||||
// Set the path
|
||||
@ -107,7 +121,6 @@ func (s *aliasesParserListener) EnterFile(ctx *parser.FileContext) {
|
||||
Path: path(ctx.GetText()),
|
||||
}
|
||||
|
||||
entry := s.Parser.Aliases[location.Start.Line]
|
||||
entry.Values.Values = append(entry.Values.Values, file)
|
||||
}
|
||||
|
||||
@ -123,7 +136,9 @@ func (s *aliasesParserListener) EnterCommand(ctx *parser.CommandContext) {
|
||||
Command: ctx.GetText()[1:],
|
||||
}
|
||||
|
||||
entry := s.Parser.Aliases[location.Start.Line]
|
||||
rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line)
|
||||
entry := rawEntry.(*AliasEntry)
|
||||
|
||||
entry.Values.Values = append(entry.Values.Values, command)
|
||||
}
|
||||
|
||||
@ -138,7 +153,9 @@ func (s *aliasesParserListener) EnterInclude(ctx *parser.IncludeContext) {
|
||||
},
|
||||
}
|
||||
|
||||
entry := s.Parser.Aliases[location.Start.Line]
|
||||
rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line)
|
||||
entry := rawEntry.(*AliasEntry)
|
||||
|
||||
entry.Values.Values = append(entry.Values.Values, include)
|
||||
|
||||
index := uint32(len(entry.Values.Values) - 1)
|
||||
@ -156,7 +173,9 @@ func (s *aliasesParserListener) EnterEmail(ctx *parser.EmailContext) {
|
||||
},
|
||||
}
|
||||
|
||||
entry := s.Parser.Aliases[location.Start.Line]
|
||||
rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line)
|
||||
entry := rawEntry.(*AliasEntry)
|
||||
|
||||
entry.Values.Values = append(entry.Values.Values, email)
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
package tree
|
||||
package ast
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/handlers/aliases/parser"
|
||||
"config-lsp/utils"
|
||||
"github.com/antlr4-go/antlr/v4"
|
||||
"regexp"
|
||||
|
||||
"github.com/antlr4-go/antlr/v4"
|
||||
"github.com/emirpasic/gods/maps/treemap"
|
||||
gods "github.com/emirpasic/gods/utils"
|
||||
)
|
||||
|
||||
func NewAliasesParser() AliasesParser {
|
||||
@ -17,7 +20,7 @@ func NewAliasesParser() AliasesParser {
|
||||
|
||||
func (p *AliasesParser) Clear() {
|
||||
p.CommentLines = make(map[uint32]struct{})
|
||||
p.Aliases = make(map[uint32]*AliasEntry)
|
||||
p.Aliases = treemap.NewWith(gods.UInt32Comparator)
|
||||
}
|
||||
|
||||
var commentPattern = regexp.MustCompile(`^\s*#.*$`)
|
||||
|
@ -1,4 +1,4 @@
|
||||
package tree
|
||||
package ast
|
||||
|
||||
import (
|
||||
"config-lsp/utils"
|
||||
@ -19,15 +19,17 @@ postmaster: root
|
||||
t.Fatalf("Expected no errors, got %v", errors)
|
||||
}
|
||||
|
||||
if !(len(parser.Aliases) == 1) {
|
||||
t.Fatalf("Expected 1 alias, got %v", len(parser.Aliases))
|
||||
if !(parser.Aliases.Size() == 1) {
|
||||
t.Fatalf("Expected 1 alias, got %v", parser.Aliases.Size())
|
||||
}
|
||||
|
||||
if !(parser.Aliases[0].Key.Value == "postmaster") {
|
||||
t.Fatalf("Expected key to be 'postmaster', got %v", parser.Aliases[1].Key.Value)
|
||||
rawEntry, _ := parser.Aliases.Get(uint32(0))
|
||||
entry := rawEntry.(*AliasEntry)
|
||||
if !(entry.Key.Value == "postmaster") {
|
||||
t.Fatalf("Expected key to be 'postmaster', got %v", entry.Key.Value)
|
||||
}
|
||||
|
||||
userValue := parser.Aliases[0].Values.Values[0].(AliasValueUser)
|
||||
userValue := entry.Values.Values[0].(AliasValueUser)
|
||||
if !(userValue.Value == "root") {
|
||||
t.Fatalf("Expected value to be 'root', got %v", userValue.Value)
|
||||
}
|
||||
@ -51,19 +53,23 @@ michel: raiks@example.com
|
||||
t.Fatalf("Expected no errors, got %v", errors)
|
||||
}
|
||||
|
||||
if !(len(parser.Aliases) == 2) {
|
||||
t.Fatalf("Expected 2 aliases, got %v", len(parser.Aliases))
|
||||
if !(parser.Aliases.Size() == 2) {
|
||||
t.Fatalf("Expected 2 aliases, got %v", parser.Aliases.Size())
|
||||
}
|
||||
|
||||
if !(parser.Aliases[0].Key.Value == "heinz") {
|
||||
t.Fatalf("Expected key to be 'heinz', got %v", parser.Aliases[1].Key.Value)
|
||||
rawEntry, _ := parser.Aliases.Get(uint32(0))
|
||||
entry := rawEntry.(*AliasEntry)
|
||||
if !(entry.Key.Value == "heinz") {
|
||||
t.Fatalf("Expected key to be 'heinz', got %v", entry.Key.Value)
|
||||
}
|
||||
|
||||
if !(parser.Aliases[1].Key.Value == "michel") {
|
||||
t.Fatalf("Expected key to be 'michel', got %v", parser.Aliases[1].Key.Value)
|
||||
rawEntry, _ = parser.Aliases.Get(uint32(1))
|
||||
entry = rawEntry.(*AliasEntry)
|
||||
if !(entry.Key.Value == "michel") {
|
||||
t.Fatalf("Expected key to be 'michel', got %v", entry.Key.Value)
|
||||
}
|
||||
|
||||
emailValue := parser.Aliases[1].Values.Values[0].(AliasValueEmail)
|
||||
emailValue := entry.Values.Values[0].(AliasValueEmail)
|
||||
|
||||
if !(emailValue.Value == "raiks@example.com") {
|
||||
t.Fatalf("Expected value to be 'raiks@example.com', got %v", emailValue.Value)
|
||||
@ -83,15 +89,17 @@ luke: :include:/etc/other_aliases
|
||||
t.Fatalf("Expected no errors, got %v", errors)
|
||||
}
|
||||
|
||||
if !(len(parser.Aliases) == 1) {
|
||||
t.Fatalf("Expected 1 alias, got %v", len(parser.Aliases))
|
||||
if !(parser.Aliases.Size() == 1) {
|
||||
t.Fatalf("Expected 1 alias, got %v", parser.Aliases.Size())
|
||||
}
|
||||
|
||||
if !(parser.Aliases[0].Key.Value == "luke") {
|
||||
t.Fatalf("Expected key to be 'luke', got %v", parser.Aliases[1].Key.Value)
|
||||
rawEntry, _ := parser.Aliases.Get(uint32(0))
|
||||
entry := rawEntry.(*AliasEntry)
|
||||
if !(entry.Key.Value == "luke") {
|
||||
t.Fatalf("Expected key to be 'luke', got %v", entry.Key.Value)
|
||||
}
|
||||
|
||||
includeValue := parser.Aliases[0].Values.Values[0].(AliasValueInclude)
|
||||
includeValue := entry.Values.Values[0].(AliasValueInclude)
|
||||
|
||||
if !(includeValue.Path.Path == "/etc/other_aliases") {
|
||||
t.Fatalf("Expected path to be '/etc/other_aliases', got %v", includeValue.Path.Path)
|
||||
@ -105,3 +113,39 @@ luke: :include:/etc/other_aliases
|
||||
t.Fatalf("Expected path location to be 15-33, got %v-%v", includeValue.Path.Location.Start.Character, includeValue.Path.Location.End.Character)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidWithEmptyValuesWorks(
|
||||
t *testing.T,
|
||||
) {
|
||||
input := utils.Dedent(`
|
||||
luke:
|
||||
`)
|
||||
parser := NewAliasesParser()
|
||||
errors := parser.Parse(input)
|
||||
|
||||
if len(errors) == 0 {
|
||||
t.Fatalf("Expected 1 error, got %v", errors)
|
||||
}
|
||||
|
||||
if !(errors[0].Range.Start.Character == 5 && errors[0].Range.End.Character == 5) {
|
||||
t.Fatalf("Expected error to be at 6, got %v", errors[0].Range.Start.Character)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidWithEmptyKeyWorks(
|
||||
t *testing.T,
|
||||
) {
|
||||
input := utils.Dedent(`
|
||||
: root
|
||||
`)
|
||||
parser := NewAliasesParser()
|
||||
errors := parser.Parse(input)
|
||||
|
||||
if len(errors) == 0 {
|
||||
t.Fatalf("Expected 1 error, got %v", errors)
|
||||
}
|
||||
|
||||
if !(errors[0].Range.Start.Character == 0 && errors[0].Range.End.Character == 0) {
|
||||
t.Fatalf("Expected error to be at 0, got %v", errors[0].Range.Start.Character)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package tree
|
||||
package ast
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
|
43
handlers/aliases/indexes/indexes.go
Normal file
43
handlers/aliases/indexes/indexes.go
Normal file
@ -0,0 +1,43 @@
|
||||
package indexes
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/handlers/aliases/ast"
|
||||
"config-lsp/handlers/aliases/shared"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AliasesIndexes struct {
|
||||
Keys map[string]*ast.AliasKey
|
||||
}
|
||||
|
||||
func CreateIndexes(parser ast.AliasesParser) (AliasesIndexes, []common.LSPError) {
|
||||
errors := make([]common.LSPError, 0)
|
||||
indexes := &AliasesIndexes{
|
||||
Keys: make(map[string]*ast.AliasKey),
|
||||
}
|
||||
|
||||
it := parser.Aliases.Iterator()
|
||||
|
||||
for it.Next() {
|
||||
entry := it.Value().(*ast.AliasEntry)
|
||||
|
||||
normalizedAlias := strings.ToLower(entry.Key.Value)
|
||||
|
||||
if existingEntry, found := indexes.Keys[normalizedAlias]; found {
|
||||
errors = append(errors, common.LSPError{
|
||||
Range: entry.Location,
|
||||
Err: shared.DuplicateKeyEntry{
|
||||
AlreadyFoundAt: existingEntry.Location.Start.Line,
|
||||
Key: entry.Key.Value,
|
||||
},
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
indexes.Keys[normalizedAlias] = entry.Key
|
||||
}
|
||||
|
||||
return *indexes, errors
|
||||
}
|
15
handlers/aliases/shared.go
Normal file
15
handlers/aliases/shared.go
Normal file
@ -0,0 +1,15 @@
|
||||
package aliases
|
||||
|
||||
import (
|
||||
"config-lsp/handlers/aliases/ast"
|
||||
"config-lsp/handlers/aliases/indexes"
|
||||
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
type HostsDocument struct {
|
||||
Parser *ast.AliasesParser
|
||||
Indexes *indexes.AliasesIndexes
|
||||
}
|
||||
|
||||
var DocumentParserMap = map[protocol.DocumentUri]*HostsDocument{}
|
12
handlers/aliases/shared/errors.go
Normal file
12
handlers/aliases/shared/errors.go
Normal file
@ -0,0 +1,12 @@
|
||||
package shared
|
||||
|
||||
import "fmt"
|
||||
|
||||
type DuplicateKeyEntry struct {
|
||||
AlreadyFoundAt uint32
|
||||
Key string
|
||||
}
|
||||
|
||||
func (d DuplicateKeyEntry) Error() string {
|
||||
return fmt.Sprintf("Alias '%s' already defined on line %d", d.Key, d.AlreadyFoundAt+1)
|
||||
}
|
@ -13,6 +13,8 @@ var sampleInvalidOptionsExample = `
|
||||
LABEL=test /mnt/test btrfs subvol=backup,fat=32 0 0
|
||||
`
|
||||
|
||||
// TODO: Improve `entries`, sometimes the indexes seem
|
||||
// to be wrong. Use a treemap instead of a map.
|
||||
func TestValidBasicExample(t *testing.T) {
|
||||
// Arrange
|
||||
parser := FstabParser{}
|
||||
|
@ -4,7 +4,9 @@ import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/handlers/hosts"
|
||||
"config-lsp/handlers/hosts/shared"
|
||||
"config-lsp/utils"
|
||||
"net"
|
||||
"slices"
|
||||
)
|
||||
|
||||
func ipToString(ip net.IPAddr) string {
|
||||
@ -17,7 +19,15 @@ func analyzeDoubleIPs(d *hosts.HostsDocument) []common.LSPError {
|
||||
|
||||
d.Indexes.DoubleIPs = make(map[uint32]shared.DuplicateIPDeclaration)
|
||||
|
||||
for lineNumber, entry := range d.Parser.Tree.Entries {
|
||||
// TODO: `range` does not seem to properly
|
||||
// iterate in a sorted way.
|
||||
// Instead, use a treemap
|
||||
lines := utils.KeysOfMap(d.Parser.Tree.Entries)
|
||||
slices.Sort(lines)
|
||||
|
||||
for _, lineNumber := range lines {
|
||||
entry := d.Parser.Tree.Entries[lineNumber]
|
||||
|
||||
if entry.IPAddress != nil {
|
||||
key := ipToString(entry.IPAddress.Value)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user