config-lsp/handlers/wireguard/wg-section.go
2024-08-17 14:26:14 +02:00

204 lines
4.2 KiB
Go

package wireguard
import (
docvalues "config-lsp/doc-values"
"config-lsp/utils"
"fmt"
"maps"
"math"
"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 {
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) 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 property.Separator == nil {
if p.Name != nil {
switch *p.Name {
case "Interface":
if _, found := interfaceOptions[property.Key.Name]; found {
return getSeparatorCompletion(*property, character)
}
case "Peer":
if _, found := peerOptions[property.Key.Name]; found {
return getSeparatorCompletion(*property, character)
}
}
// Get empty line completions
return nil, propertyNotFullyTypedError{}
}
return nil, propertyNotFoundError{}
}
var option docvalues.Value
switch *p.Name {
case "Interface":
option = interfaceOptions[property.Key.Name]
case "Peer":
option = peerOptions[property.Key.Name]
}
if option == nil {
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,
) (string, wireguardSection) {
match := validHeaderPattern.FindStringSubmatch(headerLine)
var header string
if match == nil {
// Still typing it
header = headerLine[1:]
} else {
header = match[1]
}
return header, wireguardSection{
StartLine: startLine,
EndLine: endLine,
Properties: props,
}
}