From 184cf63411d2dc61bf56a69b5d6b4f03a844eed1 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 28 Jul 2024 13:36:29 +0200 Subject: [PATCH] feat: Add simple diagnostics; Improvements --- common/errors.go | 35 ++++++++++++ common/parser.go | 43 +++++++-------- handlers/openssh/diagnostics.go | 56 ++++++++++++++++++++ handlers/openssh/text-document-did-change.go | 15 ++++-- handlers/openssh/text-document-did-open.go | 2 +- 5 files changed, 121 insertions(+), 30 deletions(-) create mode 100644 common/errors.go create mode 100644 handlers/openssh/diagnostics.go diff --git a/common/errors.go b/common/errors.go new file mode 100644 index 0000000..204560e --- /dev/null +++ b/common/errors.go @@ -0,0 +1,35 @@ +package common + +import ( + "fmt" +) + +type ParserError interface {} + +type OptionAlreadyExistsError struct { + Option string + FoundOnLine uint32 +} +func (e OptionAlreadyExistsError) Error() string { + return fmt.Sprintf("Option %s already exists", e.Option) +} + +type OptionUnknownError struct { + Option string +} +func (e OptionUnknownError) Error() string { + return fmt.Sprintf("Option '%s' does not exist", e.Option) +} + +type MalformedLineError struct { + Line string +} +func (e MalformedLineError) Error() string { + return fmt.Sprintf("Malformed line: %s", e.Line) +} + +type LineNotFoundError struct {} +func (e LineNotFoundError) Error() string { + return "Line not found" +} + diff --git a/common/parser.go b/common/parser.go index 8336d17..c26d239 100644 --- a/common/parser.go +++ b/common/parser.go @@ -1,7 +1,6 @@ package common import ( - "fmt" "regexp" "strings" ) @@ -34,29 +33,6 @@ type SimpleConfigParser struct { Options SimpleConfigOptions } -type OptionAlreadyExistsError struct { - Option string -} -func (e OptionAlreadyExistsError) Error() string { - return fmt.Sprintf("Option %s already exists", e.Option) -} -type OptionUnknownError struct { - Option string -} -func (e OptionUnknownError) Error() string { - return fmt.Sprintf("Option '%s' does not exist", e.Option) -} -type MalformedLineError struct { - Line string -} -func (e MalformedLineError) Error() string { - return fmt.Sprintf("Malformed line: %s", e.Line) -} -type LineNotFoundError struct {} -func (e LineNotFoundError) Error() string { - return "Line not found" -} - func (p *SimpleConfigParser) AddLine(line string, lineNumber int) error { parts := strings.SplitN(line, p.Options.Separator, 2) @@ -83,6 +59,7 @@ func (p *SimpleConfigParser) AddLine(line string, lineNumber int) error { if _, exists := p.Lines[option]; exists { return OptionAlreadyExistsError{ Option: option, + FoundOnLine: uint32(lineNumber), } } @@ -118,6 +95,22 @@ func (p *SimpleConfigParser) UpsertOption(option string, value string) { } } +func (p *SimpleConfigParser) GetOption(option string) (SimpleConfigLine, error) { + if _, exists := p.Lines[option]; exists { + return p.Lines[option], nil + } + + return SimpleConfigLine{ + Value: "", + Position: SimpleConfigPosition{ + Line: 0, + }, + }, + OptionUnknownError{ + Option: option, + } +} + func (p *SimpleConfigParser) ParseFromFile(content string) []error { lines := strings.Split(content, "\n") errors := make([]error, 0) @@ -142,7 +135,7 @@ func (p *SimpleConfigParser) Clear() { } // TODO: Use better approach: Store an extra array of lines in order; with references to the SimpleConfigLine -func (p SimpleConfigParser) FindByLineNumber(lineNumber int) (string, SimpleConfigLine, error) { +func (p *SimpleConfigParser) FindByLineNumber(lineNumber int) (string, SimpleConfigLine, error) { for option, line := range p.Lines { if line.Position.Line == lineNumber { return option, line, nil diff --git a/handlers/openssh/diagnostics.go b/handlers/openssh/diagnostics.go new file mode 100644 index 0000000..d483f97 --- /dev/null +++ b/handlers/openssh/diagnostics.go @@ -0,0 +1,56 @@ +package handlers + +import ( + "config-lsp/common" + "fmt" + "unicode/utf8" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func ClearDiagnostics(context *glsp.Context, uri protocol.DocumentUri) { + context.Notify( + "textDocument/publishDiagnostics", + protocol.PublishDiagnosticsParams{ + URI: uri, + Diagnostics: []protocol.Diagnostic{}, + }, + ) +} + +func SendDiagnosticsFromParserErrors(context *glsp.Context, uri protocol.DocumentUri, parserErrors []error) { + diagnosticErrors := make([]protocol.Diagnostic, 0) + + for _, parserError := range parserErrors { + switch parserError.(type) { + case common.OptionAlreadyExistsError: { + err := parserError.(common.OptionAlreadyExistsError) + existingOption, _ := Parser.GetOption(err.Option) + + diagnosticErrors = append(diagnosticErrors, protocol.Diagnostic{ + Range: protocol.Range{ + Start: protocol.Position{ + Line: err.FoundOnLine, + Character: 0, + }, + End: protocol.Position{ + Line: err.FoundOnLine, + Character: uint32(utf8.RuneCountInString(err.Option)), + }, + }, + Message: fmt.Sprintf("Option '%s' has already been declared on line %v", err.Option, existingOption.Position.Line + 1), + }) + } + } + } + + context.Notify( + "textDocument/publishDiagnostics", + protocol.PublishDiagnosticsParams{ + URI: uri, + Diagnostics: diagnosticErrors, + }, + ) +} + diff --git a/handlers/openssh/text-document-did-change.go b/handlers/openssh/text-document-did-change.go index f3a84c8..1ef611f 100644 --- a/handlers/openssh/text-document-did-change.go +++ b/handlers/openssh/text-document-did-change.go @@ -7,10 +7,17 @@ import ( // Todo: Implement incremental parsing func TextDocumentDidChange(context *glsp.Context, params *protocol.DidChangeTextDocumentParams) error { - content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text + content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text - Parser.Clear() - Parser.ParseFromFile(content) + Parser.Clear() + errors := Parser.ParseFromFile(content) - return nil + if len(errors) > 0 { + SendDiagnosticsFromParserErrors(context, params.TextDocument.URI, errors) + } else { + ClearDiagnostics(context, params.TextDocument.URI) + } + + + return nil } diff --git a/handlers/openssh/text-document-did-open.go b/handlers/openssh/text-document-did-open.go index 15ccdd9..700c07c 100644 --- a/handlers/openssh/text-document-did-open.go +++ b/handlers/openssh/text-document-did-open.go @@ -17,7 +17,7 @@ func TextDocumentDidOpen(context *glsp.Context, params *protocol.DidOpenTextDocu errors := Parser.ParseFromFile(string(readBytes)) if len(errors) > 0 { - return errors[0] + SendDiagnosticsFromParserErrors(context, params.TextDocument.URI, errors) } return nil