refactor(server): Refactor Wireguard config; Improve completions; Improve code actions

Signed-off-by: Myzel394 <github.7a2op@simplelogin.co>
This commit is contained in:
Myzel394 2025-03-08 15:56:09 +01:00 committed by Myzel394
parent a0dca94b9d
commit ff9b5db18a
No known key found for this signature in database
GPG Key ID: B603E877F73D4ABB
8 changed files with 136 additions and 107 deletions

View File

@ -55,7 +55,7 @@ func analyzeStructureIsValid(ctx *analyzerContext) {
if property.Value == nil || property.Value.Value == "" {
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Message: "This property is missing a value",
Range: property.Value.ToLSPRange(),
Range: property.ToLSPRange(),
Severity: &common.SeverityError,
})
}

View File

@ -1,6 +1,7 @@
package ast
import (
"config-lsp/utils"
"slices"
)
@ -47,3 +48,23 @@ func (s *WGSection) FindFirstPropertyByName(name string) *WGProperty {
return nil
}
func (s *WGSection) FindPropertyByName(name string) *WGProperty {
for _, property := range s.Properties {
if property.Key.Name == name {
return property
}
}
return nil
}
func (s *WGSection) GetLastProperty() *WGProperty {
if len(s.Properties) == 0 {
return nil
}
lastLine := utils.FindBiggestKey(s.Properties)
return s.Properties[lastLine]
}

View File

@ -1,9 +1,10 @@
package handlers
/*
import (
"config-lsp/handlers/wireguard"
"config-lsp/handlers/wireguard/ast"
wgcommands "config-lsp/handlers/wireguard/commands"
protocol "github.com/tliron/glsp/protocol_3_16"
)
@ -12,7 +13,6 @@ type CodeActionName string
const (
CodeActionGeneratePrivateKey CodeActionName = "generatePrivateKey"
CodeActionGeneratePresharedKey CodeActionName = "generatePresharedKey"
CodeActionAddKeepalive CodeActionName = "addKeepalive"
)
type CodeAction interface {
@ -33,14 +33,15 @@ func CodeActionGeneratePrivateKeyArgsFromArguments(arguments map[string]any) Cod
}
}
func (args CodeActionGeneratePrivateKeyArgs) RunCommand(p *ast.WGConfig) (*protocol.ApplyWorkspaceEditParams, error) {
func (args CodeActionGeneratePrivateKeyArgs) RunCommand(d *wireguard.WGDocument) (*protocol.ApplyWorkspaceEditParams, error) {
privateKey, err := wgcommands.CreateNewPrivateKey()
if err != nil {
return &protocol.ApplyWorkspaceEditParams{}, err
}
section, property := p.GetPropertyByLine(args.Line)
section := d.Config.FindSectionByLine(args.Line)
property := d.Config.FindPropertyByLine(args.Line)
if section == nil || property == nil {
return nil, nil
@ -54,7 +55,16 @@ func (args CodeActionGeneratePrivateKeyArgs) RunCommand(p *ast.WGConfig) (*proto
args.URI: {
{
NewText: " " + privateKey,
Range: property.GetInsertRange(args.Line),
Range: protocol.Range{
Start: protocol.Position{
Line: property.End.Line,
Character: property.End.Character,
},
End: protocol.Position{
Line: property.End.Line,
Character: property.End.Character,
},
},
},
},
},
@ -74,14 +84,15 @@ func CodeActionGeneratePresharedKeyArgsFromArguments(arguments map[string]any) C
}
}
func (args CodeActionGeneratePresharedKeyArgs) RunCommand(p *ast.WGConfig) (*protocol.ApplyWorkspaceEditParams, error) {
func (args CodeActionGeneratePresharedKeyArgs) RunCommand(d *wireguard.WGDocument) (*protocol.ApplyWorkspaceEditParams, error) {
presharedKey, err := wgcommands.CreatePresharedKey()
if err != nil {
return &protocol.ApplyWorkspaceEditParams{}, err
}
section, property := p.GetPropertyByLine(args.Line)
section := d.Config.FindSectionByLine(args.Line)
property := d.Config.FindPropertyByLine(args.Line)
if section == nil || property == nil {
return nil, nil
@ -95,45 +106,14 @@ func (args CodeActionGeneratePresharedKeyArgs) RunCommand(p *ast.WGConfig) (*pro
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 *ast.WGConfig) (*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,
Line: property.End.Line,
Character: property.End.Character,
},
End: protocol.Position{
Line: section.EndLine + 1,
Character: 0,
Line: property.End.Line,
Character: property.End.Character,
},
},
},
@ -142,4 +122,4 @@ func (args CodeActionAddKeepaliveArgs) RunCommand(p *ast.WGConfig) (*protocol.Ap
},
}, nil
}
*/

View File

@ -86,13 +86,20 @@ func getPropertyCompletions(
Address = 10.|
*/
if property == nil || property.Separator == nil {
currentLine := params.Position.Line
position := common.LSPCharacterAsCursorPosition(params.Position.Character)
// Special case, key defined but separator missing
if property != nil && property.Separator == nil && !property.Key.ContainsPosition(position) {
return getKeyCompletions(section, true, currentLine), nil
}
if property == nil || property.Separator == nil || property.Key.ContainsPosition(position) {
// First scenario
return getKeyCompletions(section), nil
return getKeyCompletions(section, false, currentLine), nil
}
// Check if the cursor it outside the value
position := common.LSPCharacterAsCursorPosition(params.Position.Character)
if property.Value != nil && property.Value.IsPositionAfterEnd(position) {
// Then we don't show anything
return nil, nil
@ -104,45 +111,55 @@ func getPropertyCompletions(
func getKeyCompletions(
section ast.WGSection,
onlySeparator bool,
currentLine uint32,
) []protocol.CompletionItem {
options := make(map[string]docvalues.DocumentationValue)
allowedDuplicatedFields := make(map[string]struct{})
switch section.Header.Name {
case "Interface":
maps.Copy(options, fields.InterfaceOptions)
// Remove existing, non-duplicate options
for _, property := range section.Properties {
if _, found := fields.InterfaceAllowedDuplicateFields[property.Key.Name]; found {
continue
}
delete(options, property.Key.Name)
}
allowedDuplicatedFields = fields.InterfaceAllowedDuplicateFields
case "Peer":
maps.Copy(options, fields.PeerOptions)
allowedDuplicatedFields = fields.PeerAllowedDuplicateFields
}
// Remove existing, non-duplicate options
for _, property := range section.Properties {
if _, found := fields.PeerAllowedDuplicateFields[property.Key.Name]; found {
if _, found := allowedDuplicatedFields[property.Key.Name]; found {
continue
}
if property.Key.Start.Line == currentLine {
// The user is currently typing the key, thus we should suggest it
continue
}
delete(options, property.Key.Name)
}
}
kind := protocol.CompletionItemKindField
return utils.MapMapToSlice(
options,
func(optionName string, value docvalues.DocumentationValue) protocol.CompletionItem {
insertText := optionName + " = "
func(rawOptionName string, value docvalues.DocumentationValue) protocol.CompletionItem {
var label string
var insertText string
if onlySeparator {
label = rawOptionName + " = "
insertText = "= "
} else {
label = rawOptionName
insertText = rawOptionName + " = "
}
return protocol.CompletionItem{
Kind: &kind,
Documentation: value.Documentation,
Label: optionName,
Label: label,
InsertText: &insertText,
}
},

View File

@ -1,37 +1,52 @@
package handlers
/*
import (
"config-lsp/handlers/wireguard/ast"
"config-lsp/handlers/wireguard"
"config-lsp/handlers/wireguard/commands"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func GetKeepaliveCodeActions(
p *ast.WGConfig,
d *wireguard.WGDocument,
params *protocol.CodeActionParams,
) []protocol.CodeAction {
line := params.Range.Start.Line
for index, section := range p.Sections {
if section.StartLine >= line && line <= section.EndLine && section.Header != nil && *section.Header == "Peer" {
if section.ExistsProperty("Endpoint") && !section.ExistsProperty("PersistentKeepalive") {
commandID := "wireguard." + CodeActionAddKeepalive
command := protocol.Command{
Title: "Add PersistentKeepalive",
Command: string(commandID),
Arguments: []any{
CodeActionAddKeepaliveArgs{
URI: params.TextDocument.URI,
SectionIndex: uint32(index),
},
},
for _, section := range d.Indexes.SectionsByName["Peer"] {
if section.Start.Line >= line && line <= section.End.Line {
if section.FindPropertyByName("Endpoint") != nil && section.FindFirstPropertyByName("PersistentKeepalive") == nil {
var insertionLine uint32
lastProperty := section.GetLastProperty()
if lastProperty == nil {
insertionLine = section.End.Line
} else {
insertionLine = lastProperty.End.Line + 1
}
return []protocol.CodeAction{
{
Title: "Add PersistentKeepalive",
Command: &command,
Edit: &protocol.WorkspaceEdit{
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
params.TextDocument.URI: {
{
Range: protocol.Range{
Start: protocol.Position{
Line: insertionLine,
Character: 0,
},
End: protocol.Position{
Line: insertionLine,
Character: 0,
},
},
NewText: "PersistentKeepalive = 25\n",
},
},
},
},
},
}
}
@ -42,11 +57,17 @@ func GetKeepaliveCodeActions(
}
func GetKeyGenerationCodeActions(
p *ast.WGConfig,
d *wireguard.WGDocument,
params *protocol.CodeActionParams,
) []protocol.CodeAction {
if !wgcommands.AreWireguardToolsAvailable() {
return nil
}
line := params.Range.Start.Line
section, property := p.GetPropertyByLine(line)
section := d.Config.FindSectionByLine(line)
property := d.Config.FindPropertyByLine(line)
if section == nil || property == nil || property.Separator == nil {
return nil
@ -54,10 +75,6 @@ func GetKeyGenerationCodeActions(
switch property.Key.Name {
case "PrivateKey":
if !wgcommands.AreWireguardToolsAvailable() {
return nil
}
commandID := "wireguard." + CodeActionGeneratePrivateKey
command := protocol.Command{
Title: "Generate Private Key",
@ -103,4 +120,3 @@ func GetKeyGenerationCodeActions(
return nil
}
*/

View File

@ -1,18 +1,20 @@
package lsp
import (
"config-lsp/handlers/wireguard"
"config-lsp/handlers/wireguard/handlers"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
p := documentParserMap[params.TextDocument.URI]
d := wireguard.DocumentParserMap[params.TextDocument.URI]
actions := make([]protocol.CodeAction, 0, 2)
actions = append(actions, handlers.GetKeyGenerationCodeActions(p, params)...)
actions = append(actions, handlers.GetKeepaliveCodeActions(p, params)...)
actions = append(actions, handlers.GetKeyGenerationCodeActions(d, params)...)
actions = append(actions, handlers.GetKeepaliveCodeActions(d, params)...)
if len(actions) > 0 {
return actions, nil

View File

@ -1,10 +1,6 @@
package lsp
import (
"config-lsp/handlers/wireguard/handlers"
"config-lsp/handlers/wireguard/parser"
"strings"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
@ -13,6 +9,7 @@ func TextDocumentHover(
context *glsp.Context,
params *protocol.HoverParams,
) (*protocol.Hover, error) {
/*
p := documentParserMap[params.TextDocument.URI]
switch p.GetTypeByLine(params.Position.Line) {
@ -37,6 +34,7 @@ func TextDocumentHover(
}
return &hover, nil
}
*/
return nil, nil
}

View File

@ -1,6 +1,7 @@
package lsp
import (
"config-lsp/handlers/wireguard"
"config-lsp/handlers/wireguard/handlers"
"strings"
@ -15,21 +16,15 @@ func WorkspaceExecuteCommand(context *glsp.Context, params *protocol.ExecuteComm
case string(handlers.CodeActionGeneratePrivateKey):
args := handlers.CodeActionGeneratePrivateKeyArgsFromArguments(params.Arguments[0].(map[string]any))
p := documentParserMap[args.URI]
d := wireguard.DocumentParserMap[args.URI]
return args.RunCommand(p)
return args.RunCommand(d)
case string(handlers.CodeActionGeneratePresharedKey):
args := handlers.CodeActionGeneratePresharedKeyArgsFromArguments(params.Arguments[0].(map[string]any))
parser := documentParserMap[args.URI]
d := wireguard.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 args.RunCommand(d)
}
return nil, nil