diff --git a/common/errors.go b/common/errors.go index abce7b1..bac098b 100644 --- a/common/errors.go +++ b/common/errors.go @@ -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 } diff --git a/common/location.go b/common/location.go index 6fe1138..e99bf38 100644 --- a/common/location.go +++ b/common/location.go @@ -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{ diff --git a/config-lsp b/config-lsp deleted file mode 100755 index 9b34894..0000000 Binary files a/config-lsp and /dev/null differ diff --git a/handlers/hosts/handlers/analyzer/analyzer.go b/handlers/hosts/handlers/analyzer/analyzer.go index 220b283..4863c19 100644 --- a/handlers/hosts/handlers/analyzer/analyzer.go +++ b/handlers/hosts/handlers/analyzer/analyzer.go @@ -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() + }, + ) +} diff --git a/handlers/hosts/handlers/analyzer/double_ips_test.go b/handlers/hosts/handlers/analyzer/double_ips_test.go index be09fd5..9a2317d 100644 --- a/handlers/hosts/handlers/analyzer/double_ips_test.go +++ b/handlers/hosts/handlers/analyzer/double_ips_test.go @@ -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) } } diff --git a/handlers/hosts/handlers/analyzer/resolver.go b/handlers/hosts/handlers/analyzer/resolver.go index d0c4a23..2fbd0d5 100644 --- a/handlers/hosts/handlers/analyzer/resolver.go +++ b/handlers/hosts/handlers/analyzer/resolver.go @@ -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 +} diff --git a/handlers/hosts/handlers/analyzer/resolver_test.go b/handlers/hosts/handlers/analyzer/resolver_test.go index b24c4b8..30dc76b 100644 --- a/handlers/hosts/handlers/analyzer/resolver_test.go +++ b/handlers/hosts/handlers/analyzer/resolver_test.go @@ -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)) diff --git a/handlers/hosts/handlers/analyzer/values.go b/handlers/hosts/handlers/analyzer/values.go new file mode 100644 index 0000000..695857f --- /dev/null +++ b/handlers/hosts/handlers/analyzer/values.go @@ -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 +} diff --git a/handlers/hosts/lsp/shared.go b/handlers/hosts/lsp/shared.go new file mode 100644 index 0000000..1709221 --- /dev/null +++ b/handlers/hosts/lsp/shared.go @@ -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{} diff --git a/handlers/hosts/lsp/text-document-completion.go b/handlers/hosts/lsp/text-document-completion.go new file mode 100644 index 0000000..8c0754b --- /dev/null +++ b/handlers/hosts/lsp/text-document-completion.go @@ -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 +} diff --git a/handlers/hosts/lsp/text-document-did-change.go b/handlers/hosts/lsp/text-document-did-change.go new file mode 100644 index 0000000..f9abf5b --- /dev/null +++ b/handlers/hosts/lsp/text-document-did-change.go @@ -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 +} diff --git a/handlers/hosts/lsp/text-document-did-close.go b/handlers/hosts/lsp/text-document-did-close.go new file mode 100644 index 0000000..3961658 --- /dev/null +++ b/handlers/hosts/lsp/text-document-did-close.go @@ -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 +} diff --git a/handlers/hosts/lsp/text-document-did-open.go b/handlers/hosts/lsp/text-document-did-open.go new file mode 100644 index 0000000..e89876a --- /dev/null +++ b/handlers/hosts/lsp/text-document-did-open.go @@ -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 +} diff --git a/handlers/hosts/tree/handler.go b/handlers/hosts/tree/handler.go index c75b4e2..61c1113 100644 --- a/handlers/hosts/tree/handler.go +++ b/handlers/hosts/tree/handler.go @@ -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{}{} diff --git a/handlers/hosts/tree/handler_test.go b/handlers/hosts/tree/handler_test.go index ff6a015..1f82762 100644 --- a/handlers/hosts/tree/handler_test.go +++ b/handlers/hosts/tree/handler_test.go @@ -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) } } diff --git a/root-handler/lsp-utils.go b/root-handler/lsp-utils.go index 2074510..a4337f3 100644 --- a/root-handler/lsp-utils.go +++ b/root-handler/lsp-utils.go @@ -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 } diff --git a/root-handler/singleton.go b/root-handler/singleton.go index 2fe7073..f06aae0 100644 --- a/root-handler/singleton.go +++ b/root-handler/singleton.go @@ -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) { diff --git a/root-handler/text-document-code-action.go b/root-handler/text-document-code-action.go index 5504676..a569050 100644 --- a/root-handler/text-document-code-action.go +++ b/root-handler/text-document-code-action.go @@ -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) } diff --git a/root-handler/text-document-completion.go b/root-handler/text-document-completion.go index 0ee3292..63eb383 100644 --- a/root-handler/text-document-completion.go +++ b/root-handler/text-document-completion.go @@ -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) } diff --git a/root-handler/text-document-did-change.go b/root-handler/text-document-did-change.go index 5b4af9e..841dac3 100644 --- a/root-handler/text-document-did-change.go +++ b/root-handler/text-document-did-change.go @@ -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) } diff --git a/root-handler/text-document-did-close.go b/root-handler/text-document-did-close.go index fbf90e1..7185980 100644 --- a/root-handler/text-document-did-close.go +++ b/root-handler/text-document-did-close.go @@ -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: } diff --git a/root-handler/text-document-did-open.go b/root-handler/text-document-did-open.go index 5623fd8..caaddd7 100644 --- a/root-handler/text-document-did-open.go +++ b/root-handler/text-document-did-open.go @@ -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 +} diff --git a/root-handler/text-document-hover.go b/root-handler/text-document-hover.go index a973f46..fca62bb 100644 --- a/root-handler/text-document-hover.go +++ b/root-handler/text-document-hover.go @@ -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) } diff --git a/utils/strings.go b/utils/strings.go index 1832fbf..4e85934 100644 --- a/utils/strings.go +++ b/utils/strings.go @@ -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) +}