refactor(server): Improve Wireguard AST, analyzer and indexes

Signed-off-by: Myzel394 <github.7a2op@simplelogin.co>
This commit is contained in:
Myzel394 2025-02-23 21:48:08 +01:00 committed by Myzel394
parent ba056d6ae9
commit 36950fe271
No known key found for this signature in database
GPG Key ID: B603E877F73D4ABB
11 changed files with 82 additions and 234 deletions

View File

@ -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<key>.+?)\s*(?P<separator>=)\s*(?P<value>\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,

View File

@ -19,6 +19,7 @@ type WGPropertySeparator struct {
}
type WGProperty struct {
common.LocationRange
Key WGPropertyKey
Separator *WGPropertySeparator
Value *WGPropertyValue

View File

@ -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
}

View File

@ -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{}

View File

@ -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)

View File

@ -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
}

View File

@ -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)

View File

@ -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 {
// <key = name>: 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
//
// <line here>
// [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

View File

@ -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)
}

View File

@ -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<key>.+?)\s*(?P<separator>=)\s*(?P<value>\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 [<line number>]: <property>
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,

View File

@ -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 = "<nil>"
} 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,