From 88ac504f50bf147eb92c4b4e89dde27d57f25dbd Mon Sep 17 00:00:00 2001 From: Shuu Date: Wed, 3 Jan 2024 08:29:58 +0900 Subject: [PATCH] lsp: Add support for Progress (#1987) --- ...0240101_222848_GitHub_Actions_progress.rst | 7 + documents/lsp.md | 5 +- src/moepkg/bufferstatus.nim | 1 - src/moepkg/lsp.nim | 93 ++++++ src/moepkg/lsp/client.nim | 78 ++++- src/moepkg/lsp/protocol/types.nim | 14 +- src/moepkg/lsp/utils.nim | 127 +++++++- src/moepkg/messages.nim | 3 + tests/tlsp.nim | 300 +++++++++++++++++- tests/tlsputils.nim | 120 +++++++ 10 files changed, 737 insertions(+), 11 deletions(-) create mode 100644 changelog.d/20240101_222848_GitHub_Actions_progress.rst diff --git a/changelog.d/20240101_222848_GitHub_Actions_progress.rst b/changelog.d/20240101_222848_GitHub_Actions_progress.rst new file mode 100644 index 000000000..d2d8a6a8c --- /dev/null +++ b/changelog.d/20240101_222848_GitHub_Actions_progress.rst @@ -0,0 +1,7 @@ +.. _#1987: https://github.com/fox0430/moe/pull/1987 + +Added +..... + +- `#1987`_ lsp: Add support for Progress + diff --git a/documents/lsp.md b/documents/lsp.md index 171a01983..67e65d123 100644 --- a/documents/lsp.md +++ b/documents/lsp.md @@ -12,13 +12,16 @@ Please feedback, bug reports and PRs. - `shutdown` - `window/showMessage` - `window/logMessage` -- `textDocument/publishDiagnostics` +- `window/workDoneProgress/create` +- `workspace/configuration` - `workspace/didChangeConfiguration` +- `textDocument/publishDiagnostics` - `textDocument/didOpen` - `textDocument/didChange` - `textDocument/didSave` - `textDocument/didClose` - `textDocument/hover` +- `$/progress` ## Configuration diff --git a/src/moepkg/bufferstatus.nim b/src/moepkg/bufferstatus.nim index 917833f86..4aa0a3bd2 100644 --- a/src/moepkg/bufferstatus.nim +++ b/src/moepkg/bufferstatus.nim @@ -20,7 +20,6 @@ import std/[tables, times, options, os, strformat] import pkg/results import syntax/highlite -import lsp/utils import gapbuffer, unicodeext, fileutils, highlight, independentutils, git, syntaxcheck diff --git a/src/moepkg/lsp.nim b/src/moepkg/lsp.nim index c5e1c5f48..cca775829 100644 --- a/src/moepkg/lsp.nim +++ b/src/moepkg/lsp.nim @@ -130,6 +130,7 @@ proc showLspServerLog( commandLine : var CommandLine, notify: JsonNode): Result[(), string] = ## Show the log to the command line. + ## ## window/showMessage let m = parseWindowShowMessageNotify(notify) @@ -162,6 +163,7 @@ proc lspDiagnostics( bufStatuses: var seq[BufferStatus], notify: JsonNode): Result[(), string] = ## Set BufferStatus.syntaxCheckResults to diagnostics results. + ## ## textDocument/publishDiagnostics let parseResult = parseTextDocumentPublishDiagnosticsNotify(notify) @@ -195,6 +197,90 @@ proc lspDiagnostics( return Result[(), string].ok () +proc lspProgressCreate( + c: var LspClient, + notify: JsonNode): Result[(), string] = + ## Init a LSP progress. + ## + ## window/workDoneProgress/create + + let token = parseWindowWorkDnoneProgressCreateNotify(notify) + if token.isErr: + return Result[(), string].err fmt"Invalid server notify: {token.error}" + + c.createProgress(token.get) + + return Result[(), string].ok () + +proc progressMessage(p: ProgressReport): string {.inline.} = + case p.state: + of begin: + if p.message.len > 0: + return fmt"{p.title}: {p.message}" + else: + return p.title + of report: + result = fmt"{p.title}: " + if p.percentage.isSome: result &= fmt"{$p.percentage.get}%: " + result &= p.message + of `end`: + return fmt"{p.title}: {p.message}" + else: + return "" + +proc lspProgress( + status: var EditorStatus, + notify: JsonNode): Result[(), string] = + ## Begin/Report/End the LSP progress. + ## + ## $/progress + + let token = workDoneProgressToken(notify) + + if isWorkDoneProgressBegin(notify): + let begin = parseWorkDoneProgressBegin(notify) + if begin.isErr: + return Result[(), string].err fmt"Invalid server notify: {begin.error}" + + if isCancellable(begin.get): + # Cancel + return lspClient.delProgress(token) + + let err = lspClient.beginProgress(token, begin.get) + if err.isErr: + return Result[(), string].err fmt"Invalid server notify: {err.error}" + elif isWorkDoneProgressReport(notify): + let report = parseWorkDoneProgressReport(notify) + if report.isErr: + return Result[(), string].err fmt"Invalid server notify: {report.error}" + + if isCancellable(report.get): + # Cancel + return lspClient.delProgress(token) + + let err = lspClient.reportProgress(token, report.get) + if err.isErr: + return Result[(), string].err fmt"Invalid server notify: {err.error}" + elif isWorkDoneProgressEnd(notify): + let `end` = parseWorkDoneProgressEnd(notify) + if `end`.isErr: + return Result[(), string].err fmt"Invalid server notify: {`end`.error}" + + let err = lspClient.endProgress(token, `end`.get) + if err.isErr: + return Result[(), string].err fmt"Invalid server notify: {err.error}" + else: + return Result[(), string].err fmt"Invalid server notify: {notify}" + + case lspClient.progress[token].state: + of begin, report, `end`: + status.commandLine.writeLspProgress( + progressMessage(lspClient.progress[token])) + else: + discard + + return Result[(), string].ok () + proc handleLspServerNotify( status: var EditorStatus, notify: JsonNode): Result[(), string] = @@ -212,6 +298,13 @@ proc handleLspServerNotify( of windowLogMessage: # Already logged to LspClint.log. return Result[(), string].ok () + of workspaceConfiguration: + # TODO: Configure settings based on notifications if necessary. + return Result[(), string].ok () + of windowWorkDnoneProgressCreate: + return lspClient.lspProgressCreate(notify) + of progress: + return status.lspProgress(notify) of textDocumentPublishDiagnostics: return status.bufStatus.lspDiagnostics(notify) else: diff --git a/src/moepkg/lsp/client.nim b/src/moepkg/lsp/client.nim index c28158678..580986a6f 100644 --- a/src/moepkg/lsp/client.nim +++ b/src/moepkg/lsp/client.nim @@ -20,7 +20,7 @@ # NOTE: Language Server Protocol Specification - 3.17 # https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/ -import std/[strformat, strutils, json, options, os, osproc, posix] +import std/[strformat, strutils, json, options, os, osproc, posix, tables] import pkg/results import ../appinfo @@ -53,6 +53,8 @@ type LspCapabilities* = object hover*: bool + LspProgressTable* = Table[ProgressToken, ProgressReport] + LspClient* = ref object serverProcess: Process # LSP server process. @@ -60,6 +62,8 @@ type # Input/Output streams for the LSP server process. capabilities*: Option[LspCapabilities] # LSP server capabilities + progress*: LspProgressTable + # Use in window/workDoneProgress waitingResponse*: Option[LspMethod] # The waiting response from the LSP server. log*: LspLog @@ -111,6 +115,75 @@ proc addNotifyFromServerLog*(c: var LspClient, m: JsonNode) {.inline.} = proc clearWaitingResponse*(c: var LspClient) {.inline.} = c.waitingResponse = none(LspMethod) +proc createProgress*(c: var LspClient, token: ProgressToken) = + ## Add a new progress to the `LspClint.progress`. + + if not c.progress.contains(token): + c.progress[token] = ProgressReport(state: ProgressState.create) + +proc beginProgress*( + c: var LspClient, + token: ProgressToken, + p: WorkDoneProgressBegin): Result[(), string] = + ## Begin the progress in the `LspClint.progress`. + + if not c.progress.contains(token): + return Result[(), string].err "token not found" + + c.progress[token].state = ProgressState.begin + c.progress[token].title = p.title + if p.message.isSome: + c.progress[token].message = p.message.get + if p.percentage.isSome: + c.progress[token].percentage = some(p.percentage.get.Natural) + + return Result[(), string].ok () + +proc reportProgress*( + c: var LspClient, + token: ProgressToken, + report: WorkDoneProgressReport): Result[(), string] = + ## Update the progress in the `LspClint.progress`. + + if not c.progress.contains(token): + return Result[(), string].err "token not found" + + if ProgressState.report != c.progress[token].state: + c.progress[token].state = ProgressState.report + + if report.message.isSome: + c.progress[token].message = report.message.get + if report.percentage.isSome: + c.progress[token].percentage = some(report.percentage.get.Natural) + + return Result[(), string].ok () + +proc endProgress*( + c: var LspClient, + token: ProgressToken, + p: WorkDoneProgressEnd): Result[(), string] = + ## End the progress in the `LspClint.progress`. + + if not c.progress.contains(token): + return Result[(), string].err "token not found" + + c.progress[token].state = ProgressState.end + + if p.message.isSome: + c.progress[token].message = p.message.get + + return Result[(), string].ok () + +proc delProgress*(c: var LspClient, token: ProgressToken): Result[(), string] = + ## Delete the progress from the `LspClint.progress`. + + if not c.progress.contains(token): + return Result[(), string].err "token not found" + + c.progress.del(token) + + return Result[(), string].ok () + proc readable*(c: LspClient, timeout: int = 1): LspClientReadableResult = ## Return when output is written from the LSP server or timesout. ## Wait for the output from process to be written using poll(2). @@ -253,6 +326,9 @@ proc initInitializeParams*( publishDiagnostics: some(PublishDiagnosticsCapability( dynamicRegistration: some(true) )) + )), + window: some(WindowCapabilities( + workDoneProgress: some(true) )) ), workspaceFolders: some( diff --git a/src/moepkg/lsp/protocol/types.nim b/src/moepkg/lsp/protocol/types.nim index 0e45009b1..961570e33 100644 --- a/src/moepkg/lsp/protocol/types.nim +++ b/src/moepkg/lsp/protocol/types.nim @@ -120,9 +120,12 @@ type kind*: string message*: Option[string] - ProgressParams* = ref object of RootObj - token*: string # can be also int but the server will send strings - value*: OptionalNode + WorkDoneProgressCreateParams* = ref object of RootObj + token*: OptionalNode # int or string (ProgressToken) + + ProgressTokenParams* = ref object of RootObj + token*: OptionalNode # int or string (ProgressToken) + value*: OptionalNode # T ConfigurationItem* = ref object of RootObj scopeUri*: Option[string] @@ -262,6 +265,8 @@ type WindowCapabilities* = ref object of RootObj workDoneProgress*: Option[bool] + showMessage*: Option[ShowMessageRequestParams] + showDocument*: Option[ShowDocumentClientCapabilities] ClientCapabilities* = ref object of RootObj workspace*: Option[WorkspaceClientCapabilities] @@ -369,6 +374,9 @@ type message*: string actions*: OptionalSeq[MessageActionItem] + ShowDocumentClientCapabilities* = ref object of RootObj + support*: bool + LogMessageParams* = ref object of RootObj `type`*: int message*: string diff --git a/src/moepkg/lsp/utils.nim b/src/moepkg/lsp/utils.nim index 6f8383533..3c21fac50 100644 --- a/src/moepkg/lsp/utils.nim +++ b/src/moepkg/lsp/utils.nim @@ -36,6 +36,9 @@ type shutdown windowShowMessage windowLogMessage + windowWorkDnoneProgressCreate + progress + workspaceConfiguration workspaceDidChangeConfiguration textDocumentDidOpen textDocumentDidChange @@ -44,6 +47,22 @@ type textDocumentPublishDiagnostics textDocumentHover + ProgressToken* = string + # ProgressParams.token + # Can be also int but the editor only use string. + + ProgressState* = enum + create + begin + report + `end` + + ProgressReport* = ref object + state*: ProgressState + title*: string + message*: string + percentage*: Option[Natural] + LspMessageType* = enum error warn @@ -69,9 +88,13 @@ type R = Result parseLspMessageTypeResult* = R[LspMessageType, string] LspMethodResult* = R[LspMethod, string] - LspShutdownResult = R[(), string] - LspWindowShowMessageResult = R[ServerMessage, string] - LspWindowLogMessageResult = R[ServerMessage, string] + LspShutdownResult* = R[(), string] + LspWindowShowMessageResult* = R[ServerMessage, string] + LspWindowLogMessageResult* = R[ServerMessage, string] + LspWindowWorkDnoneProgressCreateResult* = R[ProgressToken, string] + LspWorkDoneProgressBeginResult* = R[WorkDoneProgressBegin, string] + LspWorkDoneProgressReportResult* = R[WorkDoneProgressReport, string] + LspWorkDoneProgressEndResult* = R[WorkDoneProgressEnd, string] LspDiagnosticsResult* = R[Option[Diagnostics], string] LspHoverResult* = R[Option[Hover], string] @@ -119,6 +142,9 @@ proc toLspMethodStr*(m: LspMethod): string = of shutdown: "shutdown" of windowShowMessage: "window/showMessage" of windowLogMessage: "window/logMessage" + of windowWorkDnoneProgressCreate: "window/workDoneProgress/create" + of progress: "$/progress" + of workspaceConfiguration: "workspace/configuration" of workspaceDidChangeConfiguration: "workspace/didChangeConfiguration" of textDocumentDidOpen: "textDocument/didOpen" of textDocumentDidChange: "textDocument/didChange" @@ -153,6 +179,12 @@ proc lspMethod*(j: JsonNode): LspMethodResult = LspMethodResult.ok windowShowMessage of "window/logMessage": LspMethodResult.ok windowLogMessage + of "window/workDoneProgress/create": + LspMethodResult.ok windowWorkDnoneProgressCreate + of "$/progress": + LspMethodResult.ok progress + of "workspace/configuration": + LspMethodResult.ok workspaceConfiguration of "workspace/didChangeConfiguration": LspMethodResult.ok workspaceDidChangeConfiguration of "textDocument/didOpen": @@ -214,12 +246,99 @@ proc parseWindowLogMessageNotify*(n: JsonNode): LspWindowLogMessageResult = return LspWindowLogMessageResult.err "Invalid notify" +proc parseWindowWorkDnoneProgressCreateNotify*( + n: JsonNode): LspWindowWorkDnoneProgressCreateResult = + ## https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_workDoneProgress_create + + if not n.contains("params") or n["params"].kind != JObject: + return LspWindowWorkDnoneProgressCreateResult.err "Invalid notify" + + var params: WorkDoneProgressCreateParams + try: + params = n["params"].to(WorkDoneProgressCreateParams) + except CatchableError as e: + return LspWindowWorkDnoneProgressCreateResult.err fmt"Invalid notify: {e.msg}" + + if params.token.isNone: + return LspWindowWorkDnoneProgressCreateResult.err fmt"Invalid notify: token is empty" + + return LspWindowWorkDnoneProgressCreateResult.ok params.token.get.getStr + +template isProgressNotify(j: JsonNode): bool = + ## `$/ptgoress` notification + + j.contains("params") and + j["params"].kind == JObject and + j["params"].contains("token") and + j["params"].contains("value") and + j["params"]["value"].contains("kind") and + j["params"]["value"]["kind"].kind == JString + +template isWorkDoneProgressBegin*(j: JsonNode): bool = + isProgressNotify(j) and + "begin" == j["params"]["value"]["kind"].getStr + +template isWorkDoneProgressReport*(j: JsonNode): bool = + isProgressNotify(j) and + "report" == j["params"]["value"]["kind"].getStr + +template isWorkDoneProgressEnd*(j: JsonNode): bool = + isProgressNotify(j) and + "end" == j["params"]["value"]["kind"].getStr + +template workDoneProgressToken*(j: JsonNode): string = + j["params"]["token"].getStr + +proc parseWorkDoneProgressBegin*( + n: JsonNode): LspWorkDoneProgressBeginResult = + ## https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#progress + + if not isWorkDoneProgressBegin(n): + return LspWorkDoneProgressBeginResult.err "Invalid notify" + + try: + return LspWorkDoneProgressBeginResult.ok n["params"]["value"].to( + WorkDoneProgressBegin) + except CatchableError as e: + return LspWorkDoneProgressBeginResult.err fmt"Invalid notify: {e.msg}" + +proc parseWorkDoneProgressReport*( + n: JsonNode): LspWorkDoneProgressReportResult = + ## https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#progress + + if not isWorkDoneProgressReport(n): + return LspWorkDoneProgressReportResult.err "Invalid notify" + + try: + return LspWorkDoneProgressReportResult.ok n["params"]["value"].to( + WorkDoneProgressReport) + except CatchableError as e: + return LspWorkDoneProgressReportResult.err fmt"Invalid notify: {e.msg}" + +proc parseWorkDoneProgressEnd*( + n: JsonNode): LspWorkDoneProgressEndResult = + ## https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#progress + + if not isWorkDoneProgressEnd(n): + return LspWorkDoneProgressEndResult.err "Invalid notify" + + try: + return LspWorkDoneProgressEndResult.ok n["params"]["value"].to( + WorkDoneProgressEnd) + except CatchableError as e: + return LspWorkDoneProgressEndResult.err fmt"Invalid notify: {e.msg}" + +template isCancellable*( + p: WorkDoneProgressBegin | WorkDoneProgressReport): bool = + + some(true) == p.cancellable + proc parseTextDocumentPublishDiagnosticsNotify*( n: JsonNode): LspDiagnosticsResult = ## https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#publishDiagnosticsParams if not n.contains("params") or n["params"].kind != JObject: - return LspDiagnosticsResult.err fmt"Invalid notify" + return LspDiagnosticsResult.err "Invalid notify" var params: PublishDiagnosticsParams try: diff --git a/src/moepkg/messages.nim b/src/moepkg/messages.nim index 1bbd5910f..90881be9a 100644 --- a/src/moepkg/messages.nim +++ b/src/moepkg/messages.nim @@ -402,3 +402,6 @@ proc writeLspServerLog*(commandLine: var CommandLine, message: string) = proc writeLspServerDebug*(commandLine: var CommandLine, message: string) = commandLine.writeDebug(fmt"lsp: {message}") + +proc writeLspProgress*(commandLine: var CommandLine, message: string) = + commandLine.writeStandard(fmt"lsp: progress: {message}") diff --git a/tests/tlsp.nim b/tests/tlsp.nim index c674ba109..0c7192dbd 100644 --- a/tests/tlsp.nim +++ b/tests/tlsp.nim @@ -17,7 +17,7 @@ # # #[############################################################################]# -import std/[unittest, oids, options, os, osproc, json] +import std/[unittest, oids, options, os, osproc, json, tables] import pkg/results @@ -284,6 +284,178 @@ suite "lsp: lspDiagnostics": check currentBufStatus.syntaxCheckResults.len == 0 +suite "lsp: lspWorkDoneProgressCreate": + var status: EditorStatus + + setup: + status = initEditorStatus() + status.settings.lsp.enable = true + + let filename = $genOid() & ".nim" + assert status.addNewBufferInCurrentWin(filename).isOk + + status.lspClients["nim"] = LspClient() + status.lspClients["nim"].progress["token"] = ProgressReport( + title: "", + state: ProgressState.create) + + test "Invalid": + check lspClient.lspProgressCreate(%*{ + "jsonrpc": "2.0", + "id": 0, + "result": nil + }).isErr + + test "Basic": + check lspClient.lspProgressCreate(%*{ + "jsonrpc": "2.0", + "id": 1, + "method": "window/workDoneProgress/create", + "params": { + "token": "token" + } + }).isOk + + check lspClient.progress["token"].state == ProgressState.create + +suite "lsp: lspProgress": + var status: EditorStatus + + setup: + status = initEditorStatus() + status.settings.lsp.enable = true + + let filename = $genOid() & ".nim" + assert status.addNewBufferInCurrentWin(filename).isOk + + status.lspClients["nim"] = LspClient() + + test "begin": + status.lspClients["nim"].progress["token"] = ProgressReport( + title: "", + state: ProgressState.create) + + check status.lspProgress(%*{ + "jsonrpc": "2.0", + "method": "$/progress", + "params": { + "token": "token", + "value": { + "kind": "begin", + "title": "begin", + "cancellable": false + } + } + }).isOk + + check ProgressState.begin == status.lspClients["nim"].progress["token"].state + check "begin" == status.lspClients["nim"].progress["token"].title + check "" == status.lspClients["nim"].progress["token"].message + check none(Natural) == status.lspClients["nim"].progress["token"].percentage + + check ru"lsp: progress: begin" == status.commandLine.buffer + + test "begin with message": + status.lspClients["nim"].progress["token"] = ProgressReport( + title: "", + state: ProgressState.create) + + check status.lspProgress(%*{ + "jsonrpc": "2.0", + "method": "$/progress", + "params": { + "token": "token", + "value": { + "kind": "begin", + "title": "begin", + "message": "message", + "cancellable": false + } + } + }).isOk + + check ProgressState.begin == status.lspClients["nim"].progress["token"].state + check "begin" == status.lspClients["nim"].progress["token"].title + check "message" == status.lspClients["nim"].progress["token"].message + check none(Natural) == status.lspClients["nim"].progress["token"].percentage + + check ru"lsp: progress: begin: message" == status.commandLine.buffer + + test "report": + status.lspClients["nim"].progress["token"] = ProgressReport( + title: "report", + state: ProgressState.begin) + + check status.handleLspServerNotify(%*{ + "jsonrpc": "2.0", + "method": "$/progress", + "params": { + "token": "token", + "value": { + "kind":"report", + "cancellable": false, + "message": "report" + } + } + }).isOk + + check ProgressState.report == status.lspClients["nim"].progress["token"].state + check "report" == status.lspClients["nim"].progress["token"].title + check "report" == status.lspClients["nim"].progress["token"].message + check none(Natural) == status.lspClients["nim"].progress["token"].percentage + + check ru"lsp: progress: report: report" == status.commandLine.buffer + + test "report with percentage": + status.lspClients["nim"].progress["token"] = ProgressReport( + title: "report", + state: ProgressState.begin) + + check status.handleLspServerNotify(%*{ + "jsonrpc": "2.0", + "method": "$/progress", + "params": { + "token": "token", + "value": { + "kind":"report", + "cancellable": false, + "message": "report", + "percentage": 50 + } + } + }).isOk + + check ProgressState.report == status.lspClients["nim"].progress["token"].state + check "report" == status.lspClients["nim"].progress["token"].title + check "report" == status.lspClients["nim"].progress["token"].message + check some(Natural(50)) == status.lspClients["nim"].progress["token"].percentage + + check ru"lsp: progress: report: 50%: report" == status.commandLine.buffer + + test "end": + status.lspClients["nim"].progress["token"] = ProgressReport( + title: "end", + state: ProgressState.report) + + check status.handleLspServerNotify(%*{ + "jsonrpc": "2.0", + "method": "$/progress", + "params": { + "token": "token", + "value": { + "kind":"end", + "message": "end" + } + } + }).isOk + + check ProgressState.`end` == status.lspClients["nim"].progress["token"].state + check "end" == status.lspClients["nim"].progress["token"].title + check "end" == status.lspClients["nim"].progress["token"].message + check none(Natural) == status.lspClients["nim"].progress["token"].percentage + + check ru"lsp: progress: end: end" == status.commandLine.buffer + suite "lsp: handleLspServerNotify": setup: var status = initEditorStatus() @@ -315,6 +487,132 @@ suite "lsp: handleLspServerNotify": } }).isOk + test "window/logMessage": + check status.handleLspServerNotify(%*{ + "jsonrpc": "2.0", + "method": "window/logMessage", + "params": { + "type": 3, + "message": "Log message" + } + }).isOk + + test "workspace/configuration": + check status.handleLspServerNotify(%*{ + "jsonrpc": "2.0", + "id": 0, + "method": "workspace/configuration", + "params": { + "items": [ + {"section": "test"} + ] + } + }).isOk + + test "window/workDoneProgress/create": + let filename = $genOid() & ".nim" + assert status.addNewBufferInCurrentWin(filename).isOk + status.lspClients["nim"] = LspClient() + + check status.handleLspServerNotify(%*{ + "jsonrpc": "2.0", + "id": 1, + "method": "window/workDoneProgress/create", + "params": { + "token":"token" + } + }).isOk + + test "$/progress (begin)": + let filename = $genOid() & ".nim" + assert status.addNewBufferInCurrentWin(filename).isOk + + status.lspClients["nim"] = LspClient() + status.lspClients["nim"].progress["token"] = ProgressReport( + title: "", + state: ProgressState.create) + + check status.handleLspServerNotify(%*{ + "jsonrpc": "2.0", + "method": "$/progress", + "params": { + "token": "token", + "value": { + "kind": "begin", + "title": "begin", + "cancellable": false + } + } + }).isOk + + test "$/progress (report)": + let filename = $genOid() & ".nim" + assert status.addNewBufferInCurrentWin(filename).isOk + + status.lspClients["nim"] = LspClient() + status.lspClients["nim"].progress["token"] = ProgressReport( + title: "report", + state: ProgressState.create) + + check status.handleLspServerNotify(%*{ + "jsonrpc": "2.0", + "method": "$/progress", + "params": { + "token": "token", + "value": { + "kind":"report", + "cancellable": false, + "message": "report" + } + } + }).isOk + + check ru"lsp: progress: report: report" == status.commandLine.buffer + + test "$/progress (report with percentage)": + let filename = $genOid() & ".nim" + assert status.addNewBufferInCurrentWin(filename).isOk + + status.lspClients["nim"] = LspClient() + status.lspClients["nim"].progress["token"] = ProgressReport( + title: "report", + state: ProgressState.create) + + check status.handleLspServerNotify(%*{ + "jsonrpc": "2.0", + "method": "$/progress", + "params": { + "token": "token", + "value": { + "kind":"report", + "cancellable": false, + "message": "report", + "percentage": 50 + } + } + }).isOk + + test "$/progress (end)": + let filename = $genOid() & ".nim" + assert status.addNewBufferInCurrentWin(filename).isOk + + status.lspClients["nim"] = LspClient() + status.lspClients["nim"].progress["token"] = ProgressReport( + title: "end", + state: ProgressState.create) + + check status.handleLspServerNotify(%*{ + "jsonrpc": "2.0", + "method": "$/progress", + "params": { + "token": "token", + "value": { + "kind":"end", + "message": "end" + } + } + }).isOk + test "textDocument/publishDiagnostics": check status.handleLspServerNotify(%*{ "jsonrpc": "2.0", diff --git a/tests/tlsputils.nim b/tests/tlsputils.nim index 94cc4ff3a..09d9cf4f3 100644 --- a/tests/tlsputils.nim +++ b/tests/tlsputils.nim @@ -125,6 +125,30 @@ suite "lsp: lspMetod": "params": nil }).get + test "workspace/configuration": + check LspMethod.workspaceConfiguration == lspMethod(%*{ + "jsonrpc": "2.0", + "id": 0, + "method": "workspace/configuration", + "params": nil + }).get + + test "window/workDoneProgress/create": + check LspMethod.windowWorkDnoneProgressCreate == lspMethod(%*{ + "jsonrpc": "2.0", + "id": 0, + "method": "window/workDoneProgress/create", + "params": nil + }).get + + test "$/progress": + check LspMethod.progress == lspMethod(%*{ + "jsonrpc": "2.0", + "id": 0, + "method": "$/progress", + "params": nil + }).get + test "window/publishDiagnostics": check LspMethod.textDocumentPublishDiagnostics == lspMethod(%*{ "jsonrpc": "2.0", @@ -194,6 +218,102 @@ suite "lsp: parseWindowLogMessageNotify": } }).get +suite "lsp: parseWindowWorkDnoneProgressCreateNotify": + test "Invalid": + check parseWindowWorkDnoneProgressCreateNotify(%*{ + "jsonrpc": "2.0", + "id": 0, + "result": nil + }).isErr + + test "Basic": + check "token" == parseWindowWorkDnoneProgressCreateNotify(%*{ + "jsonrpc": "2.0", + "id": 0, + "method": "window/workDoneProgress/create", + "params": { + "token":"token" + } + }).get + +suite "lsp: parseWorkDoneProgressBegin": + test "Invalid": + check parseWorkDoneProgressBegin(%*{ + "jsonrpc": "2.0", + "id": 0, + "result": nil + }).isErr + + test "Basic": + check WorkDoneProgressBegin( + kind: "begin", + title: "title", + message: some("message"), + cancellable: some(false), + percentage: none(int))[] == parseWorkDoneProgressBegin(%*{ + "jsonrpc": "2.0", + "method": "$/progress", + "params": { + "token": "token", + "value": { + "kind":"begin", + "title":"title", + "message": "message", + "cancellable":false + } + } + }).get[] + +suite "lsp: parseWorkDoneProgressReport": + test "Invalid": + check parseWorkDoneProgressReport(%*{ + "jsonrpc": "2.0", + "id": 0, + "result": nil + }).isErr + + test "Basic": + check WorkDoneProgressReport( + kind: "report", + message: some("message"), + cancellable: some(false), + percentage: some(50))[] == parseWorkDoneProgressReport(%*{ + "jsonrpc": "2.0", + "method": "$/progress", + "params": { + "token": "token", + "value": { + "kind":"report", + "message": "message", + "cancellable":false, + "percentage": 50 + } + } + }).get[] + +suite "lsp: parseWorkDoneProgressEnd": + test "Invalid": + check parseWorkDoneProgressEnd(%*{ + "jsonrpc": "2.0", + "id": 0, + "result": nil + }).isErr + + test "Basic": + check WorkDoneProgressEnd( + kind: "end", + message: some("message"))[] == parseWorkDoneProgressEnd(%*{ + "jsonrpc": "2.0", + "method": "$/progress", + "params": { + "token": "token", + "value": { + "kind":"end", + "message": "message", + } + } + }).get[] + suite "lsp: parseTextDocumentHoverResponse": test "Invalid": let res = %*{"jsonrpc": "2.0", "params": nil}