mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-19 07:25:27 +02:00
refactor(wireguard): Move stuff into own packages; Improvements
This commit is contained in:
parent
91f0b0b52b
commit
fabdd83eda
BIN
config-lsp
BIN
config-lsp
Binary file not shown.
@ -1,157 +0,0 @@
|
|||||||
package wireguard
|
|
||||||
|
|
||||||
import (
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
|
||||||
)
|
|
||||||
|
|
||||||
type codeActionName string
|
|
||||||
|
|
||||||
const (
|
|
||||||
codeActionGeneratePrivateKey codeActionName = "generatePrivateKey"
|
|
||||||
codeActionGeneratePresharedKey codeActionName = "generatePresharedKey"
|
|
||||||
codeActionAddKeepalive codeActionName = "addKeepalive"
|
|
||||||
)
|
|
||||||
|
|
||||||
type codeActionGeneratePrivateKeyArgs struct {
|
|
||||||
URI protocol.DocumentUri
|
|
||||||
Line uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func codeActionGeneratePrivateKeyArgsFromArguments(arguments map[string]any) codeActionGeneratePrivateKeyArgs {
|
|
||||||
return codeActionGeneratePrivateKeyArgs{
|
|
||||||
URI: arguments["URI"].(protocol.DocumentUri),
|
|
||||||
Line: uint32(arguments["Line"].(float64)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type codeActionGeneratePresharedKeyArgs struct {
|
|
||||||
URI protocol.DocumentUri
|
|
||||||
Line uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func codeActionGeneratePresharedKeyArgsFromArguments(arguments map[string]any) codeActionGeneratePresharedKeyArgs {
|
|
||||||
return codeActionGeneratePresharedKeyArgs{
|
|
||||||
URI: arguments["URI"].(protocol.DocumentUri),
|
|
||||||
Line: uint32(arguments["Line"].(float64)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type codeActionAddKeepaliveArgs struct {
|
|
||||||
URI protocol.DocumentUri
|
|
||||||
SectionIndex uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func codeActionAddKeepaliveArgsFromArguments(arguments map[string]any) codeActionAddKeepaliveArgs {
|
|
||||||
return codeActionAddKeepaliveArgs{
|
|
||||||
URI: arguments["URI"].(protocol.DocumentUri),
|
|
||||||
SectionIndex: uint32(arguments["SectionIndex"].(float64)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *wireguardParser) runGeneratePrivateKey(args codeActionGeneratePrivateKeyArgs) (*protocol.ApplyWorkspaceEditParams, error) {
|
|
||||||
privateKey, err := createNewPrivateKey()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return &protocol.ApplyWorkspaceEditParams{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
section, property := p.getPropertyByLine(args.Line)
|
|
||||||
|
|
||||||
if section == nil || property == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
label := "Generate Private Key"
|
|
||||||
return &protocol.ApplyWorkspaceEditParams{
|
|
||||||
Label: &label,
|
|
||||||
Edit: protocol.WorkspaceEdit{
|
|
||||||
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
|
||||||
args.URI: {
|
|
||||||
{
|
|
||||||
NewText: " " + privateKey,
|
|
||||||
Range: property.getInsertRange(args.Line),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *wireguardParser) runGeneratePresharedKey(args codeActionGeneratePresharedKeyArgs) (*protocol.ApplyWorkspaceEditParams, error) {
|
|
||||||
presharedKey, err := createPresharedKey()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return &protocol.ApplyWorkspaceEditParams{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
section, property := p.getPropertyByLine(args.Line)
|
|
||||||
|
|
||||||
if section == nil || property == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
label := "Generate Preshared Key"
|
|
||||||
return &protocol.ApplyWorkspaceEditParams{
|
|
||||||
Label: &label,
|
|
||||||
Edit: protocol.WorkspaceEdit{
|
|
||||||
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
|
||||||
args.URI: {
|
|
||||||
{
|
|
||||||
NewText: " " + presharedKey,
|
|
||||||
Range: property.getInsertRange(args.Line),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *wireguardParser) runAddKeepalive(args codeActionAddKeepaliveArgs) (*protocol.ApplyWorkspaceEditParams, error) {
|
|
||||||
section := p.Sections[args.SectionIndex]
|
|
||||||
|
|
||||||
label := "Add PersistentKeepalive"
|
|
||||||
return &protocol.ApplyWorkspaceEditParams{
|
|
||||||
Label: &label,
|
|
||||||
Edit: protocol.WorkspaceEdit{
|
|
||||||
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
|
||||||
args.URI: {
|
|
||||||
{
|
|
||||||
NewText: "PersistentKeepalive = 25\n",
|
|
||||||
Range: protocol.Range{
|
|
||||||
Start: protocol.Position{
|
|
||||||
Line: section.EndLine + 1,
|
|
||||||
Character: 0,
|
|
||||||
},
|
|
||||||
End: protocol.Position{
|
|
||||||
Line: section.EndLine + 1,
|
|
||||||
Character: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package wireguard
|
package wgcommands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@ -8,13 +8,13 @@ import (
|
|||||||
|
|
||||||
var whitespacePattern = regexp.MustCompile(`[\s\n]+`)
|
var whitespacePattern = regexp.MustCompile(`[\s\n]+`)
|
||||||
|
|
||||||
func areWireguardToolsAvailable() bool {
|
func AreWireguardToolsAvailable() bool {
|
||||||
_, err := exec.LookPath("wg")
|
_, err := exec.LookPath("wg")
|
||||||
|
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createNewPrivateKey() (string, error) {
|
func CreateNewPrivateKey() (string, error) {
|
||||||
cmd := exec.Command("wg", "genkey")
|
cmd := exec.Command("wg", "genkey")
|
||||||
|
|
||||||
bytes, err := cmd.Output()
|
bytes, err := cmd.Output()
|
||||||
@ -26,7 +26,7 @@ func createNewPrivateKey() (string, error) {
|
|||||||
return string(whitespacePattern.ReplaceAll(bytes, []byte(""))), nil
|
return string(whitespacePattern.ReplaceAll(bytes, []byte(""))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createPublicKey(privateKey string) (string, error) {
|
func CreatePublicKey(privateKey string) (string, error) {
|
||||||
cmd := exec.Command("wg", "pubkey")
|
cmd := exec.Command("wg", "pubkey")
|
||||||
cmd.Stdin = strings.NewReader(privateKey)
|
cmd.Stdin = strings.NewReader(privateKey)
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ func createPublicKey(privateKey string) (string, error) {
|
|||||||
return string(whitespacePattern.ReplaceAll(bytes, []byte(""))), nil
|
return string(whitespacePattern.ReplaceAll(bytes, []byte(""))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createPresharedKey() (string, error) {
|
func CreatePresharedKey() (string, error) {
|
||||||
cmd := exec.Command("wg", "genpsk")
|
cmd := exec.Command("wg", "genpsk")
|
||||||
|
|
||||||
bytes, err := cmd.Output()
|
bytes, err := cmd.Output()
|
@ -1,11 +1,11 @@
|
|||||||
package wireguard
|
package wgcommands
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestWireguardAvailable(
|
func TestWireguardAvailable(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
if !areWireguardToolsAvailable() {
|
if !AreWireguardToolsAvailable() {
|
||||||
t.Skip("Wireguard tools not available")
|
t.Skip("Wireguard tools not available")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -13,7 +13,7 @@ func TestWireguardAvailable(
|
|||||||
func TestWireguardPrivateKey(
|
func TestWireguardPrivateKey(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
privateKey, err := createNewPrivateKey()
|
privateKey, err := CreateNewPrivateKey()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -26,7 +26,7 @@ func TestWireguardPublicKey(
|
|||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
privateKey := "UPBKR0kLF2C/+Ei5fwN5KHsAcon9xfBX+RWhebYFGWg="
|
privateKey := "UPBKR0kLF2C/+Ei5fwN5KHsAcon9xfBX+RWhebYFGWg="
|
||||||
publicKey, err := createPublicKey(privateKey)
|
publicKey, err := CreatePublicKey(privateKey)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
@ -1,15 +1,15 @@
|
|||||||
// Documentation taken from https://github.com/pirate/wireguard-docs
|
package fields
|
||||||
package wireguard
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
docvalues "config-lsp/doc-values"
|
docvalues "config-lsp/doc-values"
|
||||||
)
|
)
|
||||||
|
|
||||||
var headerInterfaceEnum = docvalues.CreateEnumStringWithDoc(
|
// Documentation taken from https://github.com/pirate/wireguard-docs
|
||||||
|
var HeaderInterfaceEnum = docvalues.CreateEnumStringWithDoc(
|
||||||
"[Interface]",
|
"[Interface]",
|
||||||
"Defines the VPN settings for the local node.",
|
"Defines the VPN settings for the local node.",
|
||||||
)
|
)
|
||||||
var headerPeerEnum = docvalues.CreateEnumStringWithDoc(
|
var HeaderPeerEnum = docvalues.CreateEnumStringWithDoc(
|
||||||
"[Peer]",
|
"[Peer]",
|
||||||
`Defines the VPN settings for a remote peer capable of routing traffic for one or more addresses (itself and/or other peers). Peers can be either a public bounce server that relays traffic to other peers, or a directly accessible client via LAN/internet that is not behind a NAT and only routes traffic for itself.
|
`Defines the VPN settings for a remote peer capable of routing traffic for one or more addresses (itself and/or other peers). Peers can be either a public bounce server that relays traffic to other peers, or a directly accessible client via LAN/internet that is not behind a NAT and only routes traffic for itself.
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ var maxPortValue = 65535
|
|||||||
var minMTUValue = 68
|
var minMTUValue = 68
|
||||||
var maxMTUValue = 1500
|
var maxMTUValue = 1500
|
||||||
|
|
||||||
var interfaceOptions = map[string]docvalues.DocumentationValue{
|
var InterfaceOptions = map[string]docvalues.DocumentationValue{
|
||||||
"Address": {
|
"Address": {
|
||||||
Documentation: `Defines what address range the local node should route traffic for. Depending on whether the node is a simple client joining the VPN subnet, or a bounce server that's relaying traffic between multiple clients, this can be set to a single IP of the node itself (specified with CIDR notation), e.g. 192.0.2.3/32), or a range of IPv4/IPv6 subnets that the node can route traffic for.
|
Documentation: `Defines what address range the local node should route traffic for. Depending on whether the node is a simple client joining the VPN subnet, or a bounce server that's relaying traffic between multiple clients, this can be set to a single IP of the node itself (specified with CIDR notation), e.g. 192.0.2.3/32), or a range of IPv4/IPv6 subnets that the node can route traffic for.
|
||||||
|
|
||||||
@ -221,14 +221,14 @@ Remove the iptables rule that forwards packets on the WireGuard interface
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var interfaceAllowedDuplicateFields = map[string]struct{}{
|
var InterfaceAllowedDuplicateFields = map[string]struct{}{
|
||||||
"PreUp": {},
|
"PreUp": {},
|
||||||
"PostUp": {},
|
"PostUp": {},
|
||||||
"PreDown": {},
|
"PreDown": {},
|
||||||
"PostDown": {},
|
"PostDown": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
var peerOptions = map[string]docvalues.DocumentationValue{
|
var PeerOptions = map[string]docvalues.DocumentationValue{
|
||||||
"Endpoint": {
|
"Endpoint": {
|
||||||
Documentation: `Defines the publicly accessible address for a remote peer. This should be left out for peers behind a NAT or peers that don't have a stable publicly accessible IP:PORT pair. Typically, this only needs to be defined on the main bounce server, but it can also be defined on other public nodes with stable IPs like public-server2 in the example config below.
|
Documentation: `Defines the publicly accessible address for a remote peer. This should be left out for peers behind a NAT or peers that don't have a stable publicly accessible IP:PORT pair. Typically, this only needs to be defined on the main bounce server, but it can also be defined on other public nodes with stable IPs like public-server2 in the example config below.
|
||||||
|
|
||||||
@ -316,11 +316,11 @@ Oocal NAT-ed node to remote public node
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var peerAllowedDuplicateFields = map[string]struct{}{
|
var PeerAllowedDuplicateFields = map[string]struct{}{
|
||||||
"AllowedIPs": {},
|
"AllowedIPs": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
var optionsHeaderMap = map[string](map[string]docvalues.DocumentationValue){
|
var OptionsHeaderMap = map[string](map[string]docvalues.DocumentationValue){
|
||||||
"Interface": interfaceOptions,
|
"Interface": InterfaceOptions,
|
||||||
"Peer": peerOptions,
|
"Peer": PeerOptions,
|
||||||
}
|
}
|
@ -1,7 +1,9 @@
|
|||||||
package wireguard
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
docvalues "config-lsp/doc-values"
|
docvalues "config-lsp/doc-values"
|
||||||
|
"config-lsp/handlers/wireguard/fields"
|
||||||
|
"config-lsp/handlers/wireguard/parser"
|
||||||
"config-lsp/utils"
|
"config-lsp/utils"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
@ -10,33 +12,38 @@ import (
|
|||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p wireguardParser) analyze() []protocol.Diagnostic {
|
func Analyze(
|
||||||
sectionsErrors := p.analyzeSections()
|
p parser.WireguardParser,
|
||||||
|
) []protocol.Diagnostic {
|
||||||
|
sectionsErrors := analyzeSections(p.Sections)
|
||||||
|
sectionsErrors = append(analyzeOnlyOneInterfaceSectionSpecified(p))
|
||||||
|
|
||||||
if len(sectionsErrors) > 0 {
|
if len(sectionsErrors) > 0 {
|
||||||
return sectionsErrors
|
return sectionsErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
validCheckErrors := p.checkIfValuesAreValid()
|
validCheckErrors := checkIfValuesAreValid(p.Sections)
|
||||||
|
|
||||||
if len(validCheckErrors) > 0 {
|
if len(validCheckErrors) > 0 {
|
||||||
return validCheckErrors
|
return validCheckErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics := []protocol.Diagnostic{}
|
diagnostics := make([]protocol.Diagnostic, 0)
|
||||||
diagnostics = append(diagnostics, p.checkForDuplicateProperties()...)
|
diagnostics = append(diagnostics, analyzeParserForDuplicateProperties(p)...)
|
||||||
diagnostics = append(diagnostics, p.analyzeDNSContainsFallback()...)
|
diagnostics = append(diagnostics, analyzeDNSContainsFallback(p)...)
|
||||||
diagnostics = append(diagnostics, p.analyzeKeepAliveIsSet()...)
|
diagnostics = append(diagnostics, analyzeKeepAliveIsSet(p)...)
|
||||||
diagnostics = append(diagnostics, p.analyzeSymmetricPropertiesExist()...)
|
diagnostics = append(diagnostics, analyzeSymmetricPropertiesExist(p)...)
|
||||||
|
|
||||||
return diagnostics
|
return diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p wireguardParser) analyzeSections() []protocol.Diagnostic {
|
func analyzeSections(
|
||||||
diagnostics := []protocol.Diagnostic{}
|
sections []*parser.WireguardSection,
|
||||||
|
) []protocol.Diagnostic {
|
||||||
|
var diagnostics []protocol.Diagnostic
|
||||||
|
|
||||||
for _, section := range p.Sections {
|
for _, section := range sections {
|
||||||
sectionDiagnostics := section.analyzeSection()
|
sectionDiagnostics := analyzeSection(*section)
|
||||||
|
|
||||||
if len(sectionDiagnostics) > 0 {
|
if len(sectionDiagnostics) > 0 {
|
||||||
diagnostics = append(diagnostics, sectionDiagnostics...)
|
diagnostics = append(diagnostics, sectionDiagnostics...)
|
||||||
@ -47,20 +54,22 @@ func (p wireguardParser) analyzeSections() []protocol.Diagnostic {
|
|||||||
return diagnostics
|
return diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.analyzeOnlyOneInterfaceSectionSpecified()
|
return diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p wireguardParser) analyzeOnlyOneInterfaceSectionSpecified() []protocol.Diagnostic {
|
func analyzeOnlyOneInterfaceSectionSpecified(
|
||||||
diagnostics := []protocol.Diagnostic{}
|
p parser.WireguardParser,
|
||||||
|
) []protocol.Diagnostic {
|
||||||
|
var diagnostics []protocol.Diagnostic
|
||||||
alreadyFound := false
|
alreadyFound := false
|
||||||
|
|
||||||
for _, section := range p.getSectionsByName("Interface") {
|
for _, section := range p.GetSectionsByName("Interface") {
|
||||||
if alreadyFound {
|
if alreadyFound {
|
||||||
severity := protocol.DiagnosticSeverityError
|
severity := protocol.DiagnosticSeverityError
|
||||||
diagnostics = append(diagnostics, protocol.Diagnostic{
|
diagnostics = append(diagnostics, protocol.Diagnostic{
|
||||||
Message: "Only one [Interface] section is allowed",
|
Message: "Only one [Interface] section is allowed",
|
||||||
Severity: &severity,
|
Severity: &severity,
|
||||||
Range: section.getHeaderLineRange(),
|
Range: section.GetHeaderLineRange(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,8 +79,10 @@ func (p wireguardParser) analyzeOnlyOneInterfaceSectionSpecified() []protocol.Di
|
|||||||
return diagnostics
|
return diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p wireguardParser) analyzeDNSContainsFallback() []protocol.Diagnostic {
|
func analyzeDNSContainsFallback(
|
||||||
lineNumber, property := p.fetchPropertyByName("DNS")
|
p parser.WireguardParser,
|
||||||
|
) []protocol.Diagnostic {
|
||||||
|
lineNumber, property := p.FindFirstPropertyByName("DNS")
|
||||||
|
|
||||||
if property == nil {
|
if property == nil {
|
||||||
return []protocol.Diagnostic{}
|
return []protocol.Diagnostic{}
|
||||||
@ -103,17 +114,19 @@ func (p wireguardParser) analyzeDNSContainsFallback() []protocol.Diagnostic {
|
|||||||
return []protocol.Diagnostic{}
|
return []protocol.Diagnostic{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p wireguardParser) analyzeKeepAliveIsSet() []protocol.Diagnostic {
|
func analyzeKeepAliveIsSet(
|
||||||
diagnostics := make([]protocol.Diagnostic, 0)
|
p parser.WireguardParser,
|
||||||
|
) []protocol.Diagnostic {
|
||||||
|
var diagnostics []protocol.Diagnostic
|
||||||
|
|
||||||
for _, section := range p.getSectionsByName("Peer") {
|
for _, section := range p.GetSectionsByName("Peer") {
|
||||||
// If an endpoint is set, then we should only check for the keepalive property
|
// If an endpoint is set, then we should only check for the keepalive property
|
||||||
if section.existsProperty("Endpoint") && !section.existsProperty("PersistentKeepalive") {
|
if section.ExistsProperty("Endpoint") && !section.ExistsProperty("PersistentKeepalive") {
|
||||||
severity := protocol.DiagnosticSeverityHint
|
severity := protocol.DiagnosticSeverityHint
|
||||||
diagnostics = append(diagnostics, protocol.Diagnostic{
|
diagnostics = append(diagnostics, protocol.Diagnostic{
|
||||||
Message: "PersistentKeepalive is not set. It is recommended to set this property, as it helps to maintain the connection when users are behind NAT",
|
Message: "PersistentKeepalive is not set. It is recommended to set this property, as it helps to maintain the connection when users are behind NAT",
|
||||||
Severity: &severity,
|
Severity: &severity,
|
||||||
Range: section.getRange(),
|
Range: section.GetRange(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,14 +136,16 @@ func (p wireguardParser) analyzeKeepAliveIsSet() []protocol.Diagnostic {
|
|||||||
|
|
||||||
// Check if the values are valid.
|
// Check if the values are valid.
|
||||||
// Assumes that sections have been analyzed already.
|
// Assumes that sections have been analyzed already.
|
||||||
func (p wireguardParser) checkIfValuesAreValid() []protocol.Diagnostic {
|
func checkIfValuesAreValid(
|
||||||
diagnostics := []protocol.Diagnostic{}
|
sections []*parser.WireguardSection,
|
||||||
|
) []protocol.Diagnostic {
|
||||||
|
var diagnostics []protocol.Diagnostic
|
||||||
|
|
||||||
for _, section := range p.Sections {
|
for _, section := range sections {
|
||||||
for lineNumber, property := range section.Properties {
|
for lineNumber, property := range section.Properties {
|
||||||
diagnostics = append(
|
diagnostics = append(
|
||||||
diagnostics,
|
diagnostics,
|
||||||
property.analyzeProperty(section, lineNumber)...,
|
analyzeProperty(property, section, lineNumber)...,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,8 +153,10 @@ func (p wireguardParser) checkIfValuesAreValid() []protocol.Diagnostic {
|
|||||||
return diagnostics
|
return diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s wireguardSection) analyzeSection() []protocol.Diagnostic {
|
func analyzeSection(
|
||||||
diagnostics := []protocol.Diagnostic{}
|
s parser.WireguardSection,
|
||||||
|
) []protocol.Diagnostic {
|
||||||
|
var diagnostics []protocol.Diagnostic
|
||||||
|
|
||||||
if s.Name == nil {
|
if s.Name == nil {
|
||||||
// No section name
|
// No section name
|
||||||
@ -147,18 +164,18 @@ func (s wireguardSection) analyzeSection() []protocol.Diagnostic {
|
|||||||
diagnostics = append(diagnostics, protocol.Diagnostic{
|
diagnostics = append(diagnostics, protocol.Diagnostic{
|
||||||
Message: "This section is missing a name",
|
Message: "This section is missing a name",
|
||||||
Severity: &severity,
|
Severity: &severity,
|
||||||
Range: s.getRange(),
|
Range: s.GetRange(),
|
||||||
})
|
})
|
||||||
return diagnostics
|
return diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, found := optionsHeaderMap[*s.Name]; !found {
|
if _, found := fields.OptionsHeaderMap[*s.Name]; !found {
|
||||||
// Unknown section
|
// Unknown section
|
||||||
severity := protocol.DiagnosticSeverityError
|
severity := protocol.DiagnosticSeverityError
|
||||||
diagnostics = append(diagnostics, protocol.Diagnostic{
|
diagnostics = append(diagnostics, protocol.Diagnostic{
|
||||||
Message: fmt.Sprintf("Unknown section '%s'. It must be one of: [Interface], [Peer]", *s.Name),
|
Message: fmt.Sprintf("Unknown section '%s'. It must be one of: [Interface], [Peer]", *s.Name),
|
||||||
Severity: &severity,
|
Severity: &severity,
|
||||||
Range: s.getHeaderLineRange(),
|
Range: s.GetHeaderLineRange(),
|
||||||
})
|
})
|
||||||
|
|
||||||
return diagnostics
|
return diagnostics
|
||||||
@ -171,11 +188,12 @@ func (s wireguardSection) analyzeSection() []protocol.Diagnostic {
|
|||||||
// Returns a list of diagnostics.
|
// Returns a list of diagnostics.
|
||||||
// `belongingSection` is the section to which the property belongs. This value is
|
// `belongingSection` is the section to which the property belongs. This value is
|
||||||
// expected to be non-nil and expected to be a valid Wireguard section.
|
// expected to be non-nil and expected to be a valid Wireguard section.
|
||||||
func (p wireguardProperty) analyzeProperty(
|
func analyzeProperty(
|
||||||
belongingSection *wireguardSection,
|
p parser.WireguardProperty,
|
||||||
|
belongingSection *parser.WireguardSection,
|
||||||
propertyLine uint32,
|
propertyLine uint32,
|
||||||
) []protocol.Diagnostic {
|
) []protocol.Diagnostic {
|
||||||
sectionOptions := optionsHeaderMap[*belongingSection.Name]
|
sectionOptions := fields.OptionsHeaderMap[*belongingSection.Name]
|
||||||
option, found := sectionOptions[p.Key.Name]
|
option, found := sectionOptions[p.Key.Name]
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
@ -206,7 +224,7 @@ func (p wireguardProperty) analyzeProperty(
|
|||||||
{
|
{
|
||||||
Message: "Property is missing a value",
|
Message: "Property is missing a value",
|
||||||
Severity: &severity,
|
Severity: &severity,
|
||||||
Range: p.getLineRange(propertyLine),
|
Range: p.GetLineRange(propertyLine),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,36 +250,40 @@ func (p wireguardProperty) analyzeProperty(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p wireguardParser) checkForDuplicateProperties() []protocol.Diagnostic {
|
func analyzeParserForDuplicateProperties(
|
||||||
|
p parser.WireguardParser,
|
||||||
|
) []protocol.Diagnostic {
|
||||||
diagnostics := make([]protocol.Diagnostic, 0)
|
diagnostics := make([]protocol.Diagnostic, 0)
|
||||||
|
|
||||||
for _, section := range p.Sections {
|
for _, section := range p.Sections {
|
||||||
diagnostics = append(diagnostics, section.analyzeDuplicateProperties()...)
|
diagnostics = append(diagnostics, analyzeDuplicateProperties(*section)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return diagnostics
|
return diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p wireguardSection) analyzeDuplicateProperties() []protocol.Diagnostic {
|
func analyzeDuplicateProperties(
|
||||||
diagnostics := make([]protocol.Diagnostic, 0)
|
s parser.WireguardSection,
|
||||||
|
) []protocol.Diagnostic {
|
||||||
|
var diagnostics []protocol.Diagnostic
|
||||||
|
|
||||||
existingProperties := make(map[string]uint32)
|
existingProperties := make(map[string]uint32)
|
||||||
|
|
||||||
lines := utils.KeysOfMap(p.Properties)
|
lines := utils.KeysOfMap(s.Properties)
|
||||||
slices.Sort(lines)
|
slices.Sort(lines)
|
||||||
|
|
||||||
for _, currentLineNumber := range lines {
|
for _, currentLineNumber := range lines {
|
||||||
property := p.Properties[currentLineNumber]
|
property := s.Properties[currentLineNumber]
|
||||||
var skipCheck = false
|
var skipCheck = false
|
||||||
|
|
||||||
if p.Name != nil {
|
if s.Name != nil {
|
||||||
switch *p.Name {
|
switch *s.Name {
|
||||||
case "Interface":
|
case "Interface":
|
||||||
if _, found := interfaceAllowedDuplicateFields[property.Key.Name]; found {
|
if _, found := fields.InterfaceAllowedDuplicateFields[property.Key.Name]; found {
|
||||||
skipCheck = true
|
skipCheck = true
|
||||||
}
|
}
|
||||||
case "Peer":
|
case "Peer":
|
||||||
if _, found := peerAllowedDuplicateFields[property.Key.Name]; found {
|
if _, found := fields.PeerAllowedDuplicateFields[property.Key.Name]; found {
|
||||||
skipCheck = true
|
skipCheck = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -295,33 +317,40 @@ func (p wireguardSection) analyzeDuplicateProperties() []protocol.Diagnostic {
|
|||||||
return diagnostics
|
return diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strategy
|
||||||
|
// Simply compare the host bits of the IP addresses.
|
||||||
|
// Use a binary tree to store the host bits.
|
||||||
|
/*
|
||||||
func (p wireguardParser) analyzeAllowedIPIsInRange() []protocol.Diagnostic {
|
func (p wireguardParser) analyzeAllowedIPIsInRange() []protocol.Diagnostic {
|
||||||
diagnostics := make([]protocol.Diagnostic, 0)
|
diagnostics := make([]protocol.Diagnostic, 0)
|
||||||
|
|
||||||
return diagnostics
|
return diagnostics
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func (p wireguardParser) analyzeSymmetricPropertiesExist() []protocol.Diagnostic {
|
func analyzeSymmetricPropertiesExist(
|
||||||
|
p parser.WireguardParser,
|
||||||
|
) []protocol.Diagnostic {
|
||||||
diagnostics := make([]protocol.Diagnostic, 0, 4)
|
diagnostics := make([]protocol.Diagnostic, 0, 4)
|
||||||
severity := protocol.DiagnosticSeverityHint
|
severity := protocol.DiagnosticSeverityHint
|
||||||
|
|
||||||
for _, section := range p.getSectionsByName("Interface") {
|
for _, section := range p.GetSectionsByName("Interface") {
|
||||||
preUpLine, preUpProperty := section.fetchFirstProperty("PreUp")
|
preUpLine, preUpProperty := section.FetchFirstProperty("PreUp")
|
||||||
preDownLine, preDownProperty := section.fetchFirstProperty("PreDown")
|
preDownLine, preDownProperty := section.FetchFirstProperty("PreDown")
|
||||||
|
|
||||||
postUpLine, postUpProperty := section.fetchFirstProperty("PostUp")
|
postUpLine, postUpProperty := section.FetchFirstProperty("PostUp")
|
||||||
postDownLine, postDownProperty := section.fetchFirstProperty("PostDown")
|
postDownLine, postDownProperty := section.FetchFirstProperty("PostDown")
|
||||||
|
|
||||||
if preUpProperty != nil && preDownProperty == nil {
|
if preUpProperty != nil && preDownProperty == nil {
|
||||||
diagnostics = append(diagnostics, protocol.Diagnostic{
|
diagnostics = append(diagnostics, protocol.Diagnostic{
|
||||||
Message: "PreUp is set, but PreDown is not. It is recommended to set both properties symmetrically",
|
Message: "PreUp is set, but PreDown is not. It is recommended to set both properties symmetrically",
|
||||||
Range: preUpProperty.getLineRange(*preUpLine),
|
Range: preUpProperty.GetLineRange(*preUpLine),
|
||||||
Severity: &severity,
|
Severity: &severity,
|
||||||
})
|
})
|
||||||
} else if preUpProperty == nil && preDownProperty != nil {
|
} else if preUpProperty == nil && preDownProperty != nil {
|
||||||
diagnostics = append(diagnostics, protocol.Diagnostic{
|
diagnostics = append(diagnostics, protocol.Diagnostic{
|
||||||
Message: "PreDown is set, but PreUp is not. It is recommended to set both properties symmetrically",
|
Message: "PreDown is set, but PreUp is not. It is recommended to set both properties symmetrically",
|
||||||
Range: preDownProperty.getLineRange(*preDownLine),
|
Range: preDownProperty.GetLineRange(*preDownLine),
|
||||||
Severity: &severity,
|
Severity: &severity,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -329,13 +358,13 @@ func (p wireguardParser) analyzeSymmetricPropertiesExist() []protocol.Diagnostic
|
|||||||
if postUpProperty != nil && postDownProperty == nil {
|
if postUpProperty != nil && postDownProperty == nil {
|
||||||
diagnostics = append(diagnostics, protocol.Diagnostic{
|
diagnostics = append(diagnostics, protocol.Diagnostic{
|
||||||
Message: "PostUp is set, but PostDown is not. It is recommended to set both properties symmetrically",
|
Message: "PostUp is set, but PostDown is not. It is recommended to set both properties symmetrically",
|
||||||
Range: postUpProperty.getLineRange(*postUpLine),
|
Range: postUpProperty.GetLineRange(*postUpLine),
|
||||||
Severity: &severity,
|
Severity: &severity,
|
||||||
})
|
})
|
||||||
} else if postUpProperty == nil && postDownProperty != nil {
|
} else if postUpProperty == nil && postDownProperty != nil {
|
||||||
diagnostics = append(diagnostics, protocol.Diagnostic{
|
diagnostics = append(diagnostics, protocol.Diagnostic{
|
||||||
Message: "PostDown is set, but PostUp is not. It is recommended to set both properties symmetrically",
|
Message: "PostDown is set, but PostUp is not. It is recommended to set both properties symmetrically",
|
||||||
Range: postDownProperty.getLineRange(*postDownLine),
|
Range: postDownProperty.GetLineRange(*postDownLine),
|
||||||
Severity: &severity,
|
Severity: &severity,
|
||||||
})
|
})
|
||||||
}
|
}
|
@ -1,19 +1,23 @@
|
|||||||
package wireguard
|
package handlers
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"config-lsp/handlers/wireguard/parser"
|
||||||
|
"config-lsp/utils"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestMultipleIntefaces(t *testing.T) {
|
func TestMultipleIntefaces(t *testing.T) {
|
||||||
content := dedent(`
|
content := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
PrivateKey = abc
|
PrivateKey = abc
|
||||||
|
|
||||||
[Interface]
|
[Interface]
|
||||||
PrivateKey = def
|
PrivateKey = def
|
||||||
`)
|
`)
|
||||||
parser := createWireguardParser()
|
p := parser.CreateWireguardParser()
|
||||||
parser.parseFromString(content)
|
p.ParseFromString(content)
|
||||||
|
|
||||||
diagnostics := parser.analyze()
|
diagnostics := Analyze(p)
|
||||||
|
|
||||||
if len(diagnostics) == 0 {
|
if len(diagnostics) == 0 {
|
||||||
t.Errorf("Expected diagnostic errors, got %d", len(diagnostics))
|
t.Errorf("Expected diagnostic errors, got %d", len(diagnostics))
|
||||||
@ -21,14 +25,14 @@ PrivateKey = def
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidValue(t *testing.T) {
|
func TestInvalidValue(t *testing.T) {
|
||||||
content := dedent(`
|
content := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
DNS = nope
|
DNS = nope
|
||||||
`)
|
`)
|
||||||
parser := createWireguardParser()
|
p := parser.CreateWireguardParser()
|
||||||
parser.parseFromString(content)
|
p.ParseFromString(content)
|
||||||
|
|
||||||
diagnostics := parser.analyze()
|
diagnostics := Analyze(p)
|
||||||
|
|
||||||
if len(diagnostics) == 0 {
|
if len(diagnostics) == 0 {
|
||||||
t.Errorf("Expected diagnostic errors, got %d", len(diagnostics))
|
t.Errorf("Expected diagnostic errors, got %d", len(diagnostics))
|
||||||
@ -36,16 +40,16 @@ DNS = nope
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDuplicateProperties(t *testing.T) {
|
func TestDuplicateProperties(t *testing.T) {
|
||||||
content := dedent(`
|
content := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
PrivateKey = abc
|
PrivateKey = abc
|
||||||
DNS = 1.1.1.1
|
DNS = 1.1.1.1
|
||||||
PrivateKey = def
|
PrivateKey = def
|
||||||
`)
|
`)
|
||||||
parser := createWireguardParser()
|
p := parser.CreateWireguardParser()
|
||||||
parser.parseFromString(content)
|
p.ParseFromString(content)
|
||||||
|
|
||||||
diagnostics := parser.analyze()
|
diagnostics := Analyze(p)
|
||||||
|
|
||||||
if len(diagnostics) == 0 {
|
if len(diagnostics) == 0 {
|
||||||
t.Errorf("Expected diagnostic errors, got %d", len(diagnostics))
|
t.Errorf("Expected diagnostic errors, got %d", len(diagnostics))
|
143
handlers/wireguard/handlers/code-actions.go
Normal file
143
handlers/wireguard/handlers/code-actions.go
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
wgcommands "config-lsp/handlers/wireguard/commands"
|
||||||
|
"config-lsp/handlers/wireguard/parser"
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CodeActionName string
|
||||||
|
|
||||||
|
const (
|
||||||
|
CodeActionGeneratePrivateKey CodeActionName = "generatePrivateKey"
|
||||||
|
CodeActionGeneratePresharedKey CodeActionName = "generatePresharedKey"
|
||||||
|
CodeActionAddKeepalive CodeActionName = "addKeepalive"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CodeAction interface {
|
||||||
|
RunCommand(*parser.WireguardParser) (*protocol.ApplyWorkspaceEditParams, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CodeActionArgs interface{}
|
||||||
|
|
||||||
|
type CodeActionGeneratePrivateKeyArgs struct {
|
||||||
|
URI protocol.DocumentUri
|
||||||
|
Line uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func CodeActionGeneratePrivateKeyArgsFromArguments(arguments map[string]any) CodeActionGeneratePrivateKeyArgs {
|
||||||
|
return CodeActionGeneratePrivateKeyArgs{
|
||||||
|
URI: arguments["URI"].(protocol.DocumentUri),
|
||||||
|
Line: uint32(arguments["Line"].(float64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (args CodeActionGeneratePrivateKeyArgs) RunCommand(p *parser.WireguardParser) (*protocol.ApplyWorkspaceEditParams, error) {
|
||||||
|
privateKey, err := wgcommands.CreateNewPrivateKey()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return &protocol.ApplyWorkspaceEditParams{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
section, property := p.GetPropertyByLine(args.Line)
|
||||||
|
|
||||||
|
if section == nil || property == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
label := "Generate Private Key"
|
||||||
|
return &protocol.ApplyWorkspaceEditParams{
|
||||||
|
Label: &label,
|
||||||
|
Edit: protocol.WorkspaceEdit{
|
||||||
|
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
||||||
|
args.URI: {
|
||||||
|
{
|
||||||
|
NewText: " " + privateKey,
|
||||||
|
Range: property.GetInsertRange(args.Line),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CodeActionGeneratePresharedKeyArgs struct {
|
||||||
|
URI protocol.DocumentUri
|
||||||
|
Line uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func CodeActionGeneratePresharedKeyArgsFromArguments(arguments map[string]any) CodeActionGeneratePresharedKeyArgs {
|
||||||
|
return CodeActionGeneratePresharedKeyArgs{
|
||||||
|
URI: arguments["URI"].(protocol.DocumentUri),
|
||||||
|
Line: uint32(arguments["Line"].(float64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (args CodeActionGeneratePresharedKeyArgs) RunCommand(p *parser.WireguardParser) (*protocol.ApplyWorkspaceEditParams, error) {
|
||||||
|
presharedKey, err := wgcommands.CreatePresharedKey()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return &protocol.ApplyWorkspaceEditParams{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
section, property := p.GetPropertyByLine(args.Line)
|
||||||
|
|
||||||
|
if section == nil || property == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
label := "Generate Preshared Key"
|
||||||
|
return &protocol.ApplyWorkspaceEditParams{
|
||||||
|
Label: &label,
|
||||||
|
Edit: protocol.WorkspaceEdit{
|
||||||
|
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
||||||
|
args.URI: {
|
||||||
|
{
|
||||||
|
NewText: " " + presharedKey,
|
||||||
|
Range: property.GetInsertRange(args.Line),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CodeActionAddKeepaliveArgs struct {
|
||||||
|
URI protocol.DocumentUri
|
||||||
|
SectionIndex uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func CodeActionAddKeepaliveArgsFromArguments(arguments map[string]any) CodeActionAddKeepaliveArgs {
|
||||||
|
return CodeActionAddKeepaliveArgs{
|
||||||
|
URI: arguments["URI"].(protocol.DocumentUri),
|
||||||
|
SectionIndex: uint32(arguments["SectionIndex"].(float64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (args CodeActionAddKeepaliveArgs) RunCommand(p *parser.WireguardParser) (*protocol.ApplyWorkspaceEditParams, error) {
|
||||||
|
section := p.Sections[args.SectionIndex]
|
||||||
|
|
||||||
|
label := "Add PersistentKeepalive"
|
||||||
|
return &protocol.ApplyWorkspaceEditParams{
|
||||||
|
Label: &label,
|
||||||
|
Edit: protocol.WorkspaceEdit{
|
||||||
|
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
||||||
|
args.URI: {
|
||||||
|
{
|
||||||
|
NewText: "PersistentKeepalive = 25\n",
|
||||||
|
Range: protocol.Range{
|
||||||
|
Start: protocol.Position{
|
||||||
|
Line: section.EndLine + 1,
|
||||||
|
Character: 0,
|
||||||
|
},
|
||||||
|
End: protocol.Position{
|
||||||
|
Line: section.EndLine + 1,
|
||||||
|
Character: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
160
handlers/wireguard/handlers/completions.go
Normal file
160
handlers/wireguard/handlers/completions.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
docvalues "config-lsp/doc-values"
|
||||||
|
"config-lsp/handlers/wireguard/fields"
|
||||||
|
"config-lsp/handlers/wireguard/parser"
|
||||||
|
"config-lsp/utils"
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
"maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getHeaderCompletion(name string, documentation string) protocol.CompletionItem {
|
||||||
|
textFormat := protocol.InsertTextFormatPlainText
|
||||||
|
kind := protocol.CompletionItemKindEnum
|
||||||
|
|
||||||
|
insertText := "[" + name + "]\n"
|
||||||
|
|
||||||
|
return protocol.CompletionItem{
|
||||||
|
Label: "[" + name + "]",
|
||||||
|
InsertTextFormat: &textFormat,
|
||||||
|
InsertText: &insertText,
|
||||||
|
Kind: &kind,
|
||||||
|
Documentation: &documentation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRootCompletionsForEmptyLine(
|
||||||
|
p parser.WireguardParser,
|
||||||
|
) ([]protocol.CompletionItem, error) {
|
||||||
|
completions := make([]protocol.CompletionItem, 0)
|
||||||
|
|
||||||
|
if _, found := p.GetInterfaceSection(); !found {
|
||||||
|
completions = append(completions, getHeaderCompletion("Interface", fields.HeaderInterfaceEnum.Documentation))
|
||||||
|
}
|
||||||
|
|
||||||
|
completions = append(completions, getHeaderCompletion("Peer", fields.HeaderPeerEnum.Documentation))
|
||||||
|
|
||||||
|
return completions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCompletionsForSectionEmptyLine(
|
||||||
|
s parser.WireguardSection,
|
||||||
|
) ([]protocol.CompletionItem, error) {
|
||||||
|
if s.Name == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
options := make(map[string]docvalues.DocumentationValue)
|
||||||
|
|
||||||
|
switch *s.Name {
|
||||||
|
case "Interface":
|
||||||
|
maps.Copy(options, fields.InterfaceOptions)
|
||||||
|
|
||||||
|
// Remove existing options
|
||||||
|
for _, property := range s.Properties {
|
||||||
|
if _, found := fields.InterfaceAllowedDuplicateFields[property.Key.Name]; found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(options, property.Key.Name)
|
||||||
|
}
|
||||||
|
case "Peer":
|
||||||
|
maps.Copy(options, fields.PeerOptions)
|
||||||
|
|
||||||
|
// Remove existing options
|
||||||
|
for _, property := range s.Properties {
|
||||||
|
if _, found := fields.PeerAllowedDuplicateFields[property.Key.Name]; found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(options, property.Key.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kind := protocol.CompletionItemKindProperty
|
||||||
|
|
||||||
|
return utils.MapMapToSlice(
|
||||||
|
options,
|
||||||
|
func(optionName string, value docvalues.DocumentationValue) protocol.CompletionItem {
|
||||||
|
insertText := optionName + " = "
|
||||||
|
|
||||||
|
return protocol.CompletionItem{
|
||||||
|
Kind: &kind,
|
||||||
|
Documentation: value.Documentation,
|
||||||
|
Label: optionName,
|
||||||
|
InsertText: &insertText,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSeparatorCompletion(property parser.WireguardProperty, character uint32) ([]protocol.CompletionItem, error) {
|
||||||
|
var insertText string
|
||||||
|
|
||||||
|
if character == property.Key.Location.End {
|
||||||
|
insertText = property.Key.Name + " = "
|
||||||
|
} else {
|
||||||
|
insertText = "= "
|
||||||
|
}
|
||||||
|
|
||||||
|
kind := protocol.CompletionItemKindValue
|
||||||
|
|
||||||
|
return []protocol.CompletionItem{
|
||||||
|
{
|
||||||
|
Label: insertText,
|
||||||
|
InsertText: &insertText,
|
||||||
|
Kind: &kind,
|
||||||
|
},
|
||||||
|
}, parser.PropertyNotFullyTypedError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCompletionsForSectionPropertyLine(
|
||||||
|
s parser.WireguardSection,
|
||||||
|
lineNumber uint32,
|
||||||
|
character uint32,
|
||||||
|
) ([]protocol.CompletionItem, error) {
|
||||||
|
property, err := s.GetPropertyByLine(lineNumber)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Name == nil {
|
||||||
|
return nil, parser.PropertyNotFoundError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
options, found := fields.OptionsHeaderMap[*s.Name]
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return nil, parser.PropertyNotFoundError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if property.Separator == nil {
|
||||||
|
if _, found := options[property.Key.Name]; found {
|
||||||
|
return GetSeparatorCompletion(*property, character)
|
||||||
|
}
|
||||||
|
// Get empty line completions
|
||||||
|
return nil, parser.PropertyNotFullyTypedError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
option, found := options[property.Key.Name]
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
if character < property.Separator.Location.Start {
|
||||||
|
return nil, parser.PropertyNotFullyTypedError{}
|
||||||
|
} else {
|
||||||
|
return nil, parser.PropertyNotFoundError{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if property.Value == nil {
|
||||||
|
if character >= property.Separator.Location.End {
|
||||||
|
return option.FetchCompletions("", 0), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
relativeCursor := character - property.Value.Location.Start
|
||||||
|
|
||||||
|
return option.FetchCompletions(property.Value.Value, relativeCursor), nil
|
||||||
|
}
|
@ -1,46 +1,51 @@
|
|||||||
package wireguard
|
package handlers
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"config-lsp/handlers/wireguard/fields"
|
||||||
|
"config-lsp/handlers/wireguard/parser"
|
||||||
|
"config-lsp/utils"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestSimplePropertyInInterface(
|
func TestSimplePropertyInInterface(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
|
|
||||||
`)
|
`)
|
||||||
parser := createWireguardParser()
|
p := parser.CreateWireguardParser()
|
||||||
parser.parseFromString(sample)
|
p.ParseFromString(sample)
|
||||||
|
|
||||||
completions, err := parser.Sections[0].getCompletionsForEmptyLine()
|
completions, err := GetCompletionsForSectionEmptyLine(*p.Sections[0])
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("getCompletionsForEmptyLine failed with error: %v", err)
|
t.Fatalf("getCompletionsForEmptyLine failed with error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(completions) != len(interfaceOptions) {
|
if len(completions) != len(fields.InterfaceOptions) {
|
||||||
t.Fatalf("getCompletionsForEmptyLine: Expected %v completions, but got %v", len(interfaceOptions), len(completions))
|
t.Fatalf("getCompletionsForEmptyLine: Expected %v completions, but got %v", len(fields.InterfaceOptions), len(completions))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSimpleOneExistingPropertyInInterface(
|
func TestSimpleOneExistingPropertyInInterface(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
PrivateKey = 1234567890
|
PrivateKey = 1234567890
|
||||||
|
|
||||||
`)
|
`)
|
||||||
parser := createWireguardParser()
|
p := parser.CreateWireguardParser()
|
||||||
parser.parseFromString(sample)
|
p.ParseFromString(sample)
|
||||||
|
|
||||||
completions, err := parser.Sections[0].getCompletionsForEmptyLine()
|
completions, err := GetCompletionsForSectionEmptyLine(*p.Sections[0])
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("getCompletionsForEmptyLine failed with error: %v", err)
|
t.Fatalf("getCompletionsForEmptyLine failed with error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := len(interfaceOptions) - 1
|
expected := len(fields.InterfaceOptions) - 1
|
||||||
if len(completions) != expected {
|
if len(completions) != expected {
|
||||||
t.Fatalf("getCompletionsForEmptyLine: Expected %v completions, but got %v", expected, len(completions))
|
t.Fatalf("getCompletionsForEmptyLine: Expected %v completions, but got %v", expected, len(completions))
|
||||||
}
|
}
|
||||||
@ -49,13 +54,13 @@ PrivateKey = 1234567890
|
|||||||
func TestEmptyRootCompletionsWork(
|
func TestEmptyRootCompletionsWork(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
`)
|
`)
|
||||||
|
|
||||||
parser := createWireguardParser()
|
p := parser.CreateWireguardParser()
|
||||||
parser.parseFromString(sample)
|
p.ParseFromString(sample)
|
||||||
|
|
||||||
completions := parser.getRootCompletionsForEmptyLine()
|
completions, _ := GetRootCompletionsForEmptyLine(p)
|
||||||
|
|
||||||
if len(completions) != 2 {
|
if len(completions) != 2 {
|
||||||
t.Fatalf("getRootCompletionsForEmptyLine: Expected 2 completions, but got %v", len(completions))
|
t.Fatalf("getRootCompletionsForEmptyLine: Expected 2 completions, but got %v", len(completions))
|
||||||
@ -65,14 +70,14 @@ func TestEmptyRootCompletionsWork(
|
|||||||
func TestInterfaceSectionRootCompletionsBeforeWork(
|
func TestInterfaceSectionRootCompletionsBeforeWork(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
|
|
||||||
[Interface]
|
[Interface]
|
||||||
`)
|
`)
|
||||||
parser := createWireguardParser()
|
p := parser.CreateWireguardParser()
|
||||||
parser.parseFromString(sample)
|
p.ParseFromString(sample)
|
||||||
|
|
||||||
completions := parser.getRootCompletionsForEmptyLine()
|
completions, _ := GetRootCompletionsForEmptyLine(p)
|
||||||
|
|
||||||
if len(completions) != 1 {
|
if len(completions) != 1 {
|
||||||
t.Fatalf("getRootCompletionsForEmptyLine: Expected 1 completions, but got %v", len(completions))
|
t.Fatalf("getRootCompletionsForEmptyLine: Expected 1 completions, but got %v", len(completions))
|
||||||
@ -82,15 +87,15 @@ func TestInterfaceSectionRootCompletionsBeforeWork(
|
|||||||
func TestInterfaceAndPeerSectionRootCompletionsWork(
|
func TestInterfaceAndPeerSectionRootCompletionsWork(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
|
|
||||||
[Peer]
|
[Peer]
|
||||||
`)
|
`)
|
||||||
parser := createWireguardParser()
|
p := parser.CreateWireguardParser()
|
||||||
parser.parseFromString(sample)
|
p.ParseFromString(sample)
|
||||||
|
|
||||||
completions := parser.getRootCompletionsForEmptyLine()
|
completions, _ := GetRootCompletionsForEmptyLine(p)
|
||||||
|
|
||||||
if len(completions) != 1 {
|
if len(completions) != 1 {
|
||||||
t.Fatalf("getRootCompletionsForEmptyLine: Expected 1 completions, but got %v", len(completions))
|
t.Fatalf("getRootCompletionsForEmptyLine: Expected 1 completions, but got %v", len(completions))
|
||||||
@ -100,14 +105,14 @@ func TestInterfaceAndPeerSectionRootCompletionsWork(
|
|||||||
func TestPropertyNoSepatorShouldCompleteSeparator(
|
func TestPropertyNoSepatorShouldCompleteSeparator(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
DNS
|
DNS
|
||||||
`)
|
`)
|
||||||
parser := createWireguardParser()
|
p := parser.CreateWireguardParser()
|
||||||
parser.parseFromString(sample)
|
p.ParseFromString(sample)
|
||||||
|
|
||||||
completions, err := parser.Sections[0].getCompletionsForPropertyLine(1, 3)
|
completions, err := GetCompletionsForSectionPropertyLine(*p.Sections[0], 1, 3)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("getCompletionsForPropertyLine err is nil but should not be")
|
t.Fatalf("getCompletionsForPropertyLine err is nil but should not be")
|
||||||
@ -125,14 +130,14 @@ DNS
|
|||||||
func TestPropertyNoSeparatorWithSpaceShouldCompleteSeparator(
|
func TestPropertyNoSeparatorWithSpaceShouldCompleteSeparator(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
DNS
|
DNS
|
||||||
`)
|
`)
|
||||||
parser := createWireguardParser()
|
p := parser.CreateWireguardParser()
|
||||||
parser.parseFromString(sample)
|
p.ParseFromString(sample)
|
||||||
|
|
||||||
completions, err := parser.Sections[0].getCompletionsForPropertyLine(1, 4)
|
completions, err := GetCompletionsForSectionPropertyLine(*p.Sections[0], 1, 4)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("getCompletionsForPropertyLine err is nil but should not be")
|
t.Fatalf("getCompletionsForPropertyLine err is nil but should not be")
|
||||||
@ -150,20 +155,20 @@ DNS
|
|||||||
func TestHeaderButNoProperty(
|
func TestHeaderButNoProperty(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
|
|
||||||
`)
|
`)
|
||||||
parser := createWireguardParser()
|
p := parser.CreateWireguardParser()
|
||||||
parser.parseFromString(sample)
|
p.ParseFromString(sample)
|
||||||
|
|
||||||
completions, err := parser.Sections[0].getCompletionsForEmptyLine()
|
completions, err := GetCompletionsForSectionEmptyLine(*p.Sections[0])
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("getCompletionsForEmptyLine failed with error: %v", err)
|
t.Fatalf("getCompletionsForEmptyLine failed with error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(completions) != len(interfaceOptions) {
|
if len(completions) != len(fields.InterfaceOptions) {
|
||||||
t.Fatalf("getCompletionsForEmptyLine: Expected %v completions, but got %v", len(interfaceOptions), len(completions))
|
t.Fatalf("getCompletionsForEmptyLine: Expected %v completions, but got %v", len(fields.InterfaceOptions), len(completions))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,22 +1,26 @@
|
|||||||
package wireguard
|
package handlers
|
||||||
|
|
||||||
import protocol "github.com/tliron/glsp/protocol_3_16"
|
import (
|
||||||
|
"config-lsp/handlers/wireguard/commands"
|
||||||
|
"config-lsp/handlers/wireguard/parser"
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
func getKeepaliveCodeActions(
|
func GetKeepaliveCodeActions(
|
||||||
|
p *parser.WireguardParser,
|
||||||
params *protocol.CodeActionParams,
|
params *protocol.CodeActionParams,
|
||||||
parser *wireguardParser,
|
|
||||||
) []protocol.CodeAction {
|
) []protocol.CodeAction {
|
||||||
line := params.Range.Start.Line
|
line := params.Range.Start.Line
|
||||||
|
|
||||||
for index, section := range parser.Sections {
|
for index, section := range p.Sections {
|
||||||
if section.StartLine >= line && line <= section.EndLine && section.Name != nil && *section.Name == "Peer" {
|
if section.StartLine >= line && line <= section.EndLine && section.Name != nil && *section.Name == "Peer" {
|
||||||
if section.existsProperty("Endpoint") && !section.existsProperty("PersistentKeepalive") {
|
if section.ExistsProperty("Endpoint") && !section.ExistsProperty("PersistentKeepalive") {
|
||||||
commandID := "wireguard." + codeActionAddKeepalive
|
commandID := "wireguard." + CodeActionAddKeepalive
|
||||||
command := protocol.Command{
|
command := protocol.Command{
|
||||||
Title: "Add PersistentKeepalive",
|
Title: "Add PersistentKeepalive",
|
||||||
Command: string(commandID),
|
Command: string(commandID),
|
||||||
Arguments: []any{
|
Arguments: []any{
|
||||||
codeActionAddKeepaliveArgs{
|
CodeActionAddKeepaliveArgs{
|
||||||
URI: params.TextDocument.URI,
|
URI: params.TextDocument.URI,
|
||||||
SectionIndex: uint32(index),
|
SectionIndex: uint32(index),
|
||||||
},
|
},
|
||||||
@ -36,12 +40,12 @@ func getKeepaliveCodeActions(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getKeyGenerationCodeActions(
|
func GetKeyGenerationCodeActions(
|
||||||
|
p *parser.WireguardParser,
|
||||||
params *protocol.CodeActionParams,
|
params *protocol.CodeActionParams,
|
||||||
parser *wireguardParser,
|
|
||||||
) []protocol.CodeAction {
|
) []protocol.CodeAction {
|
||||||
line := params.Range.Start.Line
|
line := params.Range.Start.Line
|
||||||
section, property := parser.getPropertyByLine(line)
|
section, property := p.GetPropertyByLine(line)
|
||||||
|
|
||||||
if section == nil || property == nil || property.Separator == nil {
|
if section == nil || property == nil || property.Separator == nil {
|
||||||
return nil
|
return nil
|
||||||
@ -49,16 +53,16 @@ func getKeyGenerationCodeActions(
|
|||||||
|
|
||||||
switch property.Key.Name {
|
switch property.Key.Name {
|
||||||
case "PrivateKey":
|
case "PrivateKey":
|
||||||
if !areWireguardToolsAvailable() {
|
if !wgcommands.AreWireguardToolsAvailable() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
commandID := "wireguard." + codeActionGeneratePrivateKey
|
commandID := "wireguard." + CodeActionGeneratePrivateKey
|
||||||
command := protocol.Command{
|
command := protocol.Command{
|
||||||
Title: "Generate Private Key",
|
Title: "Generate Private Key",
|
||||||
Command: string(commandID),
|
Command: string(commandID),
|
||||||
Arguments: []any{
|
Arguments: []any{
|
||||||
codeActionGeneratePrivateKeyArgs{
|
CodeActionGeneratePrivateKeyArgs{
|
||||||
URI: params.TextDocument.URI,
|
URI: params.TextDocument.URI,
|
||||||
Line: line,
|
Line: line,
|
||||||
},
|
},
|
||||||
@ -72,16 +76,16 @@ func getKeyGenerationCodeActions(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
case "PresharedKey":
|
case "PresharedKey":
|
||||||
if !areWireguardToolsAvailable() {
|
if !wgcommands.AreWireguardToolsAvailable() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
commandID := "wireguard." + codeActionGeneratePresharedKey
|
commandID := "wireguard." + CodeActionGeneratePresharedKey
|
||||||
command := protocol.Command{
|
command := protocol.Command{
|
||||||
Title: "Generate PresharedKey",
|
Title: "Generate PresharedKey",
|
||||||
Command: string(commandID),
|
Command: string(commandID),
|
||||||
Arguments: []any{
|
Arguments: []any{
|
||||||
codeActionGeneratePresharedKeyArgs{
|
CodeActionGeneratePresharedKeyArgs{
|
||||||
URI: params.TextDocument.URI,
|
URI: params.TextDocument.URI,
|
||||||
Line: line,
|
Line: line,
|
||||||
},
|
},
|
@ -1,14 +1,20 @@
|
|||||||
package wireguard
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
docvalues "config-lsp/doc-values"
|
docvalues "config-lsp/doc-values"
|
||||||
|
"config-lsp/handlers/wireguard/fields"
|
||||||
|
"config-lsp/handlers/wireguard/parser"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p wireguardProperty) getHoverInfo(cursor uint32, section *wireguardSection) []string {
|
func getPropertyInfo(
|
||||||
|
p parser.WireguardProperty,
|
||||||
|
cursor uint32,
|
||||||
|
section parser.WireguardSection,
|
||||||
|
) []string {
|
||||||
if cursor <= p.Key.Location.End {
|
if cursor <= p.Key.Location.End {
|
||||||
options, found := optionsHeaderMap[*section.Name]
|
options, found := fields.OptionsHeaderMap[*section.Name]
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
return []string{}
|
return []string{}
|
||||||
@ -23,7 +29,7 @@ func (p wireguardProperty) getHoverInfo(cursor uint32, section *wireguardSection
|
|||||||
return strings.Split(option.Documentation, "\n")
|
return strings.Split(option.Documentation, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
options, found := optionsHeaderMap[*section.Name]
|
options, found := fields.OptionsHeaderMap[*section.Name]
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
return []string{}
|
return []string{}
|
||||||
@ -36,22 +42,54 @@ func (p wireguardProperty) getHoverInfo(cursor uint32, section *wireguardSection
|
|||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p wireguardParser) getHeaderInfo(line uint32, cursor uint32) []string {
|
func getSectionInfo(s parser.WireguardSection) []string {
|
||||||
section := p.getSectionByLine(line)
|
if s.Name == nil {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
contents := []string{
|
||||||
|
fmt.Sprintf("## [%s]", *s.Name),
|
||||||
|
"",
|
||||||
|
}
|
||||||
|
|
||||||
|
var option *docvalues.EnumString = nil
|
||||||
|
|
||||||
|
switch *s.Name {
|
||||||
|
case "Interface":
|
||||||
|
option = &fields.HeaderInterfaceEnum
|
||||||
|
case "Peer":
|
||||||
|
option = &fields.HeaderPeerEnum
|
||||||
|
}
|
||||||
|
|
||||||
|
if option == nil {
|
||||||
|
return contents
|
||||||
|
}
|
||||||
|
|
||||||
|
contents = append(contents, strings.Split(option.Documentation, "\n")...)
|
||||||
|
|
||||||
|
return contents
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHoverContent(
|
||||||
|
p parser.WireguardParser,
|
||||||
|
line uint32,
|
||||||
|
cursor uint32,
|
||||||
|
) []string {
|
||||||
|
section := p.GetSectionByLine(line)
|
||||||
|
|
||||||
if section == nil {
|
if section == nil {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
sectionInfo := section.getHeaderInfo()
|
sectionInfo := getSectionInfo(*section)
|
||||||
|
|
||||||
property, _ := section.findProperty(line)
|
property, _ := section.GetPropertyByLine(line)
|
||||||
|
|
||||||
if property == nil {
|
if property == nil {
|
||||||
return sectionInfo
|
return sectionInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyInfo := property.getHoverInfo(cursor, section)
|
propertyInfo := getPropertyInfo(*property, cursor, *section)
|
||||||
|
|
||||||
if len(propertyInfo) == 0 {
|
if len(propertyInfo) == 0 {
|
||||||
return sectionInfo
|
return sectionInfo
|
||||||
@ -65,31 +103,3 @@ func (p wireguardParser) getHeaderInfo(line uint32, cursor uint32) []string {
|
|||||||
|
|
||||||
return contents
|
return contents
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p wireguardSection) getHeaderInfo() []string {
|
|
||||||
if p.Name == nil {
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
contents := []string{
|
|
||||||
fmt.Sprintf("## [%s]", *p.Name),
|
|
||||||
"",
|
|
||||||
}
|
|
||||||
|
|
||||||
var option *docvalues.EnumString = nil
|
|
||||||
|
|
||||||
switch *p.Name {
|
|
||||||
case "Interface":
|
|
||||||
option = &headerInterfaceEnum
|
|
||||||
case "Peer":
|
|
||||||
option = &headerPeerEnum
|
|
||||||
}
|
|
||||||
|
|
||||||
if option == nil {
|
|
||||||
return contents
|
|
||||||
}
|
|
||||||
|
|
||||||
contents = append(contents, strings.Split(option.Documentation, "\n")...)
|
|
||||||
|
|
||||||
return contents
|
|
||||||
}
|
|
8
handlers/wireguard/lsp/shared.go
Normal file
8
handlers/wireguard/lsp/shared.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package lsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/handlers/wireguard/parser"
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
var documentParserMap = map[protocol.DocumentUri]*parser.WireguardParser{}
|
@ -1,17 +1,18 @@
|
|||||||
package wireguard
|
package lsp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"config-lsp/handlers/wireguard/handlers"
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
|
func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
|
||||||
parser := documentParserMap[params.TextDocument.URI]
|
p := documentParserMap[params.TextDocument.URI]
|
||||||
|
|
||||||
actions := make([]protocol.CodeAction, 0, 2)
|
actions := make([]protocol.CodeAction, 0, 2)
|
||||||
|
|
||||||
actions = append(actions, getKeyGenerationCodeActions(params, parser)...)
|
actions = append(actions, handlers.GetKeyGenerationCodeActions(p, params)...)
|
||||||
actions = append(actions, getKeepaliveCodeActions(params, parser)...)
|
actions = append(actions, handlers.GetKeepaliveCodeActions(p, params)...)
|
||||||
|
|
||||||
if len(actions) > 0 {
|
if len(actions) > 0 {
|
||||||
return actions, nil
|
return actions, nil
|
47
handlers/wireguard/lsp/text-document-completion.go
Normal file
47
handlers/wireguard/lsp/text-document-completion.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package lsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/handlers/wireguard/handlers"
|
||||||
|
"config-lsp/handlers/wireguard/parser"
|
||||||
|
"github.com/tliron/glsp"
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
|
||||||
|
p := documentParserMap[params.TextDocument.URI]
|
||||||
|
|
||||||
|
lineNumber := params.Position.Line
|
||||||
|
|
||||||
|
section := p.GetBelongingSectionByLine(lineNumber)
|
||||||
|
lineType := p.GetTypeByLine(lineNumber)
|
||||||
|
|
||||||
|
switch lineType {
|
||||||
|
case parser.LineTypeComment:
|
||||||
|
return nil, nil
|
||||||
|
case parser.LineTypeHeader:
|
||||||
|
return handlers.GetRootCompletionsForEmptyLine(*p)
|
||||||
|
case parser.LineTypeEmpty:
|
||||||
|
if section.Name == nil {
|
||||||
|
// Root completions
|
||||||
|
return handlers.GetRootCompletionsForEmptyLine(*p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return handlers.GetCompletionsForSectionEmptyLine(*section)
|
||||||
|
case parser.LineTypeProperty:
|
||||||
|
completions, err := handlers.GetCompletionsForSectionPropertyLine(*section, lineNumber, params.Position.Character)
|
||||||
|
|
||||||
|
if completions == nil && err != nil {
|
||||||
|
switch err.(type) {
|
||||||
|
// Ignore
|
||||||
|
case parser.PropertyNotFullyTypedError:
|
||||||
|
return handlers.GetCompletionsForSectionEmptyLine(*section)
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return completions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("TextDocumentCompletion: unexpected line type")
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
package wireguard
|
package lsp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
|
"config-lsp/handlers/wireguard/handlers"
|
||||||
"config-lsp/utils"
|
"config-lsp/utils"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
@ -15,11 +16,11 @@ func TextDocumentDidChange(
|
|||||||
content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text
|
content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text
|
||||||
common.ClearDiagnostics(context, params.TextDocument.URI)
|
common.ClearDiagnostics(context, params.TextDocument.URI)
|
||||||
|
|
||||||
parser := documentParserMap[params.TextDocument.URI]
|
p := documentParserMap[params.TextDocument.URI]
|
||||||
parser.clear()
|
p.Clear()
|
||||||
|
|
||||||
diagnostics := make([]protocol.Diagnostic, 0)
|
diagnostics := make([]protocol.Diagnostic, 0)
|
||||||
errors := parser.parseFromString(content)
|
errors := p.ParseFromString(content)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
diagnostics = append(diagnostics, utils.Map(
|
diagnostics = append(diagnostics, utils.Map(
|
||||||
@ -30,7 +31,7 @@ func TextDocumentDidChange(
|
|||||||
)...)
|
)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics = append(diagnostics, parser.analyze()...)
|
diagnostics = append(diagnostics, handlers.Analyze(*p)...)
|
||||||
|
|
||||||
if len(diagnostics) > 0 {
|
if len(diagnostics) > 0 {
|
||||||
common.SendDiagnostics(context, params.TextDocument.URI, diagnostics)
|
common.SendDiagnostics(context, params.TextDocument.URI, diagnostics)
|
@ -1,4 +1,4 @@
|
|||||||
package wireguard
|
package lsp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
@ -1,7 +1,8 @@
|
|||||||
package wireguard
|
package lsp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
|
"config-lsp/handlers/wireguard/parser"
|
||||||
"config-lsp/utils"
|
"config-lsp/utils"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
@ -14,10 +15,10 @@ func TextDocumentDidOpen(
|
|||||||
) error {
|
) error {
|
||||||
common.ClearDiagnostics(context, params.TextDocument.URI)
|
common.ClearDiagnostics(context, params.TextDocument.URI)
|
||||||
|
|
||||||
parser := createWireguardParser()
|
p := parser.CreateWireguardParser()
|
||||||
documentParserMap[params.TextDocument.URI] = &parser
|
documentParserMap[params.TextDocument.URI] = &p
|
||||||
|
|
||||||
errors := parser.parseFromString(params.TextDocument.Text)
|
errors := p.ParseFromString(params.TextDocument.Text)
|
||||||
|
|
||||||
diagnostics := utils.Map(
|
diagnostics := utils.Map(
|
||||||
errors,
|
errors,
|
@ -1,6 +1,8 @@
|
|||||||
package wireguard
|
package lsp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"config-lsp/handlers/wireguard/handlers"
|
||||||
|
"config-lsp/handlers/wireguard/parser"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
@ -11,17 +13,21 @@ func TextDocumentHover(
|
|||||||
context *glsp.Context,
|
context *glsp.Context,
|
||||||
params *protocol.HoverParams,
|
params *protocol.HoverParams,
|
||||||
) (*protocol.Hover, error) {
|
) (*protocol.Hover, error) {
|
||||||
parser := documentParserMap[params.TextDocument.URI]
|
p := documentParserMap[params.TextDocument.URI]
|
||||||
|
|
||||||
switch parser.getTypeByLine(params.Position.Line) {
|
switch p.GetTypeByLine(params.Position.Line) {
|
||||||
case LineTypeComment:
|
case parser.LineTypeComment:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case LineTypeEmpty:
|
case parser.LineTypeEmpty:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case LineTypeHeader:
|
case parser.LineTypeHeader:
|
||||||
fallthrough
|
fallthrough
|
||||||
case LineTypeProperty:
|
case parser.LineTypeProperty:
|
||||||
documentation := parser.getHeaderInfo(params.Position.Line, params.Position.Character)
|
documentation := handlers.GetHoverContent(
|
||||||
|
*p,
|
||||||
|
params.Position.Line,
|
||||||
|
params.Position.Character,
|
||||||
|
)
|
||||||
|
|
||||||
hover := protocol.Hover{
|
hover := protocol.Hover{
|
||||||
Contents: protocol.MarkupContent{
|
Contents: protocol.MarkupContent{
|
36
handlers/wireguard/lsp/workspace-execute-command.go
Normal file
36
handlers/wireguard/lsp/workspace-execute-command.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package lsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/handlers/wireguard/handlers"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tliron/glsp"
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func WorkspaceExecuteCommand(context *glsp.Context, params *protocol.ExecuteCommandParams) (*protocol.ApplyWorkspaceEditParams, error) {
|
||||||
|
_, command, _ := strings.Cut(params.Command, ".")
|
||||||
|
|
||||||
|
switch command {
|
||||||
|
case string(handlers.CodeActionGeneratePrivateKey):
|
||||||
|
args := handlers.CodeActionGeneratePrivateKeyArgsFromArguments(params.Arguments[0].(map[string]any))
|
||||||
|
|
||||||
|
p := documentParserMap[args.URI]
|
||||||
|
|
||||||
|
return args.RunCommand(p)
|
||||||
|
case string(handlers.CodeActionGeneratePresharedKey):
|
||||||
|
args := handlers.CodeActionGeneratePresharedKeyArgsFromArguments(params.Arguments[0].(map[string]any))
|
||||||
|
|
||||||
|
parser := documentParserMap[args.URI]
|
||||||
|
|
||||||
|
return args.RunCommand(parser)
|
||||||
|
case string(handlers.CodeActionAddKeepalive):
|
||||||
|
args := handlers.CodeActionAddKeepaliveArgsFromArguments(params.Arguments[0].(map[string]any))
|
||||||
|
|
||||||
|
p := documentParserMap[args.URI]
|
||||||
|
|
||||||
|
return args.RunCommand(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
@ -1,11 +1,14 @@
|
|||||||
package wireguard
|
package parser
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"config-lsp/utils"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestGetLineTypeWorksCorrectly(
|
func TestGetLineTypeWorksCorrectly(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
# A comment at the very top
|
# A comment at the very top
|
||||||
Test=Hello
|
Test=Hello
|
||||||
|
|
||||||
@ -21,35 +24,35 @@ PublicKey = 1234567890
|
|||||||
; I'm a comment
|
; I'm a comment
|
||||||
`)
|
`)
|
||||||
|
|
||||||
parser := createWireguardParser()
|
parser := CreateWireguardParser()
|
||||||
parser.parseFromString(sample)
|
parser.ParseFromString(sample)
|
||||||
|
|
||||||
lineType := parser.getTypeByLine(0)
|
lineType := parser.GetTypeByLine(0)
|
||||||
if lineType != LineTypeComment {
|
if lineType != LineTypeComment {
|
||||||
t.Fatalf("getTypeByLine: Expected line 0 to be a comment, but it is %v", lineType)
|
t.Fatalf("getTypeByLine: Expected line 0 to be a comment, but it is %v", lineType)
|
||||||
}
|
}
|
||||||
|
|
||||||
lineType = parser.getTypeByLine(1)
|
lineType = parser.GetTypeByLine(1)
|
||||||
if lineType != LineTypeProperty {
|
if lineType != LineTypeProperty {
|
||||||
t.Fatalf("getTypeByLine: Expected line 1 to be a property, but it is %v", lineType)
|
t.Fatalf("getTypeByLine: Expected line 1 to be a property, but it is %v", lineType)
|
||||||
}
|
}
|
||||||
|
|
||||||
lineType = parser.getTypeByLine(2)
|
lineType = parser.GetTypeByLine(2)
|
||||||
if lineType != LineTypeEmpty {
|
if lineType != LineTypeEmpty {
|
||||||
t.Fatalf("getTypeByLine: Expected line 2 to be empty, but it is %v", lineType)
|
t.Fatalf("getTypeByLine: Expected line 2 to be empty, but it is %v", lineType)
|
||||||
}
|
}
|
||||||
|
|
||||||
lineType = parser.getTypeByLine(3)
|
lineType = parser.GetTypeByLine(3)
|
||||||
if lineType != LineTypeHeader {
|
if lineType != LineTypeHeader {
|
||||||
t.Fatalf("getTypeByLine: Expected line 3 to be a header, but it is %v", lineType)
|
t.Fatalf("getTypeByLine: Expected line 3 to be a header, but it is %v", lineType)
|
||||||
}
|
}
|
||||||
|
|
||||||
lineType = parser.getTypeByLine(4)
|
lineType = parser.GetTypeByLine(4)
|
||||||
if lineType != LineTypeProperty {
|
if lineType != LineTypeProperty {
|
||||||
t.Fatalf("getTypeByLine: Expected line 4 to be a property, but it is %v", lineType)
|
t.Fatalf("getTypeByLine: Expected line 4 to be a property, but it is %v", lineType)
|
||||||
}
|
}
|
||||||
|
|
||||||
lineType = parser.getTypeByLine(12)
|
lineType = parser.GetTypeByLine(12)
|
||||||
if lineType != LineTypeComment {
|
if lineType != LineTypeComment {
|
||||||
t.Fatalf("getTypeByLine: Expected line 12 to be a comment, but it is %v", lineType)
|
t.Fatalf("getTypeByLine: Expected line 12 to be a comment, but it is %v", lineType)
|
||||||
}
|
}
|
||||||
@ -58,7 +61,7 @@ PublicKey = 1234567890
|
|||||||
func TestGetBelongingSectionWorksCorrectly(
|
func TestGetBelongingSectionWorksCorrectly(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
# A comment at the very top
|
# A comment at the very top
|
||||||
Test=Hello
|
Test=Hello
|
||||||
|
|
||||||
@ -74,48 +77,48 @@ PublicKey = 1234567890
|
|||||||
; I'm a comment
|
; I'm a comment
|
||||||
`)
|
`)
|
||||||
|
|
||||||
parser := createWireguardParser()
|
parser := CreateWireguardParser()
|
||||||
parser.parseFromString(sample)
|
parser.ParseFromString(sample)
|
||||||
|
|
||||||
section := parser.getBelongingSectionByLine(0)
|
section := parser.GetBelongingSectionByLine(0)
|
||||||
|
|
||||||
// Comment
|
// Comment
|
||||||
if section != nil {
|
if section != nil {
|
||||||
t.Fatalf("getBelongingSectionByLine: Expected line 0 to be in no section, but it is in %v", section)
|
t.Fatalf("getBelongingSectionByLine: Expected line 0 to be in no section, but it is in %v", section)
|
||||||
}
|
}
|
||||||
|
|
||||||
section = parser.getBelongingSectionByLine(1)
|
section = parser.GetBelongingSectionByLine(1)
|
||||||
|
|
||||||
if section != parser.Sections[1] {
|
if section != parser.Sections[1] {
|
||||||
t.Fatalf("getBelongingSectionByLine: Expected line 1 to be in global section, but it is in %v", section)
|
t.Fatalf("getBelongingSectionByLine: Expected line 1 to be in global section, but it is in %v", section)
|
||||||
}
|
}
|
||||||
|
|
||||||
section = parser.getBelongingSectionByLine(2)
|
section = parser.GetBelongingSectionByLine(2)
|
||||||
if section != parser.Sections[1] {
|
if section != parser.Sections[1] {
|
||||||
t.Fatalf("getBelongingSectionByLine: Expected line 2 to be in global section, but it is in %v", section)
|
t.Fatalf("getBelongingSectionByLine: Expected line 2 to be in global section, but it is in %v", section)
|
||||||
}
|
}
|
||||||
|
|
||||||
section = parser.getBelongingSectionByLine(3)
|
section = parser.GetBelongingSectionByLine(3)
|
||||||
if section != parser.Sections[2] {
|
if section != parser.Sections[2] {
|
||||||
t.Fatalf("getBelongingSectionByLine: Expected line 3 to be in section Interface, but it is in %v", section)
|
t.Fatalf("getBelongingSectionByLine: Expected line 3 to be in section Interface, but it is in %v", section)
|
||||||
}
|
}
|
||||||
|
|
||||||
section = parser.getBelongingSectionByLine(4)
|
section = parser.GetBelongingSectionByLine(4)
|
||||||
if section != parser.Sections[2] {
|
if section != parser.Sections[2] {
|
||||||
t.Fatalf("getBelongingSectionByLine: Expected line 4 to be in section Interface, but it is in %v", section)
|
t.Fatalf("getBelongingSectionByLine: Expected line 4 to be in section Interface, but it is in %v", section)
|
||||||
}
|
}
|
||||||
|
|
||||||
section = parser.getBelongingSectionByLine(6)
|
section = parser.GetBelongingSectionByLine(6)
|
||||||
if section != parser.Sections[2] {
|
if section != parser.Sections[2] {
|
||||||
t.Fatalf("getBelongingSectionByLine: Expected line 6 to be in section Interface, but it is in %v", section)
|
t.Fatalf("getBelongingSectionByLine: Expected line 6 to be in section Interface, but it is in %v", section)
|
||||||
}
|
}
|
||||||
|
|
||||||
section = parser.getBelongingSectionByLine(10)
|
section = parser.GetBelongingSectionByLine(10)
|
||||||
if section != parser.Sections[3] {
|
if section != parser.Sections[3] {
|
||||||
t.Fatalf("getBelongingSectionByLine: Expected line 10 to be in section Peer, but it is in %v", section)
|
t.Fatalf("getBelongingSectionByLine: Expected line 10 to be in section Peer, but it is in %v", section)
|
||||||
}
|
}
|
||||||
|
|
||||||
section = parser.getBelongingSectionByLine(12)
|
section = parser.GetBelongingSectionByLine(12)
|
||||||
|
|
||||||
// Comment
|
// Comment
|
||||||
if section != nil {
|
if section != nil {
|
@ -1,138 +1,43 @@
|
|||||||
package wireguard
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var commentPattern = regexp.MustCompile(`^\s*(;|#)`)
|
var commentPattern = regexp.MustCompile(`^\s*(;|#)`)
|
||||||
var emptyLinePattern = regexp.MustCompile(`^\s*$`)
|
var emptyLinePattern = regexp.MustCompile(`^\s*$`)
|
||||||
var headerPattern = regexp.MustCompile(`^\s*\[`)
|
var headerPattern = regexp.MustCompile(`^\s*\[`)
|
||||||
|
|
||||||
type characterLocation struct {
|
type CharacterLocation struct {
|
||||||
Start uint32
|
Start uint32
|
||||||
End uint32
|
End uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type wireguardLineIndex struct {
|
type wireguardLineIndex struct {
|
||||||
Type lineType
|
Type LineType
|
||||||
BelongingSection *wireguardSection
|
BelongingSection *WireguardSection
|
||||||
}
|
}
|
||||||
|
|
||||||
type wireguardParser struct {
|
type WireguardParser struct {
|
||||||
// <key = name>: if nil then does not belong to a section
|
// <key = name>: if nil then does not belong to a section
|
||||||
Sections []*wireguardSection
|
Sections []*WireguardSection
|
||||||
// Used to identify where not to show diagnostics
|
// Used to identify where not to show diagnostics
|
||||||
CommentLines map[uint32]struct{}
|
commentLines map[uint32]struct{}
|
||||||
|
|
||||||
// Indexes
|
// Indexes
|
||||||
LineIndexes map[uint32]wireguardLineIndex
|
linesIndexes map[uint32]wireguardLineIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *wireguardParser) getSectionByLine(line uint32) *wireguardSection {
|
func (p *WireguardParser) Clear() {
|
||||||
for _, section := range p.Sections {
|
p.Sections = []*WireguardSection{}
|
||||||
if section.StartLine <= line && section.EndLine >= line {
|
p.commentLines = map[uint32]struct{}{}
|
||||||
return section
|
p.linesIndexes = map[uint32]wireguardLineIndex{}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
func (p *WireguardParser) ParseFromString(input string) []common.ParseError {
|
||||||
}
|
|
||||||
|
|
||||||
// Search for a property by name
|
|
||||||
// Returns (line number, property)
|
|
||||||
func (p *wireguardParser) fetchPropertyByName(name string) (*uint32, *wireguardProperty) {
|
|
||||||
for _, section := range p.Sections {
|
|
||||||
for lineNumber, property := range section.Properties {
|
|
||||||
if property.Key.Name == name {
|
|
||||||
return &lineNumber, &property
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *wireguardParser) clear() {
|
|
||||||
p.Sections = []*wireguardSection{}
|
|
||||||
p.CommentLines = map[uint32]struct{}{}
|
|
||||||
p.LineIndexes = map[uint32]wireguardLineIndex{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p wireguardParser) getInterfaceSection() (*wireguardSection, bool) {
|
|
||||||
for _, section := range p.Sections {
|
|
||||||
if section.Name != nil && *section.Name == "Interface" {
|
|
||||||
return section, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func getHeaderCompletion(name string, documentation string) protocol.CompletionItem {
|
|
||||||
textFormat := protocol.InsertTextFormatPlainText
|
|
||||||
kind := protocol.CompletionItemKindEnum
|
|
||||||
|
|
||||||
insertText := "[" + name + "]\n"
|
|
||||||
|
|
||||||
return protocol.CompletionItem{
|
|
||||||
Label: "[" + name + "]",
|
|
||||||
InsertTextFormat: &textFormat,
|
|
||||||
InsertText: &insertText,
|
|
||||||
Kind: &kind,
|
|
||||||
Documentation: &documentation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p wireguardParser) getRootCompletionsForEmptyLine() []protocol.CompletionItem {
|
|
||||||
completions := []protocol.CompletionItem{}
|
|
||||||
|
|
||||||
if _, found := p.getInterfaceSection(); !found {
|
|
||||||
completions = append(completions, getHeaderCompletion("Interface", headerInterfaceEnum.Documentation))
|
|
||||||
}
|
|
||||||
|
|
||||||
completions = append(completions, getHeaderCompletion("Peer", headerPeerEnum.Documentation))
|
|
||||||
|
|
||||||
return completions
|
|
||||||
}
|
|
||||||
|
|
||||||
func createWireguardParser() wireguardParser {
|
|
||||||
parser := wireguardParser{}
|
|
||||||
parser.clear()
|
|
||||||
|
|
||||||
return parser
|
|
||||||
}
|
|
||||||
|
|
||||||
type lineType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
LineTypeComment lineType = "comment"
|
|
||||||
LineTypeEmpty lineType = "empty"
|
|
||||||
LineTypeHeader lineType = "header"
|
|
||||||
LineTypeProperty lineType = "property"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getLineType(line string) lineType {
|
|
||||||
if commentPattern.MatchString(line) {
|
|
||||||
return LineTypeComment
|
|
||||||
}
|
|
||||||
|
|
||||||
if emptyLinePattern.MatchString(line) {
|
|
||||||
return LineTypeEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
if headerPattern.MatchString(line) {
|
|
||||||
return LineTypeHeader
|
|
||||||
}
|
|
||||||
|
|
||||||
return LineTypeProperty
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *wireguardParser) parseFromString(input string) []common.ParseError {
|
|
||||||
var errors []common.ParseError
|
var errors []common.ParseError
|
||||||
lines := strings.Split(
|
lines := strings.Split(
|
||||||
input,
|
input,
|
||||||
@ -141,7 +46,7 @@ func (p *wireguardParser) parseFromString(input string) []common.ParseError {
|
|||||||
|
|
||||||
slices.Reverse(lines)
|
slices.Reverse(lines)
|
||||||
|
|
||||||
collectedProperties := wireguardProperties{}
|
collectedProperties := WireguardProperties{}
|
||||||
var lastPropertyLine *uint32
|
var lastPropertyLine *uint32
|
||||||
|
|
||||||
for index, line := range lines {
|
for index, line := range lines {
|
||||||
@ -150,8 +55,8 @@ func (p *wireguardParser) parseFromString(input string) []common.ParseError {
|
|||||||
|
|
||||||
switch lineType {
|
switch lineType {
|
||||||
case LineTypeComment:
|
case LineTypeComment:
|
||||||
p.CommentLines[currentLineNumber] = struct{}{}
|
p.commentLines[currentLineNumber] = struct{}{}
|
||||||
p.LineIndexes[currentLineNumber] = wireguardLineIndex{
|
p.linesIndexes[currentLineNumber] = wireguardLineIndex{
|
||||||
Type: LineTypeComment,
|
Type: LineTypeComment,
|
||||||
BelongingSection: nil,
|
BelongingSection: nil,
|
||||||
}
|
}
|
||||||
@ -184,7 +89,7 @@ func (p *wireguardParser) parseFromString(input string) []common.ParseError {
|
|||||||
lastLine = *lastPropertyLine
|
lastLine = *lastPropertyLine
|
||||||
}
|
}
|
||||||
|
|
||||||
section := createWireguardSection(
|
section := CreateWireguardSection(
|
||||||
currentLineNumber,
|
currentLineNumber,
|
||||||
lastLine,
|
lastLine,
|
||||||
line,
|
line,
|
||||||
@ -195,23 +100,23 @@ func (p *wireguardParser) parseFromString(input string) []common.ParseError {
|
|||||||
|
|
||||||
// Add indexes
|
// Add indexes
|
||||||
for lineNumber := range collectedProperties {
|
for lineNumber := range collectedProperties {
|
||||||
p.LineIndexes[lineNumber] = wireguardLineIndex{
|
p.linesIndexes[lineNumber] = wireguardLineIndex{
|
||||||
Type: LineTypeProperty,
|
Type: LineTypeProperty,
|
||||||
BelongingSection: §ion,
|
BelongingSection: §ion,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.LineIndexes[currentLineNumber] = wireguardLineIndex{
|
p.linesIndexes[currentLineNumber] = wireguardLineIndex{
|
||||||
Type: LineTypeHeader,
|
Type: LineTypeHeader,
|
||||||
BelongingSection: §ion,
|
BelongingSection: §ion,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset
|
// Reset
|
||||||
collectedProperties = wireguardProperties{}
|
collectedProperties = WireguardProperties{}
|
||||||
lastPropertyLine = nil
|
lastPropertyLine = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var emptySection *wireguardSection
|
var emptySection *WireguardSection
|
||||||
|
|
||||||
if len(collectedProperties) > 0 {
|
if len(collectedProperties) > 0 {
|
||||||
var endLine uint32
|
var endLine uint32
|
||||||
@ -222,7 +127,7 @@ func (p *wireguardParser) parseFromString(input string) []common.ParseError {
|
|||||||
endLine = p.Sections[len(p.Sections)-1].StartLine
|
endLine = p.Sections[len(p.Sections)-1].StartLine
|
||||||
}
|
}
|
||||||
|
|
||||||
emptySection = &wireguardSection{
|
emptySection = &WireguardSection{
|
||||||
StartLine: 0,
|
StartLine: 0,
|
||||||
EndLine: endLine,
|
EndLine: endLine,
|
||||||
Properties: collectedProperties,
|
Properties: collectedProperties,
|
||||||
@ -231,7 +136,7 @@ func (p *wireguardParser) parseFromString(input string) []common.ParseError {
|
|||||||
p.Sections = append(p.Sections, emptySection)
|
p.Sections = append(p.Sections, emptySection)
|
||||||
|
|
||||||
for lineNumber := range collectedProperties {
|
for lineNumber := range collectedProperties {
|
||||||
p.LineIndexes[lineNumber] = wireguardLineIndex{
|
p.linesIndexes[lineNumber] = wireguardLineIndex{
|
||||||
Type: LineTypeProperty,
|
Type: LineTypeProperty,
|
||||||
BelongingSection: emptySection,
|
BelongingSection: emptySection,
|
||||||
}
|
}
|
||||||
@ -247,7 +152,7 @@ func (p *wireguardParser) parseFromString(input string) []common.ParseError {
|
|||||||
|
|
||||||
// Add empty section
|
// Add empty section
|
||||||
if endLine != 0 {
|
if endLine != 0 {
|
||||||
emptySection = &wireguardSection{
|
emptySection = &WireguardSection{
|
||||||
StartLine: 0,
|
StartLine: 0,
|
||||||
EndLine: endLine,
|
EndLine: endLine,
|
||||||
Properties: collectedProperties,
|
Properties: collectedProperties,
|
||||||
@ -256,11 +161,11 @@ func (p *wireguardParser) parseFromString(input string) []common.ParseError {
|
|||||||
p.Sections = append(p.Sections, emptySection)
|
p.Sections = append(p.Sections, emptySection)
|
||||||
|
|
||||||
for newLine := uint32(0); newLine < endLine; newLine++ {
|
for newLine := uint32(0); newLine < endLine; newLine++ {
|
||||||
if _, found := p.LineIndexes[newLine]; found {
|
if _, found := p.linesIndexes[newLine]; found {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
p.LineIndexes[newLine] = wireguardLineIndex{
|
p.linesIndexes[newLine] = wireguardLineIndex{
|
||||||
Type: LineTypeEmpty,
|
Type: LineTypeEmpty,
|
||||||
BelongingSection: emptySection,
|
BelongingSection: emptySection,
|
||||||
}
|
}
|
||||||
@ -284,11 +189,11 @@ func (p *wireguardParser) parseFromString(input string) []common.ParseError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for newLine := section.StartLine; newLine < endLine; newLine++ {
|
for newLine := section.StartLine; newLine < endLine; newLine++ {
|
||||||
if _, found := p.LineIndexes[newLine]; found {
|
if _, found := p.linesIndexes[newLine]; found {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
p.LineIndexes[newLine] = wireguardLineIndex{
|
p.linesIndexes[newLine] = wireguardLineIndex{
|
||||||
Type: LineTypeEmpty,
|
Type: LineTypeEmpty,
|
||||||
BelongingSection: section,
|
BelongingSection: section,
|
||||||
}
|
}
|
||||||
@ -298,13 +203,47 @@ func (p *wireguardParser) parseFromString(input string) []common.ParseError {
|
|||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p wireguardParser) getTypeByLine(line uint32) lineType {
|
func (p *WireguardParser) GetSectionByLine(line uint32) *WireguardSection {
|
||||||
|
for _, section := range p.Sections {
|
||||||
|
if section.StartLine <= line && section.EndLine >= line {
|
||||||
|
return section
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for a property by name
|
||||||
|
// Returns (line number, property)
|
||||||
|
func (p *WireguardParser) FindFirstPropertyByName(name string) (*uint32, *WireguardProperty) {
|
||||||
|
for _, section := range p.Sections {
|
||||||
|
for lineNumber, property := range section.Properties {
|
||||||
|
if property.Key.Name == name {
|
||||||
|
return &lineNumber, &property
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p WireguardParser) GetInterfaceSection() (*WireguardSection, bool) {
|
||||||
|
for _, section := range p.Sections {
|
||||||
|
if section.Name != nil && *section.Name == "Interface" {
|
||||||
|
return section, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p WireguardParser) GetTypeByLine(line uint32) LineType {
|
||||||
// Check if line is a comment
|
// Check if line is a comment
|
||||||
if _, found := p.CommentLines[line]; found {
|
if _, found := p.commentLines[line]; found {
|
||||||
return LineTypeComment
|
return LineTypeComment
|
||||||
}
|
}
|
||||||
|
|
||||||
if info, found := p.LineIndexes[line]; found {
|
if info, found := p.linesIndexes[line]; found {
|
||||||
return info.Type
|
return info.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,22 +259,22 @@ func (p wireguardParser) getTypeByLine(line uint32) lineType {
|
|||||||
// [Peer]
|
// [Peer]
|
||||||
//
|
//
|
||||||
// This would return the section [Interface]
|
// This would return the section [Interface]
|
||||||
func (p *wireguardParser) getBelongingSectionByLine(line uint32) *wireguardSection {
|
func (p *WireguardParser) GetBelongingSectionByLine(line uint32) *WireguardSection {
|
||||||
if info, found := p.LineIndexes[line]; found {
|
if info, found := p.linesIndexes[line]; found {
|
||||||
return info.BelongingSection
|
return info.BelongingSection
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *wireguardParser) getPropertyByLine(line uint32) (*wireguardSection, *wireguardProperty) {
|
func (p *WireguardParser) GetPropertyByLine(line uint32) (*WireguardSection, *WireguardProperty) {
|
||||||
section := p.getSectionByLine(line)
|
section := p.GetSectionByLine(line)
|
||||||
|
|
||||||
if section == nil || section.Name == nil {
|
if section == nil || section.Name == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
property, _ := section.findProperty(line)
|
property, _ := section.GetPropertyByLine(line)
|
||||||
|
|
||||||
if property == nil {
|
if property == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -344,8 +283,8 @@ func (p *wireguardParser) getPropertyByLine(line uint32) (*wireguardSection, *wi
|
|||||||
return section, property
|
return section, property
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *wireguardParser) getSectionsByName(name string) []*wireguardSection {
|
func (p *WireguardParser) GetSectionsByName(name string) []*WireguardSection {
|
||||||
var sections []*wireguardSection
|
var sections []*WireguardSection
|
||||||
|
|
||||||
for _, section := range p.Sections {
|
for _, section := range p.Sections {
|
||||||
if section.Name != nil && *section.Name == name {
|
if section.Name != nil && *section.Name == name {
|
||||||
@ -355,3 +294,35 @@ func (p *wireguardParser) getSectionsByName(name string) []*wireguardSection {
|
|||||||
|
|
||||||
return sections
|
return sections
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateWireguardParser() WireguardParser {
|
||||||
|
parser := WireguardParser{}
|
||||||
|
parser.Clear()
|
||||||
|
|
||||||
|
return parser
|
||||||
|
}
|
||||||
|
|
||||||
|
type LineType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
LineTypeComment LineType = "comment"
|
||||||
|
LineTypeEmpty LineType = "empty"
|
||||||
|
LineTypeHeader LineType = "header"
|
||||||
|
LineTypeProperty LineType = "property"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getLineType(line string) LineType {
|
||||||
|
if commentPattern.MatchString(line) {
|
||||||
|
return LineTypeComment
|
||||||
|
}
|
||||||
|
|
||||||
|
if emptyLinePattern.MatchString(line) {
|
||||||
|
return LineTypeEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
if headerPattern.MatchString(line) {
|
||||||
|
return LineTypeHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
return LineTypeProperty
|
||||||
|
}
|
@ -1,26 +1,16 @@
|
|||||||
package wireguard
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"config-lsp/utils"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/k0kubun/pp"
|
"github.com/k0kubun/pp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func dedent(s string) string {
|
|
||||||
return strings.TrimLeft(s, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func keyExists[T comparable, V any](keys map[T]V, key T) bool {
|
|
||||||
_, found := keys[key]
|
|
||||||
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidWildTestWorksFine(
|
func TestValidWildTestWorksFine(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
PrivateKey = 1234567890
|
PrivateKey = 1234567890
|
||||||
Address = 192.168.1.0/24
|
Address = 192.168.1.0/24
|
||||||
@ -34,15 +24,15 @@ Endpoint = 1.2.3.4 ; I'm just a comment
|
|||||||
PublicKey = 5555
|
PublicKey = 5555
|
||||||
`)
|
`)
|
||||||
|
|
||||||
parser := createWireguardParser()
|
parser := CreateWireguardParser()
|
||||||
errors := parser.parseFromString(sample)
|
errors := parser.ParseFromString(sample)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("parseFromString failed with error %v", errors)
|
t.Fatalf("parseFromString failed with error %v", errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(len(parser.CommentLines) == 1 && keyExists(parser.CommentLines, 4)) {
|
if !(len(parser.commentLines) == 1 && utils.KeyExists(parser.commentLines, 4)) {
|
||||||
t.Fatalf("parseFromString failed to collect comment lines %v", parser.CommentLines)
|
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].Name == "Interface") && (*parser.Sections[1].Name == "Peer") && (*parser.Sections[2].Name == "Peer")) {
|
||||||
@ -70,18 +60,18 @@ PublicKey = 5555
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if line indexes are correct
|
// Check if line indexes are correct
|
||||||
if !(parser.LineIndexes[0].Type == LineTypeHeader &&
|
if !(parser.linesIndexes[0].Type == LineTypeHeader &&
|
||||||
parser.LineIndexes[1].Type == LineTypeProperty &&
|
parser.linesIndexes[1].Type == LineTypeProperty &&
|
||||||
parser.LineIndexes[2].Type == LineTypeProperty &&
|
parser.linesIndexes[2].Type == LineTypeProperty &&
|
||||||
parser.LineIndexes[3].Type == LineTypeEmpty &&
|
parser.linesIndexes[3].Type == LineTypeEmpty &&
|
||||||
parser.LineIndexes[4].Type == LineTypeComment &&
|
parser.linesIndexes[4].Type == LineTypeComment &&
|
||||||
parser.LineIndexes[5].Type == LineTypeHeader &&
|
parser.linesIndexes[5].Type == LineTypeHeader &&
|
||||||
parser.LineIndexes[6].Type == LineTypeProperty &&
|
parser.linesIndexes[6].Type == LineTypeProperty &&
|
||||||
parser.LineIndexes[7].Type == LineTypeProperty &&
|
parser.linesIndexes[7].Type == LineTypeProperty &&
|
||||||
parser.LineIndexes[8].Type == LineTypeEmpty &&
|
parser.linesIndexes[8].Type == LineTypeEmpty &&
|
||||||
parser.LineIndexes[9].Type == LineTypeHeader &&
|
parser.linesIndexes[9].Type == LineTypeHeader &&
|
||||||
parser.LineIndexes[10].Type == LineTypeProperty) {
|
parser.linesIndexes[10].Type == LineTypeProperty) {
|
||||||
pp.Println(parser.LineIndexes)
|
pp.Println(parser.linesIndexes)
|
||||||
t.Fatal("parseFromString: Invalid line indexes")
|
t.Fatal("parseFromString: Invalid line indexes")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,15 +79,15 @@ PublicKey = 5555
|
|||||||
func TestEmptySectionAtStartWorksFine(
|
func TestEmptySectionAtStartWorksFine(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
|
|
||||||
[Peer]
|
[Peer]
|
||||||
PublicKey = 1234567890
|
PublicKey = 1234567890
|
||||||
`)
|
`)
|
||||||
|
|
||||||
parser := createWireguardParser()
|
parser := CreateWireguardParser()
|
||||||
errors := parser.parseFromString(sample)
|
errors := parser.ParseFromString(sample)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("parseFromString failed with error %v", errors)
|
t.Fatalf("parseFromString failed with error %v", errors)
|
||||||
@ -115,7 +105,7 @@ PublicKey = 1234567890
|
|||||||
func TestEmptySectionAtEndWorksFine(
|
func TestEmptySectionAtEndWorksFine(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Inteface]
|
[Inteface]
|
||||||
PrivateKey = 1234567890
|
PrivateKey = 1234567890
|
||||||
|
|
||||||
@ -123,8 +113,8 @@ PrivateKey = 1234567890
|
|||||||
# Just sneaking in here, hehe
|
# Just sneaking in here, hehe
|
||||||
`)
|
`)
|
||||||
|
|
||||||
parser := createWireguardParser()
|
parser := CreateWireguardParser()
|
||||||
errors := parser.parseFromString(sample)
|
errors := parser.ParseFromString(sample)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("parseFromString failed with error %v", errors)
|
t.Fatalf("parseFromString failed with error %v", errors)
|
||||||
@ -138,19 +128,19 @@ PrivateKey = 1234567890
|
|||||||
t.Fatalf("parseFromString failed to collect properties %v", parser.Sections)
|
t.Fatalf("parseFromString failed to collect properties %v", parser.Sections)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(len(parser.CommentLines) == 1 && keyExists(parser.CommentLines, 4)) {
|
if !(len(parser.commentLines) == 1 && utils.KeyExists(parser.commentLines, 4)) {
|
||||||
t.Fatalf("parseFromString failed to collect comment lines %v", parser.CommentLines)
|
t.Fatalf("parseFromString failed to collect comment lines %v", parser.commentLines)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyFileWorksFine(
|
func TestEmptyFileWorksFine(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
`)
|
`)
|
||||||
|
|
||||||
parser := createWireguardParser()
|
parser := CreateWireguardParser()
|
||||||
errors := parser.parseFromString(sample)
|
errors := parser.ParseFromString(sample)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("parseFromString failed with error %v", errors)
|
t.Fatalf("parseFromString failed with error %v", errors)
|
||||||
@ -164,15 +154,15 @@ func TestEmptyFileWorksFine(
|
|||||||
func TestPartialSectionWithNoPropertiesWorksFine(
|
func TestPartialSectionWithNoPropertiesWorksFine(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Inte
|
[Inte
|
||||||
|
|
||||||
[Peer]
|
[Peer]
|
||||||
PublicKey = 1234567890
|
PublicKey = 1234567890
|
||||||
`)
|
`)
|
||||||
|
|
||||||
parser := createWireguardParser()
|
parser := CreateWireguardParser()
|
||||||
errors := parser.parseFromString(sample)
|
errors := parser.ParseFromString(sample)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("parseFromString failed with error %v", errors)
|
t.Fatalf("parseFromString failed with error %v", errors)
|
||||||
@ -186,8 +176,8 @@ PublicKey = 1234567890
|
|||||||
t.Fatalf("parseFromString failed to collect properties: %v", parser.Sections)
|
t.Fatalf("parseFromString failed to collect properties: %v", parser.Sections)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(len(parser.CommentLines) == 0) {
|
if !(len(parser.commentLines) == 0) {
|
||||||
t.Fatalf("parseFromString failed to collect comment lines: %v", parser.CommentLines)
|
t.Fatalf("parseFromString failed to collect comment lines: %v", parser.commentLines)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(parser.Sections[1].Properties[3].Key.Name == "PublicKey") {
|
if !(parser.Sections[1].Properties[3].Key.Name == "PublicKey") {
|
||||||
@ -198,15 +188,15 @@ PublicKey = 1234567890
|
|||||||
func TestPartialSectionWithPropertiesWorksFine(
|
func TestPartialSectionWithPropertiesWorksFine(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Inte
|
[Inte
|
||||||
PrivateKey = 1234567890
|
PrivateKey = 1234567890
|
||||||
|
|
||||||
[Peer]
|
[Peer]
|
||||||
`)
|
`)
|
||||||
|
|
||||||
parser := createWireguardParser()
|
parser := CreateWireguardParser()
|
||||||
errors := parser.parseFromString(sample)
|
errors := parser.ParseFromString(sample)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("parseFromString failed with error: %v", errors)
|
t.Fatalf("parseFromString failed with error: %v", errors)
|
||||||
@ -228,12 +218,12 @@ PrivateKey = 1234567890
|
|||||||
func TestFileWithOnlyComments(
|
func TestFileWithOnlyComments(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
# This is a comment
|
# This is a comment
|
||||||
# Another comment
|
# Another comment
|
||||||
`)
|
`)
|
||||||
parser := createWireguardParser()
|
parser := CreateWireguardParser()
|
||||||
errors := parser.parseFromString(sample)
|
errors := parser.ParseFromString(sample)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("parseFromString failed with error: %v", errors)
|
t.Fatalf("parseFromString failed with error: %v", errors)
|
||||||
@ -243,26 +233,26 @@ func TestFileWithOnlyComments(
|
|||||||
t.Fatalf("parseFromString failed to collect sections: %v", parser.Sections)
|
t.Fatalf("parseFromString failed to collect sections: %v", parser.Sections)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(len(parser.CommentLines) == 2) {
|
if !(len(parser.commentLines) == 2) {
|
||||||
t.Fatalf("parseFromString failed to collect comment lines: %v", parser.CommentLines)
|
t.Fatalf("parseFromString failed to collect comment lines: %v", parser.commentLines)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(keyExists(parser.CommentLines, 0) && keyExists(parser.CommentLines, 1)) {
|
if !(utils.KeyExists(parser.commentLines, 0) && utils.KeyExists(parser.commentLines, 1)) {
|
||||||
t.Fatalf("parseFromString failed to collect comment lines: %v", parser.CommentLines)
|
t.Fatalf("parseFromString failed to collect comment lines: %v", parser.commentLines)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultipleSectionsNoProperties(
|
func TestMultipleSectionsNoProperties(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
[Peer]
|
[Peer]
|
||||||
[Peer]
|
[Peer]
|
||||||
`)
|
`)
|
||||||
|
|
||||||
parser := createWireguardParser()
|
parser := CreateWireguardParser()
|
||||||
errors := parser.parseFromString(sample)
|
errors := parser.ParseFromString(sample)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("parseFromString failed with error: %v", errors)
|
t.Fatalf("parseFromString failed with error: %v", errors)
|
||||||
@ -282,15 +272,14 @@ func TestMultipleSectionsNoProperties(
|
|||||||
func TestWildTest1WorksCorrectly(
|
func TestWildTest1WorksCorrectly(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
DNS=1.1.1.1
|
DNS=1.1.1.1
|
||||||
|
|
||||||
|
|
||||||
`)
|
`)
|
||||||
|
parser := CreateWireguardParser()
|
||||||
parser := createWireguardParser()
|
errors := parser.ParseFromString(sample)
|
||||||
errors := parser.parseFromString(sample)
|
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("parseFromString failed with error: %v", errors)
|
t.Fatalf("parseFromString failed with error: %v", errors)
|
||||||
@ -312,8 +301,8 @@ DNS=1.1.1.1
|
|||||||
t.Fatalf("parseFromString failed to collect properties of section 0: %v", parser.Sections[0].Properties)
|
t.Fatalf("parseFromString failed to collect properties of section 0: %v", parser.Sections[0].Properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(len(parser.CommentLines) == 0) {
|
if !(len(parser.commentLines) == 0) {
|
||||||
t.Fatalf("parseFromString failed to collect comment lines: %v", parser.CommentLines)
|
t.Fatalf("parseFromString failed to collect comment lines: %v", parser.commentLines)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(parser.Sections[0].StartLine == 0 && parser.Sections[0].EndLine == 1) {
|
if !(parser.Sections[0].StartLine == 0 && parser.Sections[0].EndLine == 1) {
|
||||||
@ -324,12 +313,12 @@ DNS=1.1.1.1
|
|||||||
func TestPartialKeyWorksCorrectly(
|
func TestPartialKeyWorksCorrectly(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
DNS
|
DNS
|
||||||
`)
|
`)
|
||||||
parser := createWireguardParser()
|
parser := CreateWireguardParser()
|
||||||
errors := parser.parseFromString(sample)
|
errors := parser.ParseFromString(sample)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("parseFromString failed with error: %v", errors)
|
t.Fatalf("parseFromString failed with error: %v", errors)
|
||||||
@ -347,12 +336,12 @@ DNS
|
|||||||
func TestPartialValueWithSeparatorWorksCorrectly(
|
func TestPartialValueWithSeparatorWorksCorrectly(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
) {
|
) {
|
||||||
sample := dedent(`
|
sample := utils.Dedent(`
|
||||||
[Interface]
|
[Interface]
|
||||||
DNS=
|
DNS=
|
||||||
`)
|
`)
|
||||||
parser := createWireguardParser()
|
parser := CreateWireguardParser()
|
||||||
errors := parser.parseFromString(sample)
|
errors := parser.ParseFromString(sample)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("parseFromString failed with error: %v", errors)
|
t.Fatalf("parseFromString failed with error: %v", errors)
|
@ -1,4 +1,4 @@
|
|||||||
package wireguard
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
docvalues "config-lsp/doc-values"
|
docvalues "config-lsp/doc-values"
|
||||||
@ -11,27 +11,27 @@ import (
|
|||||||
|
|
||||||
var linePattern = regexp.MustCompile(`^\s*(?P<key>.+?)\s*(?P<separator>=)\s*(?P<value>\S.*?)?\s*(?:(?:;|#).*)?\s*$`)
|
var linePattern = regexp.MustCompile(`^\s*(?P<key>.+?)\s*(?P<separator>=)\s*(?P<value>\S.*?)?\s*(?:(?:;|#).*)?\s*$`)
|
||||||
|
|
||||||
type wireguardPropertyKey struct {
|
type WireguardPropertyKey struct {
|
||||||
Location characterLocation
|
Location CharacterLocation
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
type wireguardPropertyValue struct {
|
type WireguardPropertyValue struct {
|
||||||
Location characterLocation
|
Location CharacterLocation
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
type wireguardPropertySeparator struct {
|
type WireguardPropertySeparator struct {
|
||||||
Location characterLocation
|
Location CharacterLocation
|
||||||
}
|
}
|
||||||
|
|
||||||
type wireguardProperty struct {
|
type WireguardProperty struct {
|
||||||
Key wireguardPropertyKey
|
Key WireguardPropertyKey
|
||||||
Separator *wireguardPropertySeparator
|
Separator *WireguardPropertySeparator
|
||||||
Value *wireguardPropertyValue
|
Value *WireguardPropertyValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p wireguardProperty) String() string {
|
func (p WireguardProperty) String() string {
|
||||||
if p.Value == nil {
|
if p.Value == nil {
|
||||||
return p.Key.Name
|
return p.Key.Name
|
||||||
}
|
}
|
||||||
@ -39,7 +39,7 @@ func (p wireguardProperty) String() string {
|
|||||||
return p.Key.Name + "=" + p.Value.Value
|
return p.Key.Name + "=" + p.Value.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p wireguardProperty) getLineRange(line uint32) protocol.Range {
|
func (p WireguardProperty) GetLineRange(line uint32) protocol.Range {
|
||||||
return protocol.Range{
|
return protocol.Range{
|
||||||
Start: protocol.Position{
|
Start: protocol.Position{
|
||||||
Line: line,
|
Line: line,
|
||||||
@ -52,7 +52,44 @@ func (p wireguardProperty) getLineRange(line uint32) protocol.Range {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createWireguardProperty(line string) (*wireguardProperty, error) {
|
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
|
||||||
|
|
||||||
|
func (p *WireguardProperties) AddLine(lineNumber uint32, line string) error {
|
||||||
|
property, err := CreateWireguardProperty(line)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
(*p)[lineNumber] = *property
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateWireguardProperty(line string) (*WireguardProperty, error) {
|
||||||
if !strings.Contains(line, "=") {
|
if !strings.Contains(line, "=") {
|
||||||
indexes := utils.GetTrimIndex(line)
|
indexes := utils.GetTrimIndex(line)
|
||||||
|
|
||||||
@ -61,10 +98,10 @@ func createWireguardProperty(line string) (*wireguardProperty, error) {
|
|||||||
return nil, &docvalues.MalformedLineError{}
|
return nil, &docvalues.MalformedLineError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &wireguardProperty{
|
return &WireguardProperty{
|
||||||
Key: wireguardPropertyKey{
|
Key: WireguardPropertyKey{
|
||||||
Name: line[indexes[0]:indexes[1]],
|
Name: line[indexes[0]:indexes[1]],
|
||||||
Location: characterLocation{
|
Location: CharacterLocation{
|
||||||
Start: uint32(indexes[0]),
|
Start: uint32(indexes[0]),
|
||||||
End: uint32(indexes[1]),
|
End: uint32(indexes[1]),
|
||||||
},
|
},
|
||||||
@ -80,8 +117,8 @@ func createWireguardProperty(line string) (*wireguardProperty, error) {
|
|||||||
|
|
||||||
keyStart := uint32(indexes[2])
|
keyStart := uint32(indexes[2])
|
||||||
keyEnd := uint32(indexes[3])
|
keyEnd := uint32(indexes[3])
|
||||||
key := wireguardPropertyKey{
|
key := WireguardPropertyKey{
|
||||||
Location: characterLocation{
|
Location: CharacterLocation{
|
||||||
Start: keyStart,
|
Start: keyStart,
|
||||||
End: keyEnd,
|
End: keyEnd,
|
||||||
},
|
},
|
||||||
@ -90,22 +127,22 @@ func createWireguardProperty(line string) (*wireguardProperty, error) {
|
|||||||
|
|
||||||
separatorStart := uint32(indexes[4])
|
separatorStart := uint32(indexes[4])
|
||||||
separatorEnd := uint32(indexes[5])
|
separatorEnd := uint32(indexes[5])
|
||||||
separator := wireguardPropertySeparator{
|
separator := WireguardPropertySeparator{
|
||||||
Location: characterLocation{
|
Location: CharacterLocation{
|
||||||
Start: separatorStart,
|
Start: separatorStart,
|
||||||
End: separatorEnd,
|
End: separatorEnd,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var value *wireguardPropertyValue
|
var value *WireguardPropertyValue
|
||||||
|
|
||||||
if indexes[6] != -1 && indexes[7] != -1 {
|
if indexes[6] != -1 && indexes[7] != -1 {
|
||||||
// value exists
|
// value exists
|
||||||
valueStart := uint32(indexes[6])
|
valueStart := uint32(indexes[6])
|
||||||
valueEnd := uint32(indexes[7])
|
valueEnd := uint32(indexes[7])
|
||||||
|
|
||||||
value = &wireguardPropertyValue{
|
value = &WireguardPropertyValue{
|
||||||
Location: characterLocation{
|
Location: CharacterLocation{
|
||||||
Start: valueStart,
|
Start: valueStart,
|
||||||
End: valueEnd,
|
End: valueEnd,
|
||||||
},
|
},
|
||||||
@ -113,24 +150,9 @@ func createWireguardProperty(line string) (*wireguardProperty, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &wireguardProperty{
|
return &WireguardProperty{
|
||||||
Key: key,
|
Key: key,
|
||||||
Separator: &separator,
|
Separator: &separator,
|
||||||
Value: value,
|
Value: value,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// [<line number>]: <property>
|
|
||||||
type wireguardProperties map[uint32]wireguardProperty
|
|
||||||
|
|
||||||
func (p *wireguardProperties) AddLine(lineNumber uint32, line string) error {
|
|
||||||
property, err := createWireguardProperty(line)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
(*p)[lineNumber] = *property
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
120
handlers/wireguard/parser/wg-section.go
Normal file
120
handlers/wireguard/parser/wg-section.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PropertyNotFoundError struct{}
|
||||||
|
|
||||||
|
func (e PropertyNotFoundError) Error() string {
|
||||||
|
return "Property not found"
|
||||||
|
}
|
||||||
|
|
||||||
|
type PropertyNotFullyTypedError struct{}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
for line, property := range s.Properties {
|
||||||
|
if property.Key.Name == name {
|
||||||
|
return &line, &property
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WireguardSection) ExistsProperty(name string) bool {
|
||||||
|
_, property := s.FetchFirstProperty(name)
|
||||||
|
|
||||||
|
return property != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WireguardSection) GetPropertyByLine(lineNumber uint32) (*WireguardProperty, error) {
|
||||||
|
property, found := s.Properties[lineNumber]
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return nil, PropertyNotFoundError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &property, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var validHeaderPattern = regexp.MustCompile(`^\s*\[(?P<header>.+?)\]\s*$`)
|
||||||
|
|
||||||
|
// Create a new create section
|
||||||
|
// Return (<name>, <new section>)
|
||||||
|
func CreateWireguardSection(
|
||||||
|
startLine uint32,
|
||||||
|
endLine uint32,
|
||||||
|
headerLine string,
|
||||||
|
props WireguardProperties,
|
||||||
|
) WireguardSection {
|
||||||
|
match := validHeaderPattern.FindStringSubmatch(headerLine)
|
||||||
|
|
||||||
|
var header string
|
||||||
|
|
||||||
|
if match == nil {
|
||||||
|
// Still typing it
|
||||||
|
header = headerLine[1:]
|
||||||
|
} else {
|
||||||
|
header = match[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return WireguardSection{
|
||||||
|
Name: &header,
|
||||||
|
StartLine: startLine,
|
||||||
|
EndLine: endLine,
|
||||||
|
Properties: props,
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
package wireguard
|
|
||||||
|
|
||||||
import (
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
|
||||||
)
|
|
||||||
|
|
||||||
var documentParserMap = map[protocol.DocumentUri]*wireguardParser{}
|
|
@ -1,45 +0,0 @@
|
|||||||
package wireguard
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/tliron/glsp"
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
|
|
||||||
parser := documentParserMap[params.TextDocument.URI]
|
|
||||||
|
|
||||||
lineNumber := params.Position.Line
|
|
||||||
|
|
||||||
section := parser.getBelongingSectionByLine(lineNumber)
|
|
||||||
lineType := parser.getTypeByLine(lineNumber)
|
|
||||||
|
|
||||||
switch lineType {
|
|
||||||
case LineTypeComment:
|
|
||||||
return nil, nil
|
|
||||||
case LineTypeHeader:
|
|
||||||
return parser.getRootCompletionsForEmptyLine(), nil
|
|
||||||
case LineTypeEmpty:
|
|
||||||
if section.Name == nil {
|
|
||||||
// Root completions
|
|
||||||
return parser.getRootCompletionsForEmptyLine(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return section.getCompletionsForEmptyLine()
|
|
||||||
case LineTypeProperty:
|
|
||||||
completions, err := section.getCompletionsForPropertyLine(lineNumber, params.Position.Character)
|
|
||||||
|
|
||||||
if completions == nil && err != nil {
|
|
||||||
switch err.(type) {
|
|
||||||
// Ignore
|
|
||||||
case propertyNotFullyTypedError:
|
|
||||||
return section.getCompletionsForEmptyLine()
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return completions, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
panic("TextDocumentCompletion: unexpected line type")
|
|
||||||
}
|
|
@ -1,249 +0,0 @@
|
|||||||
package wireguard
|
|
||||||
|
|
||||||
import (
|
|
||||||
docvalues "config-lsp/doc-values"
|
|
||||||
"config-lsp/utils"
|
|
||||||
"fmt"
|
|
||||||
"maps"
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
|
||||||
)
|
|
||||||
|
|
||||||
type propertyNotFoundError struct{}
|
|
||||||
|
|
||||||
func (e propertyNotFoundError) Error() string {
|
|
||||||
return "Property not found"
|
|
||||||
}
|
|
||||||
|
|
||||||
type propertyNotFullyTypedError struct{}
|
|
||||||
|
|
||||||
func (e propertyNotFullyTypedError) Error() string {
|
|
||||||
return "Property not fully typed"
|
|
||||||
}
|
|
||||||
|
|
||||||
type wireguardSectionType uint
|
|
||||||
|
|
||||||
const (
|
|
||||||
wireguardSectionUnknownType wireguardSectionType = 0
|
|
||||||
wireguardSectionInterfaceType wireguardSectionType = 1
|
|
||||||
wireguardSectionPeerType wireguardSectionType = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
type wireguardSection struct {
|
|
||||||
Name *string
|
|
||||||
StartLine uint32
|
|
||||||
EndLine uint32
|
|
||||||
Properties wireguardProperties
|
|
||||||
}
|
|
||||||
|
|
||||||
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) 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) fetchFirstProperty(name string) (*uint32, *wireguardProperty) {
|
|
||||||
for line, property := range s.Properties {
|
|
||||||
if property.Key.Name == name {
|
|
||||||
return &line, &property
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *wireguardSection) existsProperty(name string) bool {
|
|
||||||
_, property := s.fetchFirstProperty(name)
|
|
||||||
|
|
||||||
return property != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *wireguardSection) findProperty(lineNumber uint32) (*wireguardProperty, error) {
|
|
||||||
property, found := s.Properties[lineNumber]
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return nil, propertyNotFoundError{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &property, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s wireguardSection) getCompletionsForEmptyLine() ([]protocol.CompletionItem, error) {
|
|
||||||
if s.Name == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
options := make(map[string]docvalues.DocumentationValue)
|
|
||||||
|
|
||||||
switch *s.Name {
|
|
||||||
case "Interface":
|
|
||||||
maps.Copy(options, interfaceOptions)
|
|
||||||
|
|
||||||
// Remove existing options
|
|
||||||
for _, property := range s.Properties {
|
|
||||||
if _, found := interfaceAllowedDuplicateFields[property.Key.Name]; found {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(options, property.Key.Name)
|
|
||||||
}
|
|
||||||
case "Peer":
|
|
||||||
maps.Copy(options, peerOptions)
|
|
||||||
|
|
||||||
// Remove existing options
|
|
||||||
for _, property := range s.Properties {
|
|
||||||
if _, found := peerAllowedDuplicateFields[property.Key.Name]; found {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(options, property.Key.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kind := protocol.CompletionItemKindProperty
|
|
||||||
|
|
||||||
return utils.MapMapToSlice(
|
|
||||||
options,
|
|
||||||
func(optionName string, value docvalues.DocumentationValue) protocol.CompletionItem {
|
|
||||||
insertText := optionName + " = "
|
|
||||||
|
|
||||||
return protocol.CompletionItem{
|
|
||||||
Kind: &kind,
|
|
||||||
Documentation: value.Documentation,
|
|
||||||
Label: optionName,
|
|
||||||
InsertText: &insertText,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSeparatorCompletion(property wireguardProperty, character uint32) ([]protocol.CompletionItem, error) {
|
|
||||||
var insertText string
|
|
||||||
|
|
||||||
if character == property.Key.Location.End {
|
|
||||||
insertText = property.Key.Name + " = "
|
|
||||||
} else {
|
|
||||||
insertText = "= "
|
|
||||||
}
|
|
||||||
|
|
||||||
kind := protocol.CompletionItemKindValue
|
|
||||||
|
|
||||||
return []protocol.CompletionItem{
|
|
||||||
{
|
|
||||||
Label: insertText,
|
|
||||||
InsertText: &insertText,
|
|
||||||
Kind: &kind,
|
|
||||||
},
|
|
||||||
}, propertyNotFullyTypedError{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p wireguardSection) getCompletionsForPropertyLine(
|
|
||||||
lineNumber uint32,
|
|
||||||
character uint32,
|
|
||||||
) ([]protocol.CompletionItem, error) {
|
|
||||||
property, err := p.findProperty(lineNumber)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Name == nil {
|
|
||||||
return nil, propertyNotFoundError{}
|
|
||||||
}
|
|
||||||
|
|
||||||
options, found := optionsHeaderMap[*p.Name]
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return nil, propertyNotFoundError{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if property.Separator == nil {
|
|
||||||
if _, found := options[property.Key.Name]; found {
|
|
||||||
return getSeparatorCompletion(*property, character)
|
|
||||||
}
|
|
||||||
// Get empty line completions
|
|
||||||
return nil, propertyNotFullyTypedError{}
|
|
||||||
}
|
|
||||||
|
|
||||||
option, found := options[property.Key.Name]
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
if character < property.Separator.Location.Start {
|
|
||||||
return nil, propertyNotFullyTypedError{}
|
|
||||||
} else {
|
|
||||||
return nil, propertyNotFoundError{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if property.Value == nil {
|
|
||||||
if character >= property.Separator.Location.End {
|
|
||||||
return option.FetchCompletions("", 0), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
relativeCursor := character - property.Value.Location.Start
|
|
||||||
|
|
||||||
return option.FetchCompletions(property.Value.Value, relativeCursor), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var validHeaderPattern = regexp.MustCompile(`^\s*\[(?P<header>.+?)\]\s*$`)
|
|
||||||
|
|
||||||
// Create a new create section
|
|
||||||
// Return (<name>, <new section>)
|
|
||||||
func createWireguardSection(
|
|
||||||
startLine uint32,
|
|
||||||
endLine uint32,
|
|
||||||
headerLine string,
|
|
||||||
props wireguardProperties,
|
|
||||||
) wireguardSection {
|
|
||||||
match := validHeaderPattern.FindStringSubmatch(headerLine)
|
|
||||||
|
|
||||||
var header string
|
|
||||||
|
|
||||||
if match == nil {
|
|
||||||
// Still typing it
|
|
||||||
header = headerLine[1:]
|
|
||||||
} else {
|
|
||||||
header = match[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return wireguardSection{
|
|
||||||
Name: &header,
|
|
||||||
StartLine: startLine,
|
|
||||||
EndLine: endLine,
|
|
||||||
Properties: props,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package wireguard
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
|
||||||
)
|
|
||||||
|
|
||||||
func WorkspaceExecuteCommand(context *glsp.Context, params *protocol.ExecuteCommandParams) (*protocol.ApplyWorkspaceEditParams, error) {
|
|
||||||
_, command, _ := strings.Cut(params.Command, ".")
|
|
||||||
|
|
||||||
switch command {
|
|
||||||
case string(codeActionGeneratePrivateKey):
|
|
||||||
args := codeActionGeneratePrivateKeyArgsFromArguments(params.Arguments[0].(map[string]any))
|
|
||||||
|
|
||||||
parser := documentParserMap[args.URI]
|
|
||||||
|
|
||||||
return parser.runGeneratePrivateKey(args)
|
|
||||||
case string(codeActionGeneratePresharedKey):
|
|
||||||
args := codeActionGeneratePresharedKeyArgsFromArguments(params.Arguments[0].(map[string]any))
|
|
||||||
|
|
||||||
parser := documentParserMap[args.URI]
|
|
||||||
|
|
||||||
return parser.runGeneratePresharedKey(args)
|
|
||||||
case string(codeActionAddKeepalive):
|
|
||||||
args := codeActionAddKeepaliveArgsFromArguments(params.Arguments[0].(map[string]any))
|
|
||||||
|
|
||||||
parser := documentParserMap[args.URI]
|
|
||||||
|
|
||||||
return parser.runAddKeepalive(args)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
@ -1,8 +1,7 @@
|
|||||||
package roothandler
|
package roothandler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/handlers/wireguard"
|
wireguard "config-lsp/handlers/wireguard/lsp"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
|
@ -2,7 +2,7 @@ package roothandler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/handlers/fstab"
|
"config-lsp/handlers/fstab"
|
||||||
"config-lsp/handlers/wireguard"
|
wireguard "config-lsp/handlers/wireguard/lsp"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
@ -2,7 +2,7 @@ package roothandler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/handlers/fstab"
|
"config-lsp/handlers/fstab"
|
||||||
"config-lsp/handlers/wireguard"
|
wireguard "config-lsp/handlers/wireguard/lsp"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package roothandler
|
package roothandler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/handlers/wireguard"
|
wireguard "config-lsp/handlers/wireguard/lsp"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
@ -3,7 +3,7 @@ package roothandler
|
|||||||
import (
|
import (
|
||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
fstab "config-lsp/handlers/fstab"
|
fstab "config-lsp/handlers/fstab"
|
||||||
"config-lsp/handlers/wireguard"
|
wireguard "config-lsp/handlers/wireguard/lsp"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
|
@ -2,7 +2,7 @@ package roothandler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/handlers/fstab"
|
"config-lsp/handlers/fstab"
|
||||||
"config-lsp/handlers/wireguard"
|
wireguard "config-lsp/handlers/wireguard/lsp"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package roothandler
|
package roothandler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/handlers/wireguard"
|
wireguard "config-lsp/handlers/wireguard/lsp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
|
13
utils/tests.go
Normal file
13
utils/tests.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func Dedent(s string) string {
|
||||||
|
return strings.TrimLeft(s, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func KeyExists[T comparable, V any](keys map[T]V, key T) bool {
|
||||||
|
_, found := keys[key]
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user