From d9903805c5bdb876ebc6538f82f1fa3c93a4cb6d Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 14 Aug 2024 21:26:28 +0200 Subject: [PATCH] fix(wireguard): Improve completions for the separator --- handlers/wireguard/documentation-fields.go | 4 +- handlers/wireguard/parser_completions_test.go | 50 +++++++++++++ .../wireguard/text-document-completion.go | 8 +- handlers/wireguard/wg-section.go | 73 +++++++++++++++++++ utils/strings.go | 7 +- 5 files changed, 133 insertions(+), 9 deletions(-) diff --git a/handlers/wireguard/documentation-fields.go b/handlers/wireguard/documentation-fields.go index d594522..77f6f3c 100644 --- a/handlers/wireguard/documentation-fields.go +++ b/handlers/wireguard/documentation-fields.go @@ -1,7 +1,9 @@ // Documentation taken from https://github.com/pirate/wireguard-docs package wireguard -import docvalues "config-lsp/doc-values" +import ( + docvalues "config-lsp/doc-values" +) var HeaderField = docvalues.EnumValue{ EnforceValues: true, diff --git a/handlers/wireguard/parser_completions_test.go b/handlers/wireguard/parser_completions_test.go index ab68fb7..df842c4 100644 --- a/handlers/wireguard/parser_completions_test.go +++ b/handlers/wireguard/parser_completions_test.go @@ -96,3 +96,53 @@ func TestInterfaceAndPeerSectionRootCompletionsWork( t.Fatalf("getRootCompletionsForEmptyLine: Expected 1 completions, but got %v", len(completions)) } } + +func TestPropertyNoSepatorShouldCompleteSeparator( + t *testing.T, +) { + sample := dedent(` +[Interface] +DNS +`) + parser := createWireguardParser() + parser.parseFromString(sample) + + completions, err := parser.Sections[0].getCompletionsForPropertyLine(1, 3) + + if err == nil { + t.Fatalf("getCompletionsForPropertyLine err is nil but should not be") + } + + if len(completions) != 1 { + t.Fatalf("getCompletionsForPropertyLine: Expected 1 completion, but got %v", len(completions)) + } + + if *completions[0].InsertText != " = " { + t.Fatalf("getCompletionsForPropertyLine: Expected completion to be ' = ', but got '%v'", completions[0].Label) + } +} + +func TestPropertyNoSeparatorWithSpaceShouldCompleteSeparator( + t *testing.T, +) { + sample := dedent(` +[Interface] +DNS +`) + parser := createWireguardParser() + parser.parseFromString(sample) + + completions, err := parser.Sections[0].getCompletionsForPropertyLine(1, 4) + + if err == nil { + t.Fatalf("getCompletionsForPropertyLine err is nil but should not be") + } + + if len(completions) != 1 { + t.Fatalf("getCompletionsForPropertyLine: Expected 1 completion, but got %v", len(completions)) + } + + if *completions[0].InsertText != "= " { + t.Fatalf("getCompletionsForPropertyLine: Expected completion to be '= ', but got '%v'", completions[0].Label) + } +} diff --git a/handlers/wireguard/text-document-completion.go b/handlers/wireguard/text-document-completion.go index 54807a0..04c0c75 100644 --- a/handlers/wireguard/text-document-completion.go +++ b/handlers/wireguard/text-document-completion.go @@ -17,7 +17,7 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa case LineTypeComment: return nil, nil case LineTypeHeader: - return nil, nil + return parser.getRootCompletionsForEmptyLine(), nil case LineTypeEmpty: if section == nil { return parser.getRootCompletionsForEmptyLine(), nil @@ -25,7 +25,11 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa return section.getCompletionsForEmptyLine() case LineTypeProperty: - return nil, nil + if section == nil { + return nil, nil + } + + return section.getCompletionsForPropertyLine(lineNumber, params.Position.Character) } panic("TextDocumentCompletion: unexpected line type") diff --git a/handlers/wireguard/wg-section.go b/handlers/wireguard/wg-section.go index 48b999f..fd85e89 100644 --- a/handlers/wireguard/wg-section.go +++ b/handlers/wireguard/wg-section.go @@ -10,6 +10,18 @@ import ( 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 @@ -30,6 +42,16 @@ func (s wireguardSection) String() string { return fmt.Sprintf("[%s]; %d-%d: %v", name, s.StartLine, s.EndLine, s.Properties) } +func (s *wireguardSection) findProperty(lineNumber uint32) (*wireguardProperty, error) { + for _, property := range s.Properties { + if property.Key.Location.Start <= lineNumber && property.Key.Location.End >= lineNumber { + return &property, nil + } + } + + return nil, propertyNotFoundError{} +} + func (s wireguardSection) getCompletionsForEmptyLine() ([]protocol.CompletionItem, error) { if s.Name == nil { return nil, nil @@ -76,6 +98,57 @@ func (s wireguardSection) getCompletionsForEmptyLine() ([]protocol.CompletionIte return []protocol.CompletionItem{}, nil } +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 { + var insertText string + + if character == property.Key.Location.End { + insertText = " = " + } else { + insertText = "= " + } + + return []protocol.CompletionItem{ + { + Label: "=", + InsertText: &insertText, + }, + }, propertyNotFullyTypedError{} + } + + var option docvalues.Value + + for enum, opt := range interfaceOptions { + if enum.InsertText == property.Key.Name { + option = opt + break + } + } + + 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
.+?)\]\s*$`) func createWireguardSection(startLine uint32, endLine uint32, headerLine string, props wireguardProperties) wireguardSection { diff --git a/utils/strings.go b/utils/strings.go index 50bf387..1832fbf 100644 --- a/utils/strings.go +++ b/utils/strings.go @@ -2,14 +2,9 @@ package utils import ( "regexp" - "strings" ) -func IndexOffset(s string, search string, start int) int { - return strings.Index(s[start:], search) + start -} - -var trimIndexPattern = regexp.MustCompile(`^\s+(.+?)\s+`) +var trimIndexPattern = regexp.MustCompile(`^\s*(.+?)\s*$`) func GetTrimIndex(s string) []int { indexes := trimIndexPattern.FindStringSubmatchIndex(s)