chore(hosts): Use TreeMap for entries

This commit is contained in:
Myzel394 2024-09-08 21:59:35 +02:00
parent b27081aa8e
commit 65572886f2
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
11 changed files with 121 additions and 63 deletions

View File

@ -3,10 +3,9 @@ package analyzer
import ( import (
"config-lsp/common" "config-lsp/common"
"config-lsp/handlers/hosts" "config-lsp/handlers/hosts"
"config-lsp/handlers/hosts/ast"
"config-lsp/handlers/hosts/shared" "config-lsp/handlers/hosts/shared"
"config-lsp/utils"
"net" "net"
"slices"
) )
func ipToString(ip net.IPAddr) string { func ipToString(ip net.IPAddr) string {
@ -19,14 +18,11 @@ func analyzeDoubleIPs(d *hosts.HostsDocument) []common.LSPError {
d.Indexes.DoubleIPs = make(map[uint32]shared.DuplicateIPDeclaration) d.Indexes.DoubleIPs = make(map[uint32]shared.DuplicateIPDeclaration)
// TODO: `range` does not seem to properly it := d.Parser.Tree.Entries.Iterator()
// iterate in a sorted way.
// Instead, use a treemap
lines := utils.KeysOfMap(d.Parser.Tree.Entries)
slices.Sort(lines)
for _, lineNumber := range lines { for it.Next() {
entry := d.Parser.Tree.Entries[lineNumber] lineNumber := it.Key().(uint32)
entry := it.Value().(*ast.HostsEntry)
if entry.IPAddress != nil { if entry.IPAddress != nil {
key := ipToString(entry.IPAddress.Value) key := ipToString(entry.IPAddress.Value)

View File

@ -21,48 +21,50 @@ func TestValidSimpleExampleWorks(
t.Fatalf("Expected no errors, but got %v", errors) t.Fatalf("Expected no errors, but got %v", errors)
} }
if !(len(parser.Tree.Entries) == 1) { if !(parser.Tree.Entries.Size() == 1) {
t.Errorf("Expected 1 entry, but got %v", len(parser.Tree.Entries)) t.Errorf("Expected 1 entry, but got %v", parser.Tree.Entries.Size())
} }
if parser.Tree.Entries[0].IPAddress == nil { rawEntry, found := parser.Tree.Entries.Get(uint32(0))
t.Errorf("Expected IP address to be present, but got nil") if !found {
t.Fatalf("Expected IP address to be present, but got nil")
} }
if !(parser.Tree.Entries[0].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) { entry := rawEntry.(*ast.HostsEntry)
t.Errorf("Expected IP address to be 1.2.3.4, but got %v", parser.Tree.Entries[0].IPAddress.Value) if !(entry.IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) {
t.Errorf("Expected IP address to be 1.2.3.4, but got %v", entry.IPAddress.Value)
} }
if !(parser.Tree.Entries[0].Hostname.Value == "hello.com") { if !(entry.Hostname.Value == "hello.com") {
t.Errorf("Expected hostname to be hello.com, but got %v", parser.Tree.Entries[0].Hostname.Value) t.Errorf("Expected hostname to be hello.com, but got %v", entry.Hostname.Value)
} }
if !(parser.Tree.Entries[0].Aliases == nil) { if !(entry.Aliases == nil) {
t.Errorf("Expected no aliases, but got %v", parser.Tree.Entries[0].Aliases) t.Errorf("Expected no aliases, but got %v", entry.Aliases)
} }
if !(parser.Tree.Entries[0].Location.Start.Line == 0) { if !(entry.Location.Start.Line == 0) {
t.Errorf("Expected line to be 1, but got %v", parser.Tree.Entries[0].Location.Start.Line) t.Errorf("Expected line to be 1, but got %v", entry.Location.Start.Line)
} }
if !(parser.Tree.Entries[0].Location.Start.Character == 0) { if !(entry.Location.Start.Character == 0) {
t.Errorf("Expected start to be 0, but got %v", parser.Tree.Entries[0].Location.Start) t.Errorf("Expected start to be 0, but got %v", entry.Location.Start)
} }
if !(parser.Tree.Entries[0].Location.End.Character == 17) { if !(entry.Location.End.Character == 17) {
t.Errorf("Expected end to be 17, but got %v", parser.Tree.Entries[0].Location.End.Character) t.Errorf("Expected end to be 17, but got %v", entry.Location.End.Character)
} }
if !(parser.Tree.Entries[0].IPAddress.Location.Start.Line == 0) { if !(entry.IPAddress.Location.Start.Line == 0) {
t.Errorf("Expected IP address line to be 1, but got %v", parser.Tree.Entries[0].IPAddress.Location.Start.Line) t.Errorf("Expected IP address line to be 1, but got %v", entry.IPAddress.Location.Start.Line)
} }
if !(parser.Tree.Entries[0].IPAddress.Location.Start.Character == 0) { if !(entry.IPAddress.Location.Start.Character == 0) {
t.Errorf("Expected IP address start to be 0, but got %v", parser.Tree.Entries[0].IPAddress.Location.Start.Character) t.Errorf("Expected IP address start to be 0, but got %v", entry.IPAddress.Location.Start.Character)
} }
if !(parser.Tree.Entries[0].IPAddress.Location.End.Character == 7) { if !(entry.IPAddress.Location.End.Character == 7) {
t.Errorf("Expected IP address end to be 7, but got %v", parser.Tree.Entries[0].IPAddress.Location.End.Character) t.Errorf("Expected IP address end to be 7, but got %v", entry.IPAddress.Location.End.Character)
} }
if !(len(parser.CommentLines) == 0) { if !(len(parser.CommentLines) == 0) {
@ -88,16 +90,18 @@ func TestValidComplexExampleWorks(
t.Fatalf("Expected no errors, but got %v", errors) t.Fatalf("Expected no errors, but got %v", errors)
} }
if !(len(parser.Tree.Entries) == 3) { if !(parser.Tree.Entries.Size() == 3) {
t.Errorf("Expected 3 entries, but got %v", len(parser.Tree.Entries)) t.Fatalf("Expected 3 entries, but got %v", parser.Tree.Entries.Size())
} }
if parser.Tree.Entries[2].IPAddress == nil { rawEntry, _ := parser.Tree.Entries.Get(uint32(2))
entry := rawEntry.(*ast.HostsEntry)
if entry.IPAddress == nil {
t.Errorf("Expected IP address to be present, but got nil") t.Errorf("Expected IP address to be present, but got nil")
} }
if !(parser.Tree.Entries[2].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) { if !(entry.IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) {
t.Errorf("Expected IP address to be 1.2.3.4, but got %v", parser.Tree.Entries[2].IPAddress.Value) t.Errorf("Expected IP address to be 1.2.3.4, but got %v", entry.IPAddress.Value)
} }
if !(len(parser.CommentLines) == 1) { if !(len(parser.CommentLines) == 1) {
@ -123,23 +127,25 @@ func TestInvalidExampleWorks(
t.Fatalf("Expected errors, but got none") t.Fatalf("Expected errors, but got none")
} }
if !(len(parser.Tree.Entries) == 1) { if !(parser.Tree.Entries.Size() == 1) {
t.Errorf("Expected 1 entries, but got %v", len(parser.Tree.Entries)) t.Errorf("Expected 1 entries, but got %v", parser.Tree.Entries.Size())
} }
if !(len(parser.CommentLines) == 0) { if !(len(parser.CommentLines) == 0) {
t.Errorf("Expected no comment lines, but got %v", len(parser.CommentLines)) t.Errorf("Expected no comment lines, but got %v", len(parser.CommentLines))
} }
if !(parser.Tree.Entries[0].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) { rawEntry, _ := parser.Tree.Entries.Get(uint32(0))
t.Errorf("Expected IP address to be nil, but got %v", parser.Tree.Entries[0].IPAddress) entry := rawEntry.(*ast.HostsEntry)
if !(entry.IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) {
t.Errorf("Expected IP address to be nil, but got %v", entry.IPAddress)
} }
if !(parser.Tree.Entries[0].Hostname == nil) { if !(entry.Hostname == nil) {
t.Errorf("Expected hostname to be nil, but got %v", parser.Tree.Entries[0].Hostname) t.Errorf("Expected hostname to be nil, but got %v", entry.Hostname)
} }
if !(parser.Tree.Entries[0].Aliases == nil) { if !(entry.Aliases == nil) {
t.Errorf("Expected aliases to be nil, but got %v", parser.Tree.Entries[0].Aliases) t.Errorf("Expected aliases to be nil, but got %v", entry.Aliases)
} }
} }

View File

@ -38,7 +38,12 @@ func createResolverFromParser(p ast.HostsParser) (indexes.Resolver, []common.LSP
Entries: make(map[string]indexes.ResolverEntry), Entries: make(map[string]indexes.ResolverEntry),
} }
for lineNumber, entry := range p.Tree.Entries { it := p.Tree.Entries.Iterator()
for it.Next() {
lineNumber := it.Key().(uint32)
entry := it.Value().(*ast.HostsEntry)
if entry.IPAddress != nil && entry.Hostname != nil { if entry.IPAddress != nil && entry.Hostname != nil {
hostNames := append( hostNames := append(
[]hostnameEntry{ []hostnameEntry{

View File

@ -14,7 +14,12 @@ func analyzeEntriesSetCorrectly(
) []common.LSPError { ) []common.LSPError {
err := make([]common.LSPError, 0) err := make([]common.LSPError, 0)
for lineNumber, entry := range parser.Tree.Entries { it := parser.Tree.Entries.Iterator()
for it.Next() {
lineNumber := it.Key().(uint32)
entry := it.Value().(*ast.HostsEntry)
if entry.IPAddress == nil { if entry.IPAddress == nil {
err = append(err, common.LSPError{ err = append(err, common.LSPError{
Range: common.CreateFullLineRange(lineNumber), Range: common.CreateFullLineRange(lineNumber),
@ -40,7 +45,11 @@ func analyzeEntriesAreValid(
) []common.LSPError { ) []common.LSPError {
err := make([]common.LSPError, 0) err := make([]common.LSPError, 0)
for _, entry := range parser.Tree.Entries { it := parser.Tree.Entries.Iterator()
for it.Next() {
entry := it.Value().(*ast.HostsEntry)
err = append( err = append(
err, err,
utils.Map( utils.Map(

View File

@ -4,6 +4,8 @@ import (
"config-lsp/common" "config-lsp/common"
"fmt" "fmt"
"net" "net"
"github.com/emirpasic/gods/maps/treemap"
) )
type HostsParser struct { type HostsParser struct {
@ -13,7 +15,7 @@ type HostsParser struct {
type HostsTree struct { type HostsTree struct {
// [line]entry // [line]entry
Entries map[uint32]*HostsEntry Entries *treemap.Map
} }
type HostsEntry struct { type HostsEntry struct {

View File

@ -31,9 +31,9 @@ func (s *hostsParserListener) EnterEntry(ctx *parser2.EntryContext) {
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
location.ChangeBothLines(s.hostsContext.line) location.ChangeBothLines(s.hostsContext.line)
s.Parser.Tree.Entries[location.Start.Line] = &HostsEntry{ s.Parser.Tree.Entries.Put(location.Start.Line, &HostsEntry{
Location: location, Location: location,
} })
} }
var containsPortPattern = regexp.MustCompile(`:[0-9]+$`) var containsPortPattern = regexp.MustCompile(`:[0-9]+$`)
@ -69,7 +69,8 @@ func (s *hostsParserListener) EnterIpAddress(ctx *parser2.IpAddressContext) {
}) })
} }
entry := s.Parser.Tree.Entries[location.Start.Line] rawEntry, _ := s.Parser.Tree.Entries.Get(location.Start.Line)
entry := rawEntry.(*HostsEntry)
entry.IPAddress = &HostsIPAddress{ entry.IPAddress = &HostsIPAddress{
Location: location, Location: location,
@ -81,21 +82,21 @@ func (s *hostsParserListener) EnterHostname(ctx *parser2.HostnameContext) {
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
location.ChangeBothLines(s.hostsContext.line) location.ChangeBothLines(s.hostsContext.line)
entry := s.Parser.Tree.Entries[location.Start.Line] rawEntry, _ := s.Parser.Tree.Entries.Get(location.Start.Line)
entry := rawEntry.(*HostsEntry)
entry.Hostname = &HostsHostname{ entry.Hostname = &HostsHostname{
Location: location, Location: location,
Value: ctx.GetText(), Value: ctx.GetText(),
} }
s.Parser.Tree.Entries[location.Start.Line] = entry
} }
func (s *hostsParserListener) EnterAliases(ctx *parser2.AliasesContext) { func (s *hostsParserListener) EnterAliases(ctx *parser2.AliasesContext) {
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
location.ChangeBothLines(s.hostsContext.line) location.ChangeBothLines(s.hostsContext.line)
entry := s.Parser.Tree.Entries[location.Start.Line] rawEntry, _ := s.Parser.Tree.Entries.Get(location.Start.Line)
entry := rawEntry.(*HostsEntry)
aliases := make([]*HostsHostname, 0) aliases := make([]*HostsHostname, 0)
@ -106,7 +107,8 @@ func (s *hostsParserListener) EnterAlias(ctx *parser2.AliasContext) {
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
location.ChangeBothLines(s.hostsContext.line) location.ChangeBothLines(s.hostsContext.line)
entry := s.Parser.Tree.Entries[location.Start.Line] rawEntry, _ := s.Parser.Tree.Entries.Get(location.Start.Line)
entry := rawEntry.(*HostsEntry)
alias := HostsHostname{ alias := HostsHostname{
Location: location, Location: location,

View File

@ -7,11 +7,14 @@ import (
"regexp" "regexp"
"github.com/antlr4-go/antlr/v4" "github.com/antlr4-go/antlr/v4"
"github.com/emirpasic/gods/maps/treemap"
gods "github.com/emirpasic/gods/utils"
) )
func (p *HostsParser) Clear() { func (p *HostsParser) Clear() {
p.Tree = HostsTree{ p.Tree = HostsTree{
Entries: make(map[uint32]*HostsEntry), Entries: treemap.NewWith(gods.UInt32Comparator),
} }
p.CommentLines = make(map[uint32]struct{}) p.CommentLines = make(map[uint32]struct{})
} }
@ -44,8 +47,8 @@ func (p *HostsParser) parseStatement(
antlrParser.LineStatement(), antlrParser.LineStatement(),
) )
errors = append(errors, errorListener.Errors...)
errors = append(errors, listener.Errors...) errors = append(errors, listener.Errors...)
errors = append(errors, errorListener.Errors...)
return errors return errors
} }

View File

@ -18,3 +18,34 @@ func TestParserInvalidWithPort(
t.Fatalf("Expected 1 error, but got %v", errors) t.Fatalf("Expected 1 error, but got %v", errors)
} }
} }
func TestParserValidComplexExample(
t *testing.T,
) {
input := utils.Dedent(`
1.2.3.4 hello.com alias.com example.com
1.2.3.5 hello1.com alias1.com example1.com
192.168.1.1 goodbye.com
`)
parser := NewHostsParser()
errors := parser.Parse(input)
if len(errors) != 0 {
t.Fatalf("Expected no errors, but got %v", errors)
}
if !(parser.Tree.Entries.Size() == 3) {
t.Fatalf("Expected 3 entries, but got %v", parser.Tree.Entries.Size())
}
rawEntry, _ := parser.Tree.Entries.Get(uint32(0))
entry := rawEntry.(*HostsEntry)
if !(entry.IPAddress.Value.String() == "1.2.3.4") {
t.Errorf("Expected IP address to be 1.2.3.4, but got %v", entry.IPAddress.Value)
}
if !(entry.Hostname.Value == "hello.com") {
t.Errorf("Expected hostname to be hello.com, but got %v", entry.Hostname.Value)
}
}

View File

@ -9,5 +9,5 @@ var IPAddressField = docvalues.IPAddressValue{
var HostnameField = docvalues.DocumentationValue{ var HostnameField = docvalues.DocumentationValue{
Documentation: `Host names may contain only alphanumeric characters, minus signs ("-"), and periods ("."). They must begin with an alphabetic character and end with an alphanumeric character. Optional aliases provide for name changes, alternate spellings, shorter hostnames, or generic hostnames (for example, localhost).`, Documentation: `Host names may contain only alphanumeric characters, minus signs ("-"), and periods ("."). They must begin with an alphabetic character and end with an alphanumeric character. Optional aliases provide for name changes, alternate spellings, shorter hostnames, or generic hostnames (for example, localhost).`,
Value: docvalues.DomainValue(), Value: docvalues.StringValue{},
} }

View File

@ -36,14 +36,17 @@ func CodeActionInlineAliasesArgsFromArguments(arguments map[string]any) CodeActi
} }
func (args CodeActionInlineAliasesArgs) RunCommand(hostsParser ast.HostsParser) (*protocol.ApplyWorkspaceEditParams, error) { func (args CodeActionInlineAliasesArgs) RunCommand(hostsParser ast.HostsParser) (*protocol.ApplyWorkspaceEditParams, error) {
fromEntry := hostsParser.Tree.Entries[args.FromLine] rawFromEntry, foundFromEntry := hostsParser.Tree.Entries.Get(args.FromLine)
toEntry := hostsParser.Tree.Entries[args.ToLine] rawToEntry, foundToEntry := hostsParser.Tree.Entries.Get(args.ToLine)
if fromEntry == nil || toEntry == nil { if !foundFromEntry || !foundToEntry {
// Weird // Weird
return nil, nil return nil, nil
} }
fromEntry := rawFromEntry.(*ast.HostsEntry)
toEntry := rawToEntry.(*ast.HostsEntry)
var insertCharacter uint32 var insertCharacter uint32
if toEntry.Aliases != nil { if toEntry.Aliases != nil {

View File

@ -25,13 +25,14 @@ func TextDocumentHover(
return nil, nil return nil, nil
} }
entry, found := document.Parser.Tree.Entries[line] rawEntry, found := document.Parser.Tree.Entries.Get(line)
if !found { if !found {
// Empty line // Empty line
return nil, nil return nil, nil
} }
entry := rawEntry.(*ast.HostsEntry)
target := handlers.GetHoverTargetInEntry(*entry, character) target := handlers.GetHoverTargetInEntry(*entry, character)
var hostname *ast.HostsHostname var hostname *ast.HostsHostname