From 1227949f26264d7357c9ff4bc85c6ef914e0b288 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 9 Mar 2025 19:28:25 +0100 Subject: [PATCH] fix(server): Improve wireguard Signed-off-by: Myzel394 --- .../handlers/wireguard/analyzer/structure.go | 21 ++++--- server/handlers/wireguard/ast/parser.go | 12 +++- server/handlers/wireguard/ast/parser_test.go | 12 +++- server/handlers/wireguard/ast/wireguard.go | 5 +- .../wireguard/ast/wireguard_fields.go | 23 +++++--- server/handlers/wireguard/fields/common.go | 9 +++ .../{documentation-fields.go => fields.go} | 56 +++++++++---------- .../wireguard/fields/fields_formatted.go | 23 ++++++++ .../wireguard/handlers/code-actions.go | 1 - .../wireguard/handlers/completions_body.go | 32 +++++++---- .../wireguard/handlers/completions_test.go | 4 +- .../wireguard/handlers/fetch-code-actions.go | 2 +- .../wireguard/lsp/text-document-hover.go | 42 +++++++------- 13 files changed, 155 insertions(+), 87 deletions(-) create mode 100644 server/handlers/wireguard/fields/common.go rename server/handlers/wireguard/fields/{documentation-fields.go => fields.go} (94%) create mode 100644 server/handlers/wireguard/fields/fields_formatted.go diff --git a/server/handlers/wireguard/analyzer/structure.go b/server/handlers/wireguard/analyzer/structure.go index b399312..9f977a5 100644 --- a/server/handlers/wireguard/analyzer/structure.go +++ b/server/handlers/wireguard/analyzer/structure.go @@ -12,6 +12,7 @@ import ( func analyzeStructureIsValid(ctx *analyzerContext) { for _, section := range ctx.document.Config.Sections { + normalizedHeaderName := fields.CreateNormalizedName(section.Header.Name) // Whether to check if the property is allowed in the section checkAllowedProperty := true @@ -21,7 +22,7 @@ func analyzeStructureIsValid(ctx *analyzerContext) { Range: section.Header.ToLSPRange(), Severity: &common.SeverityError, }) - } else if !utils.KeyExists(fields.OptionsHeaderMap, section.Header.Name) { + } else if !utils.KeyExists(fields.OptionsHeaderMap, normalizedHeaderName) { ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{ Message: fmt.Sprintf("Unknown section '%s'. It must be one of: [Interface], [Peer]", section.Header.Name), Range: section.Header.ToLSPRange(), @@ -31,7 +32,7 @@ func analyzeStructureIsValid(ctx *analyzerContext) { checkAllowedProperty = false } - if len(section.Properties) == 0 { + if section.Properties.Size() == 0 { ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{ Message: "This section is empty", Range: section.Header.ToLSPRange(), @@ -41,9 +42,13 @@ func analyzeStructureIsValid(ctx *analyzerContext) { }, }) } else { - existingProperties := make(map[string]*ast.WGProperty) + existingProperties := make(map[fields.NormalizedName]*ast.WGProperty) + + it := section.Properties.Iterator() + for it.Next() { + property := it.Value().(*ast.WGProperty) + normalizedPropertyName := fields.CreateNormalizedName(property.Key.Name) - for _, property := range section.Properties { if property.Key.Name == "" { ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{ Message: "This property is missing a name", @@ -61,21 +66,23 @@ func analyzeStructureIsValid(ctx *analyzerContext) { } if checkAllowedProperty { - options := fields.OptionsHeaderMap[section.Header.Name] + options := fields.OptionsHeaderMap[normalizedHeaderName] - if !utils.KeyExists(options, property.Key.Name) { + if !utils.KeyExists(options, normalizedPropertyName) { ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{ Message: fmt.Sprintf("Unknown property '%s'", property.Key.Name), Range: property.Key.ToLSPRange(), Severity: &common.SeverityError, }) - } else if existingProperty, found := existingProperties[property.Key.Name]; found { + } else if existingProperty, found := existingProperties[normalizedPropertyName]; found { ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{ Message: fmt.Sprintf("Property '%s' has already been defined on line %d", property.Key.Name, existingProperty.Start.Line+1), Severity: &common.SeverityError, Range: existingProperty.ToLSPRange(), }) } + + existingProperties[normalizedPropertyName] = property } } } diff --git a/server/handlers/wireguard/ast/parser.go b/server/handlers/wireguard/ast/parser.go index 441232c..ebed861 100644 --- a/server/handlers/wireguard/ast/parser.go +++ b/server/handlers/wireguard/ast/parser.go @@ -6,6 +6,9 @@ import ( "fmt" "regexp" "strings" + + "github.com/emirpasic/gods/maps/treemap" + gods "github.com/emirpasic/gods/utils" ) func NewWGConfig() *WGConfig { @@ -80,7 +83,7 @@ func (c *WGConfig) Parse(input string) []common.LSPError { }, Name: name, }, - Properties: make(map[uint32]*WGProperty), + Properties: treemap.NewWith(gods.UInt32Comparator), } c.Sections = append(c.Sections, currentSection) @@ -119,7 +122,7 @@ func (c *WGConfig) Parse(input string) []common.LSPError { // Incomplete property indexes := utils.GetTrimIndex(line) - currentSection.Properties[lineNumber] = &WGProperty{ + newProperty := &WGProperty{ Key: WGPropertyKey{ LocationRange: common.LocationRange{ Start: common.Location{ @@ -134,6 +137,8 @@ func (c *WGConfig) Parse(input string) []common.LSPError { Name: line[indexes[0]:indexes[1]], }, } + + currentSection.Properties.Put(lineNumber, newProperty) } else { // Fully written out property @@ -217,7 +222,7 @@ func (c *WGConfig) Parse(input string) []common.LSPError { } // And lastly, add the property - currentSection.Properties[lineNumber] = &WGProperty{ + newProperty := &WGProperty{ LocationRange: common.LocationRange{ Start: common.Location{ Line: lineNumber, @@ -233,6 +238,7 @@ func (c *WGConfig) Parse(input string) []common.LSPError { Separator: &separator, Value: value, } + currentSection.Properties.Put(lineNumber, newProperty) } } diff --git a/server/handlers/wireguard/ast/parser_test.go b/server/handlers/wireguard/ast/parser_test.go index 2a0981b..e75f263 100644 --- a/server/handlers/wireguard/ast/parser_test.go +++ b/server/handlers/wireguard/ast/parser_test.go @@ -48,15 +48,21 @@ PublicKey = 1234567890 t.Errorf("Parse: Expected sections to be present on lines 0, 1, and 2") } - if !(config.Sections[0].Properties[4].Key.Name == "PrivateKey" && config.Sections[0].Properties[4].Value.Value == "1234567890") { + rawFourthProperty, _ := config.Sections[0].Properties.Get(uint32(4)) + fourthProperty := rawFourthProperty.(*WGProperty) + if !(fourthProperty.Key.Name == "PrivateKey" && fourthProperty.Value.Value == "1234567890") { t.Errorf("Parse: Expected property line 4 to be correct") } - if !(config.Sections[0].Properties[5].Key.Name == "Address" && config.Sections[0].Properties[5].Value.Value == "10.0.0.1") { + rawFifthProperty, _ := config.Sections[0].Properties.Get(uint32(5)) + fifthProperty := rawFifthProperty.(*WGProperty) + if !(fifthProperty.Key.Name == "Address" && fifthProperty.Value.Value == "10.0.0.1") { t.Errorf("Parse: Expected property line 5 to be correct") } - if !(config.Sections[1].Properties[10].Key.Name == "PublicKey" && config.Sections[1].Properties[10].Value.Value == "1234567890") { + rawTenthProperty, _ := config.Sections[1].Properties.Get(uint32(10)) + tenthProperty := rawTenthProperty.(*WGProperty) + if !(tenthProperty.Key.Name == "PublicKey" && tenthProperty.Value.Value == "1234567890") { t.Errorf("Parse: Expected property line 10 to be correct") } } diff --git a/server/handlers/wireguard/ast/wireguard.go b/server/handlers/wireguard/ast/wireguard.go index ed29233..5c3d520 100644 --- a/server/handlers/wireguard/ast/wireguard.go +++ b/server/handlers/wireguard/ast/wireguard.go @@ -2,6 +2,7 @@ package ast import ( "config-lsp/common" + "github.com/emirpasic/gods/maps/treemap" ) type WGPropertyKey struct { @@ -34,8 +35,8 @@ type WGHeader struct { type WGSection struct { common.LocationRange Header WGHeader - // map of: line number -> WGProperty - Properties map[uint32]*WGProperty + // [uint32]WGProperty: line number -> WGProperty + Properties *treemap.Map } type WGConfig struct { diff --git a/server/handlers/wireguard/ast/wireguard_fields.go b/server/handlers/wireguard/ast/wireguard_fields.go index 053283d..5135153 100644 --- a/server/handlers/wireguard/ast/wireguard_fields.go +++ b/server/handlers/wireguard/ast/wireguard_fields.go @@ -1,7 +1,6 @@ package ast import ( - "config-lsp/utils" "slices" ) @@ -36,11 +35,17 @@ func (c *WGConfig) FindPropertyByLine(line uint32) *WGProperty { return nil } - return section.Properties[line] + if property, found := section.Properties.Get(line); found { + return property.(*WGProperty) + } + + return nil } func (s *WGSection) FindFirstPropertyByName(name string) *WGProperty { - for _, property := range s.Properties { + it := s.Properties.Iterator() + for it.Next() { + property := it.Value().(*WGProperty) if property.Key.Name == name { return property } @@ -50,7 +55,9 @@ func (s *WGSection) FindFirstPropertyByName(name string) *WGProperty { } func (s *WGSection) FindPropertyByName(name string) *WGProperty { - for _, property := range s.Properties { + it := s.Properties.Iterator() + for it.Next() { + property := it.Value().(*WGProperty) if property.Key.Name == name { return property } @@ -60,11 +67,11 @@ func (s *WGSection) FindPropertyByName(name string) *WGProperty { } func (s *WGSection) GetLastProperty() *WGProperty { - if len(s.Properties) == 0 { + if s.Properties.Size() == 0 { return nil } - lastLine := utils.FindBiggestKey(s.Properties) - - return s.Properties[lastLine] + lastLine, _ := s.Properties.Max() + lastProperty, _ := s.Properties.Get(lastLine) + return lastProperty.(*WGProperty) } diff --git a/server/handlers/wireguard/fields/common.go b/server/handlers/wireguard/fields/common.go new file mode 100644 index 0000000..5881ab5 --- /dev/null +++ b/server/handlers/wireguard/fields/common.go @@ -0,0 +1,9 @@ +package fields + +import "strings" + +type NormalizedName string + +func CreateNormalizedName(s string) NormalizedName { + return NormalizedName(strings.ToLower(s)) +} diff --git a/server/handlers/wireguard/fields/documentation-fields.go b/server/handlers/wireguard/fields/fields.go similarity index 94% rename from server/handlers/wireguard/fields/documentation-fields.go rename to server/handlers/wireguard/fields/fields.go index 20204ce..6224d01 100644 --- a/server/handlers/wireguard/fields/documentation-fields.go +++ b/server/handlers/wireguard/fields/fields.go @@ -25,8 +25,8 @@ var maxPortValue = 65535 var minMTUValue = 68 var maxMTUValue = 1500 -var InterfaceOptions = map[string]docvalues.DocumentationValue{ - "Address": { +var InterfaceOptions = map[NormalizedName]docvalues.DocumentationValue{ + "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. ## Examples @@ -49,7 +49,7 @@ You can also specify multiple subnets or IPv6 subnets like so: AllowRange: true, }, }, - "ListenPort": { + "listenport": { Documentation: `When the node is acting as a public bounce server, it should hardcode a port to listen for incoming VPN connections from the public internet. Clients not acting as relays should not set this value. If not specified, chosen randomly. ## Examples @@ -66,14 +66,14 @@ Using custom WireGuard port Max: &maxPortValue, }, }, - "PrivateKey": { + "privatekey": { Documentation: `This is the private key for the local node, never shared with other servers. All nodes must have a private key set, regardless of whether they are public bounce servers relaying traffic, or simple clients joining the VPN. This key can be generated with [wg genkey > example.key] `, Value: docvalues.StringValue{}, }, - "DNS": { + "dns": { Documentation: `The DNS server(s) to announce to VPN clients via DHCP, most clients will use this server for DNS requests over the VPN, but clients can also override this value locally on their nodes The value can be left unconfigured to use the system's default DNS servers @@ -97,7 +97,7 @@ or multiple DNS servers can be provided }, }, }, - "Table": { + "table": { Documentation: `Optionally defines which routing table to use for the WireGuard routes, not necessary to configure for most setups. There are two special values: ‘off’ disables the creation of routes altogether, and ‘auto’ (the default) adds routes to the default table and enables special handling of default routes. @@ -127,7 +127,7 @@ https://git.zx2c4.com/WireGuard/about/src/tools/man/wg-quick.8 }, }, }, - "MTU": { + "mtu": { Documentation: `Optionally defines the maximum transmission unit (MTU, aka packet/frame size) to use when connecting to the peer, not necessary to configure for most setups. The MTU is automatically determined from the endpoint addresses or the system default route, which is usually a sane choice. @@ -142,7 +142,7 @@ https://git.zx2c4.com/WireGuard/about/src/tools/man/wg-quick.8 Max: &maxMTUValue, }, }, - "PreUp": { + "preup": { Documentation: `Optionally run a command before the interface is brought up. This option can be specified multiple times, with commands executed in the order they appear in the file. ## Examples @@ -152,7 +152,7 @@ Add an IP route PreUp = ip rule add ipproto tcp dport 22 table 1234 `, Value: docvalues.StringValue{}, }, - "PostUp": { + "postup": { Documentation: `Optionally run a command after the interface is brought up. This option can appear multiple times, as with PreUp ## Examples @@ -182,7 +182,7 @@ Force WireGuard to re-resolve IP address for peer domain `, Value: docvalues.StringValue{}, }, - "PreDown": { + "predown": { Documentation: `Optionally run a command before the interface is brought down. This option can appear multiple times, as with PreUp ## Examples @@ -196,7 +196,7 @@ Hit a webhook on another server `, Value: docvalues.StringValue{}, }, - "PostDown": { + "postdown": { Documentation: `Optionally run a command after the interface is brought down. This option can appear multiple times, as with PreUp ## Examples @@ -215,21 +215,21 @@ Remove the iptables rule that forwards packets on the WireGuard interface `, Value: docvalues.StringValue{}, }, - "FwMark": { + "fwmark": { Documentation: "a 32-bit fwmark for outgoing packets. If set to 0 or \"off\", this option is disabled. May be specified in hexadecimal by prepending \"0x\". Optional", Value: docvalues.StringValue{}, }, } -var InterfaceAllowedDuplicateFields = map[string]struct{}{ - "PreUp": {}, - "PostUp": {}, - "PreDown": {}, - "PostDown": {}, +var InterfaceAllowedDuplicateFields = map[NormalizedName]struct{}{ + "preup": {}, + "postup": {}, + "predown": {}, + "postdown": {}, } -var PeerOptions = map[string]docvalues.DocumentationValue{ - "Endpoint": { +var PeerOptions = map[NormalizedName]docvalues.DocumentationValue{ + "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. ## Examples @@ -243,7 +243,7 @@ Endpoint is a hostname/FQDN `, Value: docvalues.StringValue{}, }, - "AllowedIPs": { + "allowedips": { Documentation: `This defines the IP ranges for which a peer will route traffic. On simple clients, this is usually a single address (the VPN address of the simple client itself). For bounce servers this will be a range of the IPs or subnets that the relay server is capable of routing traffic for. Multiple IPs and subnets may be specified using comma-separated IPv4 or IPv6 CIDR notation (from a single /32 or /128 address, all the way up to 0.0.0.0/0 and ::/0 to indicate a default route to send all internet and VPN traffic through that peer). This option may be specified multiple times. When deciding how to route a packet, the system chooses the most specific route first, and falls back to broader routes. So for a packet destined to 192.0.2.3, the system would first look for a peer advertising 192.0.2.3/32 specifically, and would fall back to a peer advertising 192.0.2.1/24 or a larger range like 0.0.0.0/0 as a last resort. @@ -280,7 +280,7 @@ Peer is a relay server that routes to itself and all nodes on its local LAN }, }, }, - "PublicKey": { + "publickey": { Documentation: `This is the public key for the remote node, shareable with all peers. All nodes must have a public key set, regardless of whether they are public bounce servers relaying traffic, or simple clients joining the VPN. This key can be generated with wg pubkey < example.key > example.key.pub. (see above for how to generate the private key example.key) @@ -291,7 +291,7 @@ This key can be generated with wg pubkey < example.key > example.key.pub. (see a `, Value: docvalues.StringValue{}, }, - "PersistentKeepalive": { + "persistentkeepalive": { Documentation: `If the connection is going from a NAT-ed peer to a public peer, the node behind the NAT must regularly send an outgoing ping in order to keep the bidirectional connection alive in the NAT router's connection table. ## Examples @@ -310,17 +310,17 @@ Oocal NAT-ed node to remote public node `, Value: docvalues.PositiveNumberValue(), }, - "PresharedKey": { + "presharedkey": { Documentation: "Optionally defines a pre-shared key for the peer, used to authenticate the connection. This is not necessary, but strongly recommended for security.", Value: docvalues.StringValue{}, }, } -var PeerAllowedDuplicateFields = map[string]struct{}{ - "AllowedIPs": {}, +var PeerAllowedDuplicateFields = map[NormalizedName]struct{}{ + "allowedips": {}, } -var OptionsHeaderMap = map[string](map[string]docvalues.DocumentationValue){ - "Interface": InterfaceOptions, - "Peer": PeerOptions, +var OptionsHeaderMap = map[NormalizedName](map[NormalizedName]docvalues.DocumentationValue){ + "interface": InterfaceOptions, + "peer": PeerOptions, } diff --git a/server/handlers/wireguard/fields/fields_formatted.go b/server/handlers/wireguard/fields/fields_formatted.go new file mode 100644 index 0000000..fc4ed4b --- /dev/null +++ b/server/handlers/wireguard/fields/fields_formatted.go @@ -0,0 +1,23 @@ +package fields + +var AllOptionsFormatted = map[NormalizedName]string{ + // Interface + "address": "Address", + "listenport": "ListenPort", + "privatekey": "PrivateKey", + "dns": "DNS", + "table": "Table", + "mtu": "MTU", + "preup": "PreUp", + "postup": "PostUp", + "predown": "Predown", + "postdown": "PostDown", + "fwmark": "FwMark", + + // Peer Options + "endpoint": "Endpoint", + "allowedips": "AllowedIPs", + "publickey": "PublicKey", + "persistentkeepalive": "PersistentKeepalive", + "presharedkey": "PresharedKey", +} diff --git a/server/handlers/wireguard/handlers/code-actions.go b/server/handlers/wireguard/handlers/code-actions.go index bbdad88..566890b 100644 --- a/server/handlers/wireguard/handlers/code-actions.go +++ b/server/handlers/wireguard/handlers/code-actions.go @@ -122,4 +122,3 @@ func (args CodeActionGeneratePresharedKeyArgs) RunCommand(d *wireguard.WGDocumen }, }, nil } - diff --git a/server/handlers/wireguard/handlers/completions_body.go b/server/handlers/wireguard/handlers/completions_body.go index b844734..051f9c0 100644 --- a/server/handlers/wireguard/handlers/completions_body.go +++ b/server/handlers/wireguard/handlers/completions_body.go @@ -55,6 +55,11 @@ func GetSectionBodyCompletions( // In this case, the user may want to add a property or add a new section. // We should therefore suggest both options. + isLineEmpty := property == nil + if !isLineEmpty { + return completions, nil + } + // Check if previous line is empty previousLineProperty := d.Config.FindPropertyByLine(params.Position.Line - 1) @@ -114,8 +119,8 @@ func getKeyCompletions( onlySeparator bool, currentLine uint32, ) []protocol.CompletionItem { - options := make(map[string]docvalues.DocumentationValue) - allowedDuplicatedFields := make(map[string]struct{}) + options := make(map[fields.NormalizedName]docvalues.DocumentationValue) + allowedDuplicatedFields := make(map[fields.NormalizedName]struct{}) switch section.Header.Name { case "Interface": @@ -127,8 +132,11 @@ func getKeyCompletions( } // Remove existing, non-duplicate options - for _, property := range section.Properties { - if _, found := allowedDuplicatedFields[property.Key.Name]; found { + it := section.Properties.Iterator() + for it.Next() { + property := it.Value().(*ast.WGProperty) + normalizedName := fields.CreateNormalizedName(property.Key.Name) + if _, found := allowedDuplicatedFields[normalizedName]; found { continue } @@ -137,23 +145,24 @@ func getKeyCompletions( continue } - delete(options, property.Key.Name) + delete(options, normalizedName) } kind := protocol.CompletionItemKindField return utils.MapMapToSlice( options, - func(rawOptionName string, value docvalues.DocumentationValue) protocol.CompletionItem { + func(rawOptionName fields.NormalizedName, value docvalues.DocumentationValue) protocol.CompletionItem { + optionName := fields.AllOptionsFormatted[rawOptionName] var label string var insertText string if onlySeparator { - label = rawOptionName + " = " + label = optionName + " = " insertText = "= " } else { - label = rawOptionName - insertText = rawOptionName + " = " + label = optionName + insertText = optionName + " = " } return protocol.CompletionItem{ @@ -172,13 +181,14 @@ func getValueCompletions( cursorPosition common.CursorPosition, ) []protocol.CompletionItem { // TODO: Normalize section header name - options, found := fields.OptionsHeaderMap[section.Header.Name] + normalizedHeaderName := fields.CreateNormalizedName(section.Header.Name) + options, found := fields.OptionsHeaderMap[normalizedHeaderName] if !found { return nil } - option, found := options[property.Key.Name] + option, found := options[fields.CreateNormalizedName(property.Key.Name)] if !found { return nil diff --git a/server/handlers/wireguard/handlers/completions_test.go b/server/handlers/wireguard/handlers/completions_test.go index c8f4118..fce079e 100644 --- a/server/handlers/wireguard/handlers/completions_test.go +++ b/server/handlers/wireguard/handlers/completions_test.go @@ -227,10 +227,10 @@ Table = completions, err := SuggestCompletions(d, params) if err != nil { - t.Errorf("getCompletionsForPropertyLine failed with error: %v", err) + t.Errorf("SuggestComplete failed with error: %v", err) } if !(len(completions) == 2) { - t.Errorf("getCompletionsForPropertyLine: Expected completions, but got %v", len(completions)) + t.Errorf("Expected 2 completions, but got %v", len(completions)) } } diff --git a/server/handlers/wireguard/handlers/fetch-code-actions.go b/server/handlers/wireguard/handlers/fetch-code-actions.go index 389ceba..f4263f4 100644 --- a/server/handlers/wireguard/handlers/fetch-code-actions.go +++ b/server/handlers/wireguard/handlers/fetch-code-actions.go @@ -27,7 +27,7 @@ func GetKeepaliveCodeActions( return []protocol.CodeAction{ { - Title: "Add PersistentKeepalive", + Title: "Add PersistentKeepalive", Edit: &protocol.WorkspaceEdit{ Changes: map[protocol.DocumentUri][]protocol.TextEdit{ params.TextDocument.URI: { diff --git a/server/handlers/wireguard/lsp/text-document-hover.go b/server/handlers/wireguard/lsp/text-document-hover.go index bc54cec..1f2945f 100644 --- a/server/handlers/wireguard/lsp/text-document-hover.go +++ b/server/handlers/wireguard/lsp/text-document-hover.go @@ -10,30 +10,30 @@ func TextDocumentHover( params *protocol.HoverParams, ) (*protocol.Hover, error) { /* - p := documentParserMap[params.TextDocument.URI] + p := documentParserMap[params.TextDocument.URI] - switch p.GetTypeByLine(params.Position.Line) { - case parser.LineTypeComment: - return nil, nil - case parser.LineTypeEmpty: - return nil, nil - case parser.LineTypeHeader: - fallthrough - case parser.LineTypeProperty: - documentation := handlers.GetHoverContent( - *p, - params.Position.Line, - params.Position.Character, - ) + switch p.GetTypeByLine(params.Position.Line) { + case parser.LineTypeComment: + return nil, nil + case parser.LineTypeEmpty: + return nil, nil + case parser.LineTypeHeader: + fallthrough + case parser.LineTypeProperty: + documentation := handlers.GetHoverContent( + *p, + params.Position.Line, + params.Position.Character, + ) - hover := protocol.Hover{ - Contents: protocol.MarkupContent{ - Kind: protocol.MarkupKindMarkdown, - Value: strings.Join(documentation, "\n"), - }, + hover := protocol.Hover{ + Contents: protocol.MarkupContent{ + Kind: protocol.MarkupKindMarkdown, + Value: strings.Join(documentation, "\n"), + }, + } + return &hover, nil } - return &hover, nil - } */ return nil, nil