diff --git a/handlers/wireguard/parser_completions_test.go b/handlers/wireguard/parser_completions_test.go index 26c2fed..ab68fb7 100644 --- a/handlers/wireguard/parser_completions_test.go +++ b/handlers/wireguard/parser_completions_test.go @@ -45,3 +45,54 @@ PrivateKey = 1234567890 t.Fatalf("getCompletionsForEmptyLine: Expected %v completions, but got %v", expected, len(completions)) } } + +func TestEmptyRootCompletionsWork( + t *testing.T, +) { + sample := dedent(` + `) + + parser := createWireguardParser() + parser.parseFromString(sample) + + completions := parser.getRootCompletionsForEmptyLine() + + if len(completions) != 2 { + t.Fatalf("getRootCompletionsForEmptyLine: Expected 2 completions, but got %v", len(completions)) + } +} + +func TestInterfaceSectionRootCompletionsBeforeWork( + t *testing.T, +) { + sample := dedent(` + +[Interface] +`) + parser := createWireguardParser() + parser.parseFromString(sample) + + completions := parser.getRootCompletionsForEmptyLine() + + if len(completions) != 1 { + t.Fatalf("getRootCompletionsForEmptyLine: Expected 1 completions, but got %v", len(completions)) + } +} + +func TestInterfaceAndPeerSectionRootCompletionsWork( + t *testing.T, +) { + sample := dedent(` +[Interface] + +[Peer] +`) + parser := createWireguardParser() + parser.parseFromString(sample) + + completions := parser.getRootCompletionsForEmptyLine() + + if len(completions) != 1 { + t.Fatalf("getRootCompletionsForEmptyLine: Expected 1 completions, but got %v", len(completions)) + } +} diff --git a/handlers/wireguard/text-document-completion.go b/handlers/wireguard/text-document-completion.go index 79854ea..54807a0 100644 --- a/handlers/wireguard/text-document-completion.go +++ b/handlers/wireguard/text-document-completion.go @@ -20,7 +20,7 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa return nil, nil case LineTypeEmpty: if section == nil { - return nil, nil + return parser.getRootCompletionsForEmptyLine(), nil } return section.getCompletionsForEmptyLine() diff --git a/handlers/wireguard/parser.go b/handlers/wireguard/wg-parser.go similarity index 77% rename from handlers/wireguard/parser.go rename to handlers/wireguard/wg-parser.go index 21286cd..b48da6d 100644 --- a/handlers/wireguard/parser.go +++ b/handlers/wireguard/wg-parser.go @@ -5,6 +5,8 @@ import ( "regexp" "slices" "strings" + + protocol "github.com/tliron/glsp/protocol_3_16" ) var commentPattern = regexp.MustCompile(`^\s*(;|#)`) @@ -27,6 +29,45 @@ func (p *wireguardParser) clear() { p.CommentLines = map[uint32]struct{}{} } +func (p wireguardParser) hasInterfaceSection() bool { + for _, section := range p.Sections { + if section.Name != nil && *section.Name == "Interface" { + return true + } + } + + return false +} + +func (p wireguardParser) getRootCompletionsForEmptyLine() []protocol.CompletionItem { + kind := protocol.CompletionItemKindInterface + completions := []protocol.CompletionItem{} + + if !p.hasInterfaceSection() { + sortText := "Z" + insertText := "[Interface]\n" + completions = append(completions, protocol.CompletionItem{ + Label: "[Interface]", + InsertText: &insertText, + Kind: &kind, + Documentation: "An interface section represents the local node's configuration", + SortText: &sortText, + }) + } + + sortText := "A" + insertText := "[Peer]\n" + completions = append(completions, protocol.CompletionItem{ + Label: "[Peer]", + InsertText: &insertText, + Kind: &kind, + Documentation: "A peer section represents a peer that this node communicates with", + SortText: &sortText, + }) + + return completions +} + func createWireguardParser() wireguardParser { parser := wireguardParser{} parser.clear()