From 65572886f2fa709416c21a06b2fe9b0532d89546 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 8 Sep 2024 21:59:35 +0200 Subject: [PATCH] chore(hosts): Use TreeMap for entries --- handlers/hosts/analyzer/double_ips.go | 14 ++-- handlers/hosts/analyzer/handler_test.go | 76 ++++++++++--------- .../analyzer/{resolve.go => resolver.go} | 7 +- handlers/hosts/analyzer/values.go | 13 +++- handlers/hosts/ast/hosts.go | 4 +- handlers/hosts/ast/listener.go | 18 +++-- handlers/hosts/ast/parser.go | 7 +- handlers/hosts/ast/parser_test.go | 31 ++++++++ handlers/hosts/fields/documentation-fields.go | 2 +- handlers/hosts/handlers/code-actions.go | 9 ++- handlers/hosts/lsp/text-document-hover.go | 3 +- 11 files changed, 121 insertions(+), 63 deletions(-) rename handlers/hosts/analyzer/{resolve.go => resolver.go} (93%) diff --git a/handlers/hosts/analyzer/double_ips.go b/handlers/hosts/analyzer/double_ips.go index 4fcd2f4..8ad1bbf 100644 --- a/handlers/hosts/analyzer/double_ips.go +++ b/handlers/hosts/analyzer/double_ips.go @@ -3,10 +3,9 @@ package analyzer import ( "config-lsp/common" "config-lsp/handlers/hosts" + "config-lsp/handlers/hosts/ast" "config-lsp/handlers/hosts/shared" - "config-lsp/utils" "net" - "slices" ) 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) - // 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) + it := d.Parser.Tree.Entries.Iterator() - for _, lineNumber := range lines { - entry := d.Parser.Tree.Entries[lineNumber] + for it.Next() { + lineNumber := it.Key().(uint32) + entry := it.Value().(*ast.HostsEntry) if entry.IPAddress != nil { key := ipToString(entry.IPAddress.Value) diff --git a/handlers/hosts/analyzer/handler_test.go b/handlers/hosts/analyzer/handler_test.go index ce274c6..b7182a2 100644 --- a/handlers/hosts/analyzer/handler_test.go +++ b/handlers/hosts/analyzer/handler_test.go @@ -21,48 +21,50 @@ func TestValidSimpleExampleWorks( t.Fatalf("Expected no errors, but got %v", errors) } - if !(len(parser.Tree.Entries) == 1) { - t.Errorf("Expected 1 entry, but got %v", len(parser.Tree.Entries)) + if !(parser.Tree.Entries.Size() == 1) { + t.Errorf("Expected 1 entry, but got %v", parser.Tree.Entries.Size()) } - if parser.Tree.Entries[0].IPAddress == nil { - t.Errorf("Expected IP address to be present, but got nil") + rawEntry, found := parser.Tree.Entries.Get(uint32(0)) + 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()) { - t.Errorf("Expected IP address to be 1.2.3.4, but got %v", parser.Tree.Entries[0].IPAddress.Value) + entry := rawEntry.(*ast.HostsEntry) + 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") { - t.Errorf("Expected hostname to be hello.com, but got %v", parser.Tree.Entries[0].Hostname.Value) + if !(entry.Hostname.Value == "hello.com") { + t.Errorf("Expected hostname to be hello.com, but got %v", entry.Hostname.Value) } - if !(parser.Tree.Entries[0].Aliases == nil) { - t.Errorf("Expected no aliases, but got %v", parser.Tree.Entries[0].Aliases) + if !(entry.Aliases == nil) { + t.Errorf("Expected no aliases, but got %v", entry.Aliases) } - if !(parser.Tree.Entries[0].Location.Start.Line == 0) { - t.Errorf("Expected line to be 1, but got %v", parser.Tree.Entries[0].Location.Start.Line) + if !(entry.Location.Start.Line == 0) { + t.Errorf("Expected line to be 1, but got %v", entry.Location.Start.Line) } - if !(parser.Tree.Entries[0].Location.Start.Character == 0) { - t.Errorf("Expected start to be 0, but got %v", parser.Tree.Entries[0].Location.Start) + if !(entry.Location.Start.Character == 0) { + t.Errorf("Expected start to be 0, but got %v", entry.Location.Start) } - if !(parser.Tree.Entries[0].Location.End.Character == 17) { - t.Errorf("Expected end to be 17, but got %v", parser.Tree.Entries[0].Location.End.Character) + if !(entry.Location.End.Character == 17) { + t.Errorf("Expected end to be 17, but got %v", entry.Location.End.Character) } - if !(parser.Tree.Entries[0].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) + if !(entry.IPAddress.Location.Start.Line == 0) { + 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) { - t.Errorf("Expected IP address start to be 0, but got %v", parser.Tree.Entries[0].IPAddress.Location.Start.Character) + if !(entry.IPAddress.Location.Start.Character == 0) { + 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) { - t.Errorf("Expected IP address end to be 7, but got %v", parser.Tree.Entries[0].IPAddress.Location.End.Character) + if !(entry.IPAddress.Location.End.Character == 7) { + t.Errorf("Expected IP address end to be 7, but got %v", entry.IPAddress.Location.End.Character) } if !(len(parser.CommentLines) == 0) { @@ -88,16 +90,18 @@ func TestValidComplexExampleWorks( t.Fatalf("Expected no errors, but got %v", errors) } - if !(len(parser.Tree.Entries) == 3) { - t.Errorf("Expected 3 entries, but got %v", len(parser.Tree.Entries)) + if !(parser.Tree.Entries.Size() == 3) { + 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") } - if !(parser.Tree.Entries[2].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) + 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 !(len(parser.CommentLines) == 1) { @@ -123,23 +127,25 @@ func TestInvalidExampleWorks( t.Fatalf("Expected errors, but got none") } - if !(len(parser.Tree.Entries) == 1) { - t.Errorf("Expected 1 entries, but got %v", len(parser.Tree.Entries)) + if !(parser.Tree.Entries.Size() == 1) { + t.Errorf("Expected 1 entries, but got %v", parser.Tree.Entries.Size()) } if !(len(parser.CommentLines) == 0) { 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()) { - t.Errorf("Expected IP address to be nil, but got %v", parser.Tree.Entries[0].IPAddress) + rawEntry, _ := parser.Tree.Entries.Get(uint32(0)) + 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) { - t.Errorf("Expected hostname to be nil, but got %v", parser.Tree.Entries[0].Hostname) + if !(entry.Hostname == nil) { + t.Errorf("Expected hostname to be nil, but got %v", entry.Hostname) } - if !(parser.Tree.Entries[0].Aliases == nil) { - t.Errorf("Expected aliases to be nil, but got %v", parser.Tree.Entries[0].Aliases) + if !(entry.Aliases == nil) { + t.Errorf("Expected aliases to be nil, but got %v", entry.Aliases) } } diff --git a/handlers/hosts/analyzer/resolve.go b/handlers/hosts/analyzer/resolver.go similarity index 93% rename from handlers/hosts/analyzer/resolve.go rename to handlers/hosts/analyzer/resolver.go index 846af98..d59b412 100644 --- a/handlers/hosts/analyzer/resolve.go +++ b/handlers/hosts/analyzer/resolver.go @@ -38,7 +38,12 @@ func createResolverFromParser(p ast.HostsParser) (indexes.Resolver, []common.LSP 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 { hostNames := append( []hostnameEntry{ diff --git a/handlers/hosts/analyzer/values.go b/handlers/hosts/analyzer/values.go index e6da88e..d1c4e0a 100644 --- a/handlers/hosts/analyzer/values.go +++ b/handlers/hosts/analyzer/values.go @@ -14,7 +14,12 @@ func analyzeEntriesSetCorrectly( ) []common.LSPError { 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 { err = append(err, common.LSPError{ Range: common.CreateFullLineRange(lineNumber), @@ -40,7 +45,11 @@ func analyzeEntriesAreValid( ) []common.LSPError { 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, utils.Map( diff --git a/handlers/hosts/ast/hosts.go b/handlers/hosts/ast/hosts.go index 5f28219..99b8fa0 100644 --- a/handlers/hosts/ast/hosts.go +++ b/handlers/hosts/ast/hosts.go @@ -4,6 +4,8 @@ import ( "config-lsp/common" "fmt" "net" + + "github.com/emirpasic/gods/maps/treemap" ) type HostsParser struct { @@ -13,7 +15,7 @@ type HostsParser struct { type HostsTree struct { // [line]entry - Entries map[uint32]*HostsEntry + Entries *treemap.Map } type HostsEntry struct { diff --git a/handlers/hosts/ast/listener.go b/handlers/hosts/ast/listener.go index 11d9083..6bcf3f1 100644 --- a/handlers/hosts/ast/listener.go +++ b/handlers/hosts/ast/listener.go @@ -31,9 +31,9 @@ func (s *hostsParserListener) EnterEntry(ctx *parser2.EntryContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.hostsContext.line) - s.Parser.Tree.Entries[location.Start.Line] = &HostsEntry{ + s.Parser.Tree.Entries.Put(location.Start.Line, &HostsEntry{ Location: location, - } + }) } 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{ Location: location, @@ -81,21 +82,21 @@ func (s *hostsParserListener) EnterHostname(ctx *parser2.HostnameContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) 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{ Location: location, Value: ctx.GetText(), } - - s.Parser.Tree.Entries[location.Start.Line] = entry } func (s *hostsParserListener) EnterAliases(ctx *parser2.AliasesContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) 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) @@ -106,7 +107,8 @@ func (s *hostsParserListener) EnterAlias(ctx *parser2.AliasContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) 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{ Location: location, diff --git a/handlers/hosts/ast/parser.go b/handlers/hosts/ast/parser.go index 42dd6c6..99b760c 100644 --- a/handlers/hosts/ast/parser.go +++ b/handlers/hosts/ast/parser.go @@ -7,11 +7,14 @@ import ( "regexp" "github.com/antlr4-go/antlr/v4" + "github.com/emirpasic/gods/maps/treemap" + + gods "github.com/emirpasic/gods/utils" ) func (p *HostsParser) Clear() { p.Tree = HostsTree{ - Entries: make(map[uint32]*HostsEntry), + Entries: treemap.NewWith(gods.UInt32Comparator), } p.CommentLines = make(map[uint32]struct{}) } @@ -44,8 +47,8 @@ func (p *HostsParser) parseStatement( antlrParser.LineStatement(), ) - errors = append(errors, errorListener.Errors...) errors = append(errors, listener.Errors...) + errors = append(errors, errorListener.Errors...) return errors } diff --git a/handlers/hosts/ast/parser_test.go b/handlers/hosts/ast/parser_test.go index e390a96..c62b075 100644 --- a/handlers/hosts/ast/parser_test.go +++ b/handlers/hosts/ast/parser_test.go @@ -18,3 +18,34 @@ func TestParserInvalidWithPort( 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) + } +} diff --git a/handlers/hosts/fields/documentation-fields.go b/handlers/hosts/fields/documentation-fields.go index 83837a8..9d15411 100644 --- a/handlers/hosts/fields/documentation-fields.go +++ b/handlers/hosts/fields/documentation-fields.go @@ -9,5 +9,5 @@ var IPAddressField = docvalues.IPAddressValue{ 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).`, - Value: docvalues.DomainValue(), + Value: docvalues.StringValue{}, } diff --git a/handlers/hosts/handlers/code-actions.go b/handlers/hosts/handlers/code-actions.go index 7618226..4d91dcf 100644 --- a/handlers/hosts/handlers/code-actions.go +++ b/handlers/hosts/handlers/code-actions.go @@ -36,14 +36,17 @@ func CodeActionInlineAliasesArgsFromArguments(arguments map[string]any) CodeActi } func (args CodeActionInlineAliasesArgs) RunCommand(hostsParser ast.HostsParser) (*protocol.ApplyWorkspaceEditParams, error) { - fromEntry := hostsParser.Tree.Entries[args.FromLine] - toEntry := hostsParser.Tree.Entries[args.ToLine] + rawFromEntry, foundFromEntry := hostsParser.Tree.Entries.Get(args.FromLine) + rawToEntry, foundToEntry := hostsParser.Tree.Entries.Get(args.ToLine) - if fromEntry == nil || toEntry == nil { + if !foundFromEntry || !foundToEntry { // Weird return nil, nil } + fromEntry := rawFromEntry.(*ast.HostsEntry) + toEntry := rawToEntry.(*ast.HostsEntry) + var insertCharacter uint32 if toEntry.Aliases != nil { diff --git a/handlers/hosts/lsp/text-document-hover.go b/handlers/hosts/lsp/text-document-hover.go index 1c6484e..086daa5 100644 --- a/handlers/hosts/lsp/text-document-hover.go +++ b/handlers/hosts/lsp/text-document-hover.go @@ -25,13 +25,14 @@ func TextDocumentHover( return nil, nil } - entry, found := document.Parser.Tree.Entries[line] + rawEntry, found := document.Parser.Tree.Entries.Get(line) if !found { // Empty line return nil, nil } + entry := rawEntry.(*ast.HostsEntry) target := handlers.GetHoverTargetInEntry(*entry, character) var hostname *ast.HostsHostname