feat: Improve root handler; Improve language detection; Bugfixes

This commit is contained in:
Myzel394 2024-08-25 15:49:27 +02:00
parent 0ed506f604
commit 67085d761e
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
24 changed files with 426 additions and 71 deletions

View File

@ -1,10 +1,21 @@
package common
import (
protocol "github.com/tliron/glsp/protocol_3_16"
)
type LSPError struct {
Range LocationRange
Err error
}
func (l LSPError) ToDiagnostic() protocol.Diagnostic {
return protocol.Diagnostic{
Range: l.Range.ToLSPRange(),
Message: l.Err.Error(),
}
}
type SyntaxError struct {
Message string
}

View File

@ -1,5 +1,7 @@
package common
import protocol "github.com/tliron/glsp/protocol_3_16"
type Location struct {
Line uint32
Character uint32
@ -10,11 +12,37 @@ type LocationRange struct {
End Location
}
func (l LocationRange) ToLSPRange() protocol.Range {
return protocol.Range{
Start: protocol.Position{
Line: l.Start.Line,
Character: l.Start.Character,
},
End: protocol.Position{
Line: l.End.Line,
Character: l.End.Character,
},
}
}
func (l *LocationRange) ChangeBothLines(newLine uint32) {
l.Start.Line = newLine
l.End.Line = newLine
}
func CreateFullLineRange(line uint32) LocationRange {
return LocationRange{
Start: Location{
Line: line,
Character: 0,
},
End: Location{
Line: line,
Character: 999999,
},
}
}
func CreateSingleCharRange(line uint32, character uint32) LocationRange {
return LocationRange{
Start: Location{

Binary file not shown.

View File

@ -1 +1,32 @@
package analyzer
import (
"config-lsp/common"
"config-lsp/handlers/hosts/tree"
"config-lsp/utils"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func Analyze(parser tree.HostsParser) []protocol.Diagnostic {
errors := analyzeEntriesAreValid(parser)
if len(errors) > 0 {
return utils.Map(
errors,
func(err common.LSPError) protocol.Diagnostic {
return err.ToDiagnostic()
},
)
}
errors = append(errors, analyzeDoubleIPs(parser)...)
errors = append(errors, analyzeDoubleHostNames(parser)...)
return utils.Map(
errors,
func(err common.LSPError) protocol.Diagnostic {
return err.ToDiagnostic()
},
)
}

View File

@ -52,11 +52,11 @@ func TestWorksWithDoubleIPs(
t.Errorf("Expected 1 error, but got %v", len(errors))
}
if !(errors[0].Range.Start.Line == 3) {
if !(errors[0].Range.Start.Line == 2) {
t.Errorf("Expected error on line 3, but got %v", errors[0].Range.Start.Line)
}
if !(errors[0].Err.(DuplicateIPDeclaration).AlreadyFoundAt == 1) {
if !(errors[0].Err.(DuplicateIPDeclaration).AlreadyFoundAt == 0) {
t.Errorf("Expected error on line 1, but got %v", errors[0].Err.(DuplicateIPDeclaration).AlreadyFoundAt)
}
}

View File

@ -39,7 +39,7 @@ type hostnameEntry struct {
HostName string
}
func CreateResolverFromParser(p tree.HostsParser) (Resolver, []common.LSPError) {
func createResolverFromParser(p tree.HostsParser) (Resolver, []common.LSPError) {
errors := make([]common.LSPError, 0)
resolver := Resolver{
Entries: make(map[string]ResolverEntry),
@ -91,3 +91,9 @@ func CreateResolverFromParser(p tree.HostsParser) (Resolver, []common.LSPError)
return resolver, errors
}
func analyzeDoubleHostNames(p tree.HostsParser) []common.LSPError {
_, errors := createResolverFromParser(p)
return errors
}

View File

@ -21,7 +21,7 @@ func TestResolverEntriesWorksWithNonOverlapping(
t.Fatalf("PARER FAILED! Expected no errors, but got %v", errors)
}
resolver, errors := CreateResolverFromParser(parser)
resolver, errors := createResolverFromParser(parser)
if len(errors) != 0 {
t.Errorf("Expected no errors, but got %v", errors)
@ -63,7 +63,7 @@ func TestResolverEntriesWithSimpleOverlapping(
t.Fatalf("PARER FAILED! Expected no errors, but got %v", errors)
}
resolver, errors := CreateResolverFromParser(parser)
resolver, errors := createResolverFromParser(parser)
if !(len(errors) == 1) {
t.Errorf("Expected 1 error, but got %v", len(errors))
@ -93,7 +93,7 @@ func TestResolverEntriesWithComplexOverlapping(
t.Fatalf("PARER FAILED! Expected no errors, but got %v", errors)
}
resolver, errors := CreateResolverFromParser(parser)
resolver, errors := createResolverFromParser(parser)
if !(len(errors) == 1) {
t.Errorf("Expected 1 error, but got %v", len(errors))

View File

@ -0,0 +1,33 @@
package analyzer
import (
"config-lsp/common"
"config-lsp/handlers/hosts/tree"
"errors"
)
func analyzeEntriesAreValid(
parser tree.HostsParser,
) []common.LSPError {
err := make([]common.LSPError, 0)
for lineNumber, entry := range parser.Tree.Entries {
if entry.IPAddress == nil {
err = append(err, common.LSPError{
Range: common.CreateFullLineRange(lineNumber),
Err: errors.New("IP Address is required"),
})
continue
}
if entry.Hostname == nil {
err = append(err, common.LSPError{
Range: common.CreateFullLineRange(lineNumber),
Err: errors.New("Hostname is required"),
})
continue
}
}
return err
}

View File

@ -0,0 +1,9 @@
package lsp
import (
"config-lsp/handlers/hosts/tree"
protocol "github.com/tliron/glsp/protocol_3_16"
)
var documentParserMap = map[protocol.DocumentUri]*tree.HostsParser{}

View File

@ -0,0 +1,12 @@
package lsp
import (
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
// p := documentParserMap[params.TextDocument.URI]
return nil, nil
}

View File

@ -0,0 +1,41 @@
package lsp
import (
"config-lsp/common"
"config-lsp/handlers/hosts/handlers/analyzer"
"config-lsp/utils"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func TextDocumentDidChange(
context *glsp.Context,
params *protocol.DidChangeTextDocumentParams,
) error {
content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text
common.ClearDiagnostics(context, params.TextDocument.URI)
parser := documentParserMap[params.TextDocument.URI]
parser.Clear()
diagnostics := make([]protocol.Diagnostic, 0)
errors := parser.Parse(content)
if len(errors) > 0 {
diagnostics = append(diagnostics, utils.Map(
errors,
func(err common.LSPError) protocol.Diagnostic {
return err.ToDiagnostic()
},
)...)
}
diagnostics = append(diagnostics, analyzer.Analyze(*parser)...)
if len(diagnostics) > 0 {
common.SendDiagnostics(context, params.TextDocument.URI, diagnostics)
}
return nil
}

View File

@ -0,0 +1,12 @@
package lsp
import (
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func TextDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDocumentParams) error {
delete(documentParserMap, params.TextDocument.URI)
return nil
}

View File

@ -0,0 +1,41 @@
package lsp
import (
"config-lsp/common"
"config-lsp/handlers/hosts/handlers/analyzer"
"config-lsp/handlers/hosts/tree"
"config-lsp/utils"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func TextDocumentDidOpen(
context *glsp.Context,
params *protocol.DidOpenTextDocumentParams,
) error {
common.ClearDiagnostics(context, params.TextDocument.URI)
parser := tree.CreateNewHostsParser()
documentParserMap[params.TextDocument.URI] = &parser
errors := parser.Parse(params.TextDocument.Text)
diagnostics := utils.Map(
errors,
func(err common.LSPError) protocol.Diagnostic {
return err.ToDiagnostic()
},
)
diagnostics = append(
diagnostics,
analyzer.Analyze(parser)...,
)
if len(diagnostics) > 0 {
common.SendDiagnostics(context, params.TextDocument.URI, diagnostics)
}
return nil
}

View File

@ -3,8 +3,8 @@ package tree
import (
"config-lsp/common"
"config-lsp/handlers/hosts/parser"
"config-lsp/utils"
"regexp"
"strings"
"github.com/antlr4-go/antlr/v4"
)
@ -51,10 +51,10 @@ func (p *HostsParser) parseStatement(
func (p *HostsParser) Parse(input string) []common.LSPError {
errors := make([]common.LSPError, 0)
lines := strings.Split(input, "\n")
lines := utils.SplitIntoLines(input)
for rawLineNumber, line := range lines {
lineNumber := uint32(rawLineNumber + 1)
lineNumber := uint32(rawLineNumber)
if commentPattern.MatchString(line) {
p.CommentLines[lineNumber] = struct{}{}

View File

@ -24,44 +24,44 @@ func TestValidSimpleExampleWorks(
t.Errorf("Expected 1 entry, but got %v", len(parser.Tree.Entries))
}
if parser.Tree.Entries[1].IPAddress == nil {
if parser.Tree.Entries[0].IPAddress == nil {
t.Errorf("Expected IP address to be present, but got nil")
}
if !(parser.Tree.Entries[1].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) {
t.Errorf("Expected IP address to be 1.2.3.4, but got %v", parser.Tree.Entries[1].IPAddress.Value)
if !(parser.Tree.Entries[0].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) {
t.Errorf("Expected IP address to be 1.2.3.4, but got %v", parser.Tree.Entries[0].IPAddress.Value)
}
if !(parser.Tree.Entries[1].Hostname.Value == "hello.com") {
t.Errorf("Expected hostname to be hello.com, but got %v", parser.Tree.Entries[1].Hostname.Value)
if !(parser.Tree.Entries[0].Hostname.Value == "hello.com") {
t.Errorf("Expected hostname to be hello.com, but got %v", parser.Tree.Entries[0].Hostname.Value)
}
if !(parser.Tree.Entries[1].Aliases == nil) {
t.Errorf("Expected no aliases, but got %v", parser.Tree.Entries[1].Aliases)
if !(parser.Tree.Entries[0].Aliases == nil) {
t.Errorf("Expected no aliases, but got %v", parser.Tree.Entries[0].Aliases)
}
if !(parser.Tree.Entries[1].Location.Start.Line == 1) {
t.Errorf("Expected line to be 1, but got %v", parser.Tree.Entries[1].Location.Start.Line)
if !(parser.Tree.Entries[0].Location.Start.Line == 0) {
t.Errorf("Expected line to be 1, but got %v", parser.Tree.Entries[0].Location.Start.Line)
}
if !(parser.Tree.Entries[1].Location.Start.Character == 0) {
t.Errorf("Expected start to be 0, but got %v", parser.Tree.Entries[1].Location.Start)
if !(parser.Tree.Entries[0].Location.Start.Character == 0) {
t.Errorf("Expected start to be 0, but got %v", parser.Tree.Entries[0].Location.Start)
}
if !(parser.Tree.Entries[1].Location.End.Character == 16) {
t.Errorf("Expected end to be 16, but got %v", parser.Tree.Entries[1].Location.End.Character)
if !(parser.Tree.Entries[0].Location.End.Character == 16) {
t.Errorf("Expected end to be 16, but got %v", parser.Tree.Entries[0].Location.End.Character)
}
if !(parser.Tree.Entries[1].IPAddress.Location.Start.Line == 1) {
t.Errorf("Expected IP address line to be 1, but got %v", parser.Tree.Entries[1].IPAddress.Location.Start.Line)
if !(parser.Tree.Entries[0].IPAddress.Location.Start.Line == 0) {
t.Errorf("Expected IP address line to be 1, but got %v", parser.Tree.Entries[0].IPAddress.Location.Start.Line)
}
if !(parser.Tree.Entries[1].IPAddress.Location.Start.Character == 0) {
t.Errorf("Expected IP address start to be 0, but got %v", parser.Tree.Entries[1].IPAddress.Location.Start.Character)
if !(parser.Tree.Entries[0].IPAddress.Location.Start.Character == 0) {
t.Errorf("Expected IP address start to be 0, but got %v", parser.Tree.Entries[0].IPAddress.Location.Start.Character)
}
if !(parser.Tree.Entries[1].IPAddress.Location.End.Character == 6) {
t.Errorf("Expected IP address end to be 6, but got %v", parser.Tree.Entries[1].IPAddress.Location.End.Character)
if !(parser.Tree.Entries[0].IPAddress.Location.End.Character == 6) {
t.Errorf("Expected IP address end to be 6, but got %v", parser.Tree.Entries[0].IPAddress.Location.End.Character)
}
if !(len(parser.CommentLines) == 0) {
@ -91,11 +91,11 @@ func TestValidComplexExampleWorks(
t.Errorf("Expected 3 entries, but got %v", len(parser.Tree.Entries))
}
if parser.Tree.Entries[3].IPAddress == nil {
if parser.Tree.Entries[2].IPAddress == nil {
t.Errorf("Expected IP address to be present, but got nil")
}
if !(parser.Tree.Entries[3].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) {
if !(parser.Tree.Entries[2].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) {
t.Errorf("Expected IP address to be 1.2.3.4, but got %v", parser.Tree.Entries[2].IPAddress.Value)
}
@ -103,7 +103,7 @@ func TestValidComplexExampleWorks(
t.Errorf("Expected 1 comment line, but got %v", len(parser.CommentLines))
}
if !(utils.KeyExists(parser.CommentLines, 2)) {
if !(utils.KeyExists(parser.CommentLines, 1)) {
t.Errorf("Expected comment line 2 to exist, but it does not")
}
}
@ -130,15 +130,15 @@ func TestInvalidExampleWorks(
t.Errorf("Expected no comment lines, but got %v", len(parser.CommentLines))
}
if !(parser.Tree.Entries[1].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) {
t.Errorf("Expected IP address to be nil, but got %v", parser.Tree.Entries[1].IPAddress)
if !(parser.Tree.Entries[0].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) {
t.Errorf("Expected IP address to be nil, but got %v", parser.Tree.Entries[0].IPAddress)
}
if !(parser.Tree.Entries[1].Hostname == nil) {
t.Errorf("Expected hostname to be nil, but got %v", parser.Tree.Entries[1].Hostname)
if !(parser.Tree.Entries[0].Hostname == nil) {
t.Errorf("Expected hostname to be nil, but got %v", parser.Tree.Entries[0].Hostname)
}
if !(parser.Tree.Entries[1].Aliases == nil) {
t.Errorf("Expected aliases to be nil, but got %v", parser.Tree.Entries[1].Aliases)
if !(parser.Tree.Entries[0].Aliases == nil) {
t.Errorf("Expected aliases to be nil, but got %v", parser.Tree.Entries[0].Aliases)
}
}

View File

@ -16,12 +16,14 @@ const (
LanguageSSHDConfig SupportedLanguage = "sshd_config"
LanguageFstab SupportedLanguage = "fstab"
LanguageWireguard SupportedLanguage = "languagewireguard"
LanguageHosts SupportedLanguage = "hosts"
)
var AllSupportedLanguages = []string{
string(LanguageSSHDConfig),
string(LanguageFstab),
string(LanguageWireguard),
string(LanguageHosts),
}
type FatalFileNotReadableError struct {
@ -59,11 +61,19 @@ var valueToLanguageMap = map[string]SupportedLanguage{
"wireguard": LanguageWireguard,
"wg": LanguageWireguard,
"languagewireguard": LanguageWireguard,
"host": LanguageHosts,
"hosts": LanguageHosts,
"etc/hosts": LanguageHosts,
}
var typeOverwriteRegex = regexp.MustCompile(`^#\?\s*lsp\.language\s*=\s*(\w+)\s*$`)
var typeOverwriteRegex = regexp.MustCompile(`#\?\s*lsp\.language\s*=\s*(\w+)\s*`)
var wireguardPattern = regexp.MustCompile(`/wg\d+\.conf$`)
var undetectableError = common.ParseError{
Line: 0,
Err: LanguageUndetectableError{},
}
func DetectLanguage(
content string,
advertisedLanguage string,
@ -99,14 +109,13 @@ func DetectLanguage(
return LanguageSSHDConfig, nil
case "file:///etc/fstab":
return LanguageFstab, nil
case "file:///etc/hosts":
return LanguageHosts, nil
}
if strings.HasPrefix(uri, "file:///etc/wireguard/") || wireguardPattern.MatchString(uri) {
return LanguageWireguard, nil
}
return "", common.ParseError{
Line: 0,
Err: LanguageUndetectableError{},
}
return "", undetectableError
}

View File

@ -20,8 +20,14 @@ func (h *RootHandler) AddDocument(uri protocol.DocumentUri, language SupportedLa
h.languageMap[uri] = language
}
func (h *RootHandler) GetLanguageForDocument(uri protocol.DocumentUri) SupportedLanguage {
return h.languageMap[uri]
func (h *RootHandler) GetLanguageForDocument(uri protocol.DocumentUri) *SupportedLanguage {
language, found := h.languageMap[uri]
if !found {
return nil
}
return &language
}
func (h *RootHandler) RemoveDocument(uri protocol.DocumentUri) {

View File

@ -9,14 +9,26 @@ import (
func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) (any, error) {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI)
switch language {
if language == nil {
showParseError(
context,
params.TextDocument.URI,
undetectableError,
)
return nil, undetectableError.Err
}
switch *language {
case LanguageFstab:
return nil, nil
fallthrough
case LanguageHosts:
fallthrough
case LanguageSSHDConfig:
return nil, nil
case LanguageWireguard:
return wireguard.TextDocumentCodeAction(context, params)
}
panic("root-handler/TextDocumentCompletion: unexpected language" + language)
panic("root-handler/TextDocumentCompletion: unexpected language" + *language)
}

View File

@ -2,6 +2,7 @@ package roothandler
import (
"config-lsp/handlers/fstab"
hosts "config-lsp/handlers/hosts/lsp"
wireguard "config-lsp/handlers/wireguard/lsp"
"github.com/tliron/glsp"
@ -11,14 +12,26 @@ import (
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI)
switch language {
if language == nil {
showParseError(
context,
params.TextDocument.URI,
undetectableError,
)
return nil, undetectableError.Err
}
switch *language {
case LanguageFstab:
return fstab.TextDocumentCompletion(context, params)
case LanguageSSHDConfig:
return nil, nil
case LanguageWireguard:
return wireguard.TextDocumentCompletion(context, params)
case LanguageHosts:
return hosts.TextDocumentCompletion(context, params)
}
panic("root-handler/TextDocumentCompletion: unexpected language" + language)
panic("root-handler/TextDocumentCompletion: unexpected language" + *language)
}

View File

@ -2,6 +2,7 @@ package roothandler
import (
"config-lsp/handlers/fstab"
hosts "config-lsp/handlers/hosts/lsp"
wireguard "config-lsp/handlers/wireguard/lsp"
"github.com/tliron/glsp"
@ -11,14 +12,52 @@ import (
func TextDocumentDidChange(context *glsp.Context, params *protocol.DidChangeTextDocumentParams) error {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI)
switch language {
if language == nil {
content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text
newLanguage, err := initFile(
context,
content,
params.TextDocument.URI,
"",
)
if err != nil {
return err
}
language = newLanguage
params := &protocol.DidOpenTextDocumentParams{
TextDocument: protocol.TextDocumentItem{
URI: params.TextDocument.URI,
Text: content,
Version: params.TextDocument.Version,
LanguageID: string(*language),
},
}
switch *language {
case LanguageFstab:
return fstab.TextDocumentDidOpen(context, params)
case LanguageSSHDConfig:
break
case LanguageWireguard:
return wireguard.TextDocumentDidOpen(context, params)
case LanguageHosts:
return hosts.TextDocumentDidOpen(context, params)
}
}
switch *language {
case LanguageFstab:
return fstab.TextDocumentDidChange(context, params)
case LanguageSSHDConfig:
return nil
case LanguageWireguard:
return wireguard.TextDocumentDidChange(context, params)
case LanguageHosts:
return hosts.TextDocumentDidChange(context, params)
}
panic("root-handler/TextDocumentDidChange: unexpected language" + language)
panic("root-handler/TextDocumentDidChange: unexpected language" + *language)
}

View File

@ -1,6 +1,7 @@
package roothandler
import (
hosts "config-lsp/handlers/hosts/lsp"
wireguard "config-lsp/handlers/wireguard/lsp"
"github.com/tliron/glsp"
@ -10,14 +11,26 @@ import (
func TextDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDocumentParams) error {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI)
if language == nil {
showParseError(
context,
params.TextDocument.URI,
undetectableError,
)
return undetectableError.Err
}
delete(openedFiles, params.TextDocument.URI)
rootHandler.RemoveDocument(params.TextDocument.URI)
switch language {
switch *language {
case LanguageFstab:
case LanguageSSHDConfig:
case LanguageWireguard:
return wireguard.TextDocumentDidClose(context, params)
case LanguageHosts:
return hosts.TextDocumentDidClose(context, params)
default:
}

View File

@ -3,6 +3,7 @@ package roothandler
import (
"config-lsp/common"
fstab "config-lsp/handlers/fstab"
hosts "config-lsp/handlers/hosts/lsp"
wireguard "config-lsp/handlers/wireguard/lsp"
"fmt"
@ -15,31 +16,26 @@ func TextDocumentDidOpen(context *glsp.Context, params *protocol.DidOpenTextDocu
// Find the file type
content := params.TextDocument.Text
language, err := DetectLanguage(content, params.TextDocument.LanguageID, params.TextDocument.URI)
language, err := initFile(
context,
content,
params.TextDocument.URI,
params.TextDocument.LanguageID,
)
if err != nil {
parseError := err.(common.ParseError)
showParseError(
context,
params.TextDocument.URI,
parseError,
)
return parseError.Err
return err
}
openedFiles[params.TextDocument.URI] = struct{}{}
// Everything okay, now we can handle the file
rootHandler.AddDocument(params.TextDocument.URI, language)
switch language {
switch *language {
case LanguageFstab:
return fstab.TextDocumentDidOpen(context, params)
case LanguageSSHDConfig:
break
case LanguageWireguard:
return wireguard.TextDocumentDidOpen(context, params)
case LanguageHosts:
return hosts.TextDocumentDidOpen(context, params)
}
panic(fmt.Sprintf("unexpected roothandler.SupportedLanguage: %#v", language))
@ -73,3 +69,30 @@ func showParseError(
},
)
}
func initFile(
context *glsp.Context,
content string,
uri protocol.DocumentUri,
advertisedLanguage string,
) (*SupportedLanguage, error) {
language, err := DetectLanguage(content, advertisedLanguage, uri)
if err != nil {
parseError := err.(common.ParseError)
showParseError(
context,
uri,
parseError,
)
return nil, parseError.Err
}
openedFiles[uri] = struct{}{}
// Everything okay, now we can handle the file
rootHandler.AddDocument(uri, language)
return &language, nil
}

View File

@ -11,14 +11,26 @@ import (
func TextDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI)
switch language {
case LanguageFstab:
return fstab.TextDocumentHover(context, params)
if language == nil {
showParseError(
context,
params.TextDocument.URI,
undetectableError,
)
return nil, undetectableError.Err
}
switch *language {
case LanguageHosts:
fallthrough
case LanguageSSHDConfig:
return nil, nil
case LanguageFstab:
return fstab.TextDocumentHover(context, params)
case LanguageWireguard:
return wireguard.TextDocumentHover(context, params)
}
panic("root-handler/TextDocumentHover: unexpected language" + language)
panic("root-handler/TextDocumentHover: unexpected language" + *language)
}

View File

@ -15,3 +15,7 @@ func GetTrimIndex(s string) []int {
return indexes[2:4]
}
func SplitIntoLines(s string) []string {
return regexp.MustCompile("\r?\n").Split(s, -1)
}