From 36950fe27135c009d52a2fd2d1e388df3591c3e3 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 23 Feb 2025 21:48:08 +0100 Subject: [PATCH] refactor(server): Improve Wireguard AST, analyzer and indexes Signed-off-by: Myzel394 --- server/handlers/wireguard/ast/parser.go | 14 ++- server/handlers/wireguard/ast/wireguard.go | 1 + .../wireguard/ast/wireguard_fields.go | 10 ++ server/handlers/wireguard/lsp/shared.go | 8 -- .../wireguard/lsp/text-document-did-change.go | 14 +-- .../wireguard/lsp/text-document-did-close.go | 4 +- .../wireguard/lsp/text-document-did-open.go | 18 +++- server/handlers/wireguard/parser/wg-parser.go | 94 +++---------------- .../wireguard/parser/wg-parser_test.go | 10 +- .../handlers/wireguard/parser/wg-property.go | 82 ++-------------- .../handlers/wireguard/parser/wg-section.go | 61 ++---------- 11 files changed, 82 insertions(+), 234 deletions(-) delete mode 100644 server/handlers/wireguard/lsp/shared.go diff --git a/server/handlers/wireguard/ast/parser.go b/server/handlers/wireguard/ast/parser.go index eb6c153..8d2c066 100644 --- a/server/handlers/wireguard/ast/parser.go +++ b/server/handlers/wireguard/ast/parser.go @@ -22,7 +22,7 @@ func (c *WGConfig) Clear() { var commentPattern = regexp.MustCompile(`^\s*([;#])`) var emptyPattern = regexp.MustCompile(`^\s*$`) -var headerPattern = regexp.MustCompile(`^\s*\[(\w+)]?`) +var headerPattern = regexp.MustCompile(`^\s*\[(\w+)?]?`) var linePattern = regexp.MustCompile(`^\s*(?P.+?)\s*(?P=)\s*(?P\S.*?)?\s*(?:[;#].*)?\s*$`) func (c *WGConfig) Parse(input string) []common.LSPError { @@ -177,11 +177,13 @@ func (c *WGConfig) Parse(input string) []common.LSPError { // Construct value var value *WGPropertyValue + propertyEnd := uint32(len(line)) if indexes[6] != -1 && indexes[7] != -1 { // value exists valueStart := uint32(indexes[6]) valueEnd := uint32(indexes[7]) + propertyEnd = valueEnd value = &WGPropertyValue{ LocationRange: common.LocationRange{ @@ -200,6 +202,16 @@ func (c *WGConfig) Parse(input string) []common.LSPError { // And lastly, add the property currentSection.Properties[lineNumber] = &WGProperty{ + LocationRange: common.LocationRange{ + Start: common.Location{ + Line: lineNumber, + Character: keyStart, + }, + End: common.Location{ + Line: lineNumber, + Character: propertyEnd, + }, + }, Key: key, Separator: &separator, Value: value, diff --git a/server/handlers/wireguard/ast/wireguard.go b/server/handlers/wireguard/ast/wireguard.go index c648b07..9f1274c 100644 --- a/server/handlers/wireguard/ast/wireguard.go +++ b/server/handlers/wireguard/ast/wireguard.go @@ -19,6 +19,7 @@ type WGPropertySeparator struct { } type WGProperty struct { + common.LocationRange Key WGPropertyKey Separator *WGPropertySeparator Value *WGPropertyValue diff --git a/server/handlers/wireguard/ast/wireguard_fields.go b/server/handlers/wireguard/ast/wireguard_fields.go index 46460c3..3904220 100644 --- a/server/handlers/wireguard/ast/wireguard_fields.go +++ b/server/handlers/wireguard/ast/wireguard_fields.go @@ -37,3 +37,13 @@ func (c *WGConfig) FindPropertyByLine(line uint32) *WGProperty { return section.Properties[line] } + +func (s *WGSection) FindFirstPropertyByName(name string) *WGProperty { + for _, property := range s.Properties { + if property.Key.Name == name { + return property + } + } + + return nil +} diff --git a/server/handlers/wireguard/lsp/shared.go b/server/handlers/wireguard/lsp/shared.go deleted file mode 100644 index 377f850..0000000 --- a/server/handlers/wireguard/lsp/shared.go +++ /dev/null @@ -1,8 +0,0 @@ -package lsp - -import ( - "config-lsp/handlers/wireguard/parser" - protocol "github.com/tliron/glsp/protocol_3_16" -) - -var documentParserMap = map[protocol.DocumentUri]*parser.WireguardParser{} diff --git a/server/handlers/wireguard/lsp/text-document-did-change.go b/server/handlers/wireguard/lsp/text-document-did-change.go index c5ec400..1b58bbd 100644 --- a/server/handlers/wireguard/lsp/text-document-did-change.go +++ b/server/handlers/wireguard/lsp/text-document-did-change.go @@ -2,8 +2,10 @@ package lsp import ( "config-lsp/common" - "config-lsp/handlers/wireguard/handlers" + "config-lsp/handlers/wireguard" + "config-lsp/handlers/wireguard/analyzer" "config-lsp/utils" + "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) @@ -15,22 +17,22 @@ func TextDocumentDidChange( content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text common.ClearDiagnostics(context, params.TextDocument.URI) - p := documentParserMap[params.TextDocument.URI] - p.Clear() + document := wireguard.DocumentParserMap[params.TextDocument.URI] + document.Config.Clear() diagnostics := make([]protocol.Diagnostic, 0) - errors := p.ParseFromString(content) + errors := document.Config.Parse(content) if len(errors) > 0 { diagnostics = append(diagnostics, utils.Map( errors, - func(err common.ParseError) protocol.Diagnostic { + func(err common.LSPError) protocol.Diagnostic { return err.ToDiagnostic() }, )...) } - diagnostics = append(diagnostics, handlers.Analyze(*p)...) + diagnostics = append(diagnostics, analyzer.Analyze(document)...) if len(diagnostics) > 0 { common.SendDiagnostics(context, params.TextDocument.URI, diagnostics) diff --git a/server/handlers/wireguard/lsp/text-document-did-close.go b/server/handlers/wireguard/lsp/text-document-did-close.go index 3961658..318d28d 100644 --- a/server/handlers/wireguard/lsp/text-document-did-close.go +++ b/server/handlers/wireguard/lsp/text-document-did-close.go @@ -1,12 +1,14 @@ package lsp import ( + "config-lsp/handlers/wireguard" + "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) func TextDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDocumentParams) error { - delete(documentParserMap, params.TextDocument.URI) + delete(wireguard.DocumentParserMap, params.TextDocument.URI) return nil } diff --git a/server/handlers/wireguard/lsp/text-document-did-open.go b/server/handlers/wireguard/lsp/text-document-did-open.go index fc9e0b2..49193b3 100644 --- a/server/handlers/wireguard/lsp/text-document-did-open.go +++ b/server/handlers/wireguard/lsp/text-document-did-open.go @@ -2,8 +2,12 @@ package lsp import ( "config-lsp/common" - "config-lsp/handlers/wireguard/parser" + "config-lsp/handlers/wireguard" + "config-lsp/handlers/wireguard/analyzer" + "config-lsp/handlers/wireguard/ast" + "config-lsp/handlers/wireguard/indexes" "config-lsp/utils" + "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) @@ -14,17 +18,21 @@ func TextDocumentDidOpen( ) error { common.ClearDiagnostics(context, params.TextDocument.URI) - p := parser.CreateWireguardParser() - documentParserMap[params.TextDocument.URI] = &p + document := &wireguard.WGDocument{ + Config: ast.NewWGConfig(), + Indexes: &indexes.WGIndexes{}, + } + wireguard.DocumentParserMap[params.TextDocument.URI] = document - errors := p.ParseFromString(params.TextDocument.Text) + errors := document.Config.Parse(params.TextDocument.Text) diagnostics := utils.Map( errors, - func(err common.ParseError) protocol.Diagnostic { + func(err common.LSPError) protocol.Diagnostic { return err.ToDiagnostic() }, ) + diagnostics = append(diagnostics, analyzer.Analyze(document)...) if len(diagnostics) > 0 { common.SendDiagnostics(context, params.TextDocument.URI, diagnostics) diff --git a/server/handlers/wireguard/parser/wg-parser.go b/server/handlers/wireguard/parser/wg-parser.go index a85d262..688dc5c 100644 --- a/server/handlers/wireguard/parser/wg-parser.go +++ b/server/handlers/wireguard/parser/wg-parser.go @@ -2,6 +2,7 @@ package parser import ( "config-lsp/common" + "config-lsp/handlers/wireguard/ast" "regexp" "slices" "strings" @@ -11,33 +12,7 @@ var commentPattern = regexp.MustCompile(`^\s*(;|#)`) var emptyLinePattern = regexp.MustCompile(`^\s*$`) var headerPattern = regexp.MustCompile(`^\s*\[`) -type CharacterLocation struct { - Start uint32 - End uint32 -} - -type wireguardLineIndex struct { - Type LineType - BelongingSection *WireguardSection -} - -type WireguardParser struct { - // : if nil then does not belong to a section - Sections []*WireguardSection - // Used to identify where not to show diagnostics - commentLines map[uint32]struct{} - - // Indexes - linesIndexes map[uint32]wireguardLineIndex -} - -func (p *WireguardParser) Clear() { - p.Sections = []*WireguardSection{} - p.commentLines = map[uint32]struct{}{} - p.linesIndexes = map[uint32]wireguardLineIndex{} -} - -func (p *WireguardParser) ParseFromString(input string) []common.ParseError { +func (p *ast.WGConfig) ParseFromString(input string) []common.ParseError { var errors []common.ParseError lines := strings.Split( input, @@ -116,7 +91,7 @@ func (p *WireguardParser) ParseFromString(input string) []common.ParseError { } } - var emptySection *WireguardSection + var emptySection *ast.WGSection if len(collectedProperties) > 0 { var endLine uint32 @@ -127,7 +102,7 @@ func (p *WireguardParser) ParseFromString(input string) []common.ParseError { endLine = p.Sections[len(p.Sections)-1].StartLine } - emptySection = &WireguardSection{ + emptySection = &ast.WGSection{ StartLine: 0, EndLine: endLine, Properties: collectedProperties, @@ -152,7 +127,7 @@ func (p *WireguardParser) ParseFromString(input string) []common.ParseError { // Add empty section if endLine != 0 { - emptySection = &WireguardSection{ + emptySection = &ast.WGSection{ StartLine: 0, EndLine: endLine, Properties: collectedProperties, @@ -203,7 +178,7 @@ func (p *WireguardParser) ParseFromString(input string) []common.ParseError { return errors } -func (p *WireguardParser) GetSectionByLine(line uint32) *WireguardSection { +func (p *ast.WGConfig) GetSectionByLine(line uint32) *ast.WGSection { for _, section := range p.Sections { if section.StartLine <= line && section.EndLine >= line { return section @@ -215,7 +190,7 @@ func (p *WireguardParser) GetSectionByLine(line uint32) *WireguardSection { // Search for a property by name // Returns (line number, property) -func (p *WireguardParser) FindFirstPropertyByName(name string) (*uint32, *WireguardProperty) { +func (p *ast.WGConfig) FindFirstPropertyByName(name string) (*uint32, *ast.WGProperty) { for _, section := range p.Sections { for lineNumber, property := range section.Properties { if property.Key.Name == name { @@ -227,9 +202,9 @@ func (p *WireguardParser) FindFirstPropertyByName(name string) (*uint32, *Wiregu return nil, nil } -func (p WireguardParser) GetInterfaceSection() (*WireguardSection, bool) { +func (p ast.WGConfig) GetInterfaceSection() (*ast.WGSection, bool) { for _, section := range p.Sections { - if section.Name != nil && *section.Name == "Interface" { + if section.Header != nil && *section.Header == "Interface" { return section, true } } @@ -237,7 +212,7 @@ func (p WireguardParser) GetInterfaceSection() (*WireguardSection, bool) { return nil, false } -func (p WireguardParser) GetTypeByLine(line uint32) LineType { +func (p ast.WGConfig) GetTypeByLine(line uint32) LineType { // Check if line is a comment if _, found := p.commentLines[line]; found { return LineTypeComment @@ -250,53 +225,8 @@ func (p WireguardParser) GetTypeByLine(line uint32) LineType { return LineTypeEmpty } -// Get the section that the line belongs to -// Example: -// [Interface] -// Address = 10.0.0.1 -// -// -// [Peer] -// -// This would return the section [Interface] -func (p *WireguardParser) GetBelongingSectionByLine(line uint32) *WireguardSection { - if info, found := p.linesIndexes[line]; found { - return info.BelongingSection - } - - return nil -} - -func (p *WireguardParser) GetPropertyByLine(line uint32) (*WireguardSection, *WireguardProperty) { - section := p.GetSectionByLine(line) - - if section == nil || section.Name == nil { - return nil, nil - } - - property, _ := section.GetPropertyByLine(line) - - if property == nil { - return nil, nil - } - - return section, property -} - -func (p *WireguardParser) GetSectionsByName(name string) []*WireguardSection { - var sections []*WireguardSection - - for _, section := range p.Sections { - if section.Name != nil && *section.Name == name { - sections = append(sections, section) - } - } - - return sections -} - -func CreateWireguardParser() WireguardParser { - parser := WireguardParser{} +func CreateWireguardParser() ast.WGConfig { + parser := ast.WGConfig{} parser.Clear() return parser diff --git a/server/handlers/wireguard/parser/wg-parser_test.go b/server/handlers/wireguard/parser/wg-parser_test.go index eeee672..c49fa54 100644 --- a/server/handlers/wireguard/parser/wg-parser_test.go +++ b/server/handlers/wireguard/parser/wg-parser_test.go @@ -35,7 +35,7 @@ PublicKey = 5555 t.Fatalf("parseFromString failed to collect comment lines %v", parser.commentLines) } - if !((len(parser.Sections) == 3) && (*parser.Sections[0].Name == "Interface") && (*parser.Sections[1].Name == "Peer") && (*parser.Sections[2].Name == "Peer")) { + if !((len(parser.Sections) == 3) && (*parser.Sections[0].Header == "Interface") && (*parser.Sections[1].Header == "Peer") && (*parser.Sections[2].Header == "Peer")) { t.Fatalf("parseFromString failed to collect sections %v", parser.Sections) } @@ -93,7 +93,7 @@ PublicKey = 1234567890 t.Fatalf("parseFromString failed with error %v", errors) } - if !((len(parser.Sections) == 2) && (*parser.Sections[0].Name == "Interface") && (*parser.Sections[1].Name == "Peer")) { + if !((len(parser.Sections) == 2) && (*parser.Sections[0].Header == "Interface") && (*parser.Sections[1].Header == "Peer")) { t.Fatalf("parseFromString failed to collect sections %v", parser.Sections) } @@ -120,7 +120,7 @@ PrivateKey = 1234567890 t.Fatalf("parseFromString failed with error %v", errors) } - if !((len(parser.Sections) == 2) && (*parser.Sections[0].Name == "Inteface") && (*parser.Sections[1].Name == "Peer")) { + if !((len(parser.Sections) == 2) && (*parser.Sections[0].Header == "Inteface") && (*parser.Sections[1].Header == "Peer")) { t.Fatalf("parseFromString failed to collect sections %v", parser.Sections) } @@ -168,7 +168,7 @@ PublicKey = 1234567890 t.Fatalf("parseFromString failed with error %v", errors) } - if !((len(parser.Sections) == 2) && (*parser.Sections[0].Name == "Inte") && (*parser.Sections[1].Name == "Peer")) { + if !((len(parser.Sections) == 2) && (*parser.Sections[0].Header == "Inte") && (*parser.Sections[1].Header == "Peer")) { t.Fatalf("parseFromString failed to collect sections: %v", parser.Sections) } @@ -202,7 +202,7 @@ PrivateKey = 1234567890 t.Fatalf("parseFromString failed with error: %v", errors) } - if !((len(parser.Sections) == 2) && (*parser.Sections[0].Name == "Inte") && (*parser.Sections[1].Name == "Peer")) { + if !((len(parser.Sections) == 2) && (*parser.Sections[0].Header == "Inte") && (*parser.Sections[1].Header == "Peer")) { t.Fatalf("parseFromString failed to collect sections: %v", parser.Sections) } diff --git a/server/handlers/wireguard/parser/wg-property.go b/server/handlers/wireguard/parser/wg-property.go index 5deae64..a96937b 100644 --- a/server/handlers/wireguard/parser/wg-property.go +++ b/server/handlers/wireguard/parser/wg-property.go @@ -2,6 +2,7 @@ package parser import ( docvalues "config-lsp/doc-values" + "config-lsp/handlers/wireguard/ast" "config-lsp/utils" "regexp" "strings" @@ -11,71 +12,8 @@ import ( var linePattern = regexp.MustCompile(`^\s*(?P.+?)\s*(?P=)\s*(?P\S.*?)?\s*(?:(?:;|#).*)?\s*$`) -type WireguardPropertyKey struct { - Location CharacterLocation - Name string -} - -type WireguardPropertyValue struct { - Location CharacterLocation - Value string -} - -type WireguardPropertySeparator struct { - Location CharacterLocation -} - -type WireguardProperty struct { - Key WireguardPropertyKey - Separator *WireguardPropertySeparator - Value *WireguardPropertyValue -} - -func (p WireguardProperty) String() string { - if p.Value == nil { - return p.Key.Name - } - - return p.Key.Name + "=" + p.Value.Value -} - -func (p WireguardProperty) GetLineRange(line uint32) protocol.Range { - return protocol.Range{ - Start: protocol.Position{ - Line: line, - Character: p.Key.Location.Start, - }, - End: protocol.Position{ - Line: line, - Character: p.Key.Location.End, - }, - } -} - -func (p WireguardProperty) GetInsertRange(line uint32) protocol.Range { - var insertPosition uint32 = p.Separator.Location.End - var length uint32 = 0 - - if p.Value != nil { - insertPosition = p.Value.Location.Start - 1 - // Length of the value; +1 because of the starting space - length = (p.Value.Location.End - p.Value.Location.Start) + 1 - } - - return protocol.Range{ - Start: protocol.Position{ - Line: line, - Character: insertPosition, - }, - End: protocol.Position{ - Line: line, - Character: insertPosition + length, - }, - } -} - // WireguardProperties []: -type WireguardProperties map[uint32]WireguardProperty +type WireguardProperties map[uint32]ast.WGProperty func (p *WireguardProperties) AddLine(lineNumber uint32, line string) error { property, err := CreateWireguardProperty(line) @@ -89,7 +27,7 @@ func (p *WireguardProperties) AddLine(lineNumber uint32, line string) error { return nil } -func CreateWireguardProperty(line string) (*WireguardProperty, error) { +func CreateWireguardProperty(line string) (*ast.WGProperty, error) { if !strings.Contains(line, "=") { indexes := utils.GetTrimIndex(line) @@ -98,8 +36,8 @@ func CreateWireguardProperty(line string) (*WireguardProperty, error) { return nil, &docvalues.MalformedLineError{} } - return &WireguardProperty{ - Key: WireguardPropertyKey{ + return &ast.WGProperty{ + Key: ast.WGPropertyKey{ Name: line[indexes[0]:indexes[1]], Location: CharacterLocation{ Start: uint32(indexes[0]), @@ -117,7 +55,7 @@ func CreateWireguardProperty(line string) (*WireguardProperty, error) { keyStart := uint32(indexes[2]) keyEnd := uint32(indexes[3]) - key := WireguardPropertyKey{ + key := ast.WGPropertyKey{ Location: CharacterLocation{ Start: keyStart, End: keyEnd, @@ -127,21 +65,21 @@ func CreateWireguardProperty(line string) (*WireguardProperty, error) { separatorStart := uint32(indexes[4]) separatorEnd := uint32(indexes[5]) - separator := WireguardPropertySeparator{ + separator := ast.WGPropertySeparator{ Location: CharacterLocation{ Start: separatorStart, End: separatorEnd, }, } - var value *WireguardPropertyValue + var value *ast.WGPropertyValue if indexes[6] != -1 && indexes[7] != -1 { // value exists valueStart := uint32(indexes[6]) valueEnd := uint32(indexes[7]) - value = &WireguardPropertyValue{ + value = &ast.WGPropertyValue{ Location: CharacterLocation{ Start: valueStart, End: valueEnd, @@ -150,7 +88,7 @@ func CreateWireguardProperty(line string) (*WireguardProperty, error) { } } - return &WireguardProperty{ + return &ast.WGProperty{ Key: key, Separator: &separator, Value: value, diff --git a/server/handlers/wireguard/parser/wg-section.go b/server/handlers/wireguard/parser/wg-section.go index 8e94a9e..8dc5097 100644 --- a/server/handlers/wireguard/parser/wg-section.go +++ b/server/handlers/wireguard/parser/wg-section.go @@ -1,10 +1,8 @@ package parser import ( - "fmt" + "config-lsp/handlers/wireguard/ast" "regexp" - - protocol "github.com/tliron/glsp/protocol_3_16" ) type PropertyNotFoundError struct{} @@ -19,52 +17,7 @@ func (e PropertyNotFullyTypedError) Error() string { return "Property not fully typed" } -type WireguardSection struct { - Name *string - StartLine uint32 - EndLine uint32 - Properties WireguardProperties -} - -func (s WireguardSection) String() string { - var name string - - if s.Name == nil { - name = "" - } else { - name = *s.Name - } - - return fmt.Sprintf("[%s]; %d-%d: %v", name, s.StartLine, s.EndLine, s.Properties) -} - -func (s WireguardSection) GetHeaderLineRange() protocol.Range { - return protocol.Range{ - Start: protocol.Position{ - Line: s.StartLine, - Character: 0, - }, - End: protocol.Position{ - Line: s.StartLine, - Character: 99999999, - }, - } -} - -func (s WireguardSection) GetRange() protocol.Range { - return protocol.Range{ - Start: protocol.Position{ - Line: s.StartLine, - Character: 0, - }, - End: protocol.Position{ - Line: s.EndLine, - Character: 99999999, - }, - } -} - -func (s WireguardSection) FetchFirstProperty(name string) (*uint32, *WireguardProperty) { +func (s ast.WGSection) FetchFirstProperty(name string) (*uint32, *ast.WGProperty) { for line, property := range s.Properties { if property.Key.Name == name { return &line, &property @@ -74,13 +27,13 @@ func (s WireguardSection) FetchFirstProperty(name string) (*uint32, *WireguardPr return nil, nil } -func (s WireguardSection) ExistsProperty(name string) bool { +func (s ast.WGSection) ExistsProperty(name string) bool { _, property := s.FetchFirstProperty(name) return property != nil } -func (s WireguardSection) GetPropertyByLine(lineNumber uint32) (*WireguardProperty, error) { +func (s ast.WGSection) GetPropertyByLine(lineNumber uint32) (*ast.WGProperty, error) { property, found := s.Properties[lineNumber] if !found { @@ -99,7 +52,7 @@ func CreateWireguardSection( endLine uint32, headerLine string, props WireguardProperties, -) WireguardSection { +) ast.WGSection { match := validHeaderPattern.FindStringSubmatch(headerLine) var header string @@ -111,8 +64,8 @@ func CreateWireguardSection( header = match[1] } - return WireguardSection{ - Name: &header, + return ast.WGSection{ + Header: &header, StartLine: startLine, EndLine: endLine, Properties: props,