Skip to content

Commit f526163

Browse files
authored
Implements restart nimsuggest extension (#231)
1 parent 8839357 commit f526163

File tree

9 files changed

+118
-11
lines changed

9 files changed

+118
-11
lines changed

ls.nim

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import macros, strformat,
22
chronos, chronos/threadsync,
33
os, sugar, sequtils, hashes, osproc,
44
suggestapi, protocol/enums, protocol/types, with, tables, strutils, sets,
5-
./utils, chronicles, std/re, uri,
5+
./utils, chronicles, std/re, uri, std/setutils,
66
json_serialization, std/json, streams, json_rpc/[servers/socketserver]
77

88
proc getVersionFromNimble(): string =
@@ -87,9 +87,13 @@ type
8787
onStdReadSignal*: ThreadSignalPtr #used by the thread to notify it read from the std
8888
onMainReadSignal*: ThreadSignalPtr #used by the main thread to notify it read the value from the signal
8989
value*: cstring
90-
90+
91+
LspExtensionCapability* = enum #List of extensions this server support. Useful for clients
92+
excRestartSuggest = "RestartSuggest"
93+
9194
LanguageServer* = ref object
9295
clientCapabilities*: ClientCapabilities
96+
extensionCapabilities*: set[LspExtensionCapability]
9397
initializeParams*: InitializeParams
9498
notify*: NotifyAction
9599
call*: CallAction
@@ -148,6 +152,7 @@ proc initLs*(params: CommandLineParams, storageDir: string): LanguageServer =
148152
responseMap: newTable[string, Future[JsonNode]](),
149153
storageDir: storageDir,
150154
cmdLineClientProcessId: params.clientProcessId,
155+
extensionCapabilities: LspExtensionCapability.items.toSet
151156
)
152157
153158
proc getNimbleEntryPoints*(dumpInfo: NimbleDumpInfo, nimbleProjectPath: string): seq[string] =

nimlangserver.nim

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,13 @@ proc registerRoutes(srv: RpcSocketServer, ls: LanguageServer) =
2525
srv.register(
2626
"textDocument/documentHighlight", ls.addRpcToCancellable(wrapRpc(partial(documentHighlight, ls)))
2727
)
28-
srv.register("extension/macroExpand", wrapRpc(partial(expand, ls)))
29-
srv.register("extension/status", wrapRpc(partial(status, ls)))
3028
srv.register("shutdown", wrapRpc(partial(shutdown, ls)))
3129
srv.register("exit", wrapRpc(partial(exit, (ls: ls, onExit: ls.onExit))))
30+
#Extension
31+
srv.register("extension/macroExpand", wrapRpc(partial(expand, ls)))
32+
srv.register("extension/status", wrapRpc(partial(status, ls)))
33+
srv.register("extension/capabilities", wrapRpc(partial(extensionCapabilities, ls)))
34+
srv.register("extension/suggest", wrapRpc(partial(extensionSuggest, ls)))
3235

3336
#Notifications
3437
srv.register("$/cancelRequest", wrapRpc(partial(cancelRequest, ls)))
@@ -65,9 +68,9 @@ proc handleParams(): CommandLineParams =
6568
stderr.writeLine("Invalid client process ID: ", pidStr)
6669
quit 1
6770
if param == "--stdio":
68-
result.transport = some stdio
71+
result.transport = some TransportMode.stdio
6972
if param == "--socket":
70-
result.transport = some socket
73+
result.transport = some TransportMode.socket
7174
if param.startsWith "--port":
7275
let port = param.substr(7)
7376
try:
@@ -105,7 +108,7 @@ proc registerProcMonitor(ls: LanguageServer) =
105108
hookAsyncProcMonitor(ls.cmdLineClientProcessId.get, onCmdLineClientProcessExit)
106109

107110
proc main*(cmdLineParams: CommandLineParams): LanguageServer =
108-
debug "Starting nimlangserver", params = cmdLineParams
111+
debug "Starting nimlangserver", version = LSPVersion, params = cmdLineParams
109112
#[
110113
`nimlangserver` supports both transports: stdio and socket. By default it uses stdio transport.
111114
But we do construct a RPC socket server even in stdio mode, so that we can reuse the same code for both transports.

nimlangserver.nimble

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
mode = ScriptMode.Verbose
22

33
packageName = "nimlangserver"
4-
version = "1.5.1"
4+
version = "1.5.2"
55
author = "The core Nim team"
66
description = "Nim language server for IDEs"
77
license = "MIT"

nimlangserverold

-2.04 MB
Binary file not shown.

protocol/types.nim

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -992,4 +992,13 @@ type
992992
openFiles*: seq[string]
993993

994994
NimLangServerStatusParams* = object
995-
995+
996+
SuggestAction* = enum
997+
saNone = "none", saRestart = "restart"
998+
999+
SuggestParams* = object
1000+
action*: SuggestAction
1001+
projectFile*: string #Absolute path to file
1002+
1003+
SuggestResult* = object
1004+
actionPerformed*: SuggestAction

routes.nim

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,36 @@ proc status*(ls: LanguageServer, params: NimLangServerStatusParams): Future[NimL
218218
debug "Received status request"
219219
ls.getLspStatus()
220220

221+
proc extensionCapabilities*(ls: LanguageServer, _: JsonNode): Future[seq[string]] {.async.} =
222+
ls.extensionCapabilities.toSeq.mapIt($it)
223+
224+
proc extensionSuggest*(ls: LanguageServer, params: SuggestParams): Future[SuggestResult] {.async.} =
225+
debug "Extension Suggest ", params = params
226+
let projectFile = params.projectFile
227+
if projectFile != "*" and projectFile notin ls.projectFiles:
228+
error "Project file must exists ", params = params
229+
return SuggestResult()
230+
template restart() =
231+
ls.showMessage(fmt "Restarting nimsuggest {projectFile}", MessageType.Info)
232+
ns.stop()
233+
ls.createOrRestartNimsuggest(projectFile, projectFile.pathToUri)
234+
ls.sendStatusChanged()
235+
236+
case params.action:
237+
of saRestart:
238+
if projectFile == "*":
239+
for projectFile, nsFut in ls.projectFiles:
240+
let ns = await nsFut
241+
restart
242+
else:
243+
let ns = await ls.projectFiles[projectFile]
244+
restart
245+
246+
SuggestResult(actionPerformed: saRestart)
247+
of saNone:
248+
error "An action must be specified", params = params
249+
SuggestResult()
250+
221251
proc typeDefinition*(ls: LanguageServer, params: TextDocumentPositionParams, id: int):
222252
Future[seq[Location]] {.async.} =
223253
with (params.position, params.textDocument):

suggestapi.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ type
7878
openFiles*: OrderedSet[string]
7979
successfullCall*: bool
8080
errorCallback: NimsuggestCallback
81-
process: Process
81+
process*: Process
8282
port*: int
8383
root: string
8484
requestQueue: Deque[SuggestCall]

tests/all.nim

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import
22
tsuggestapi,
33
tnimlangserver,
4-
tprojectsetup
4+
tprojectsetup,
5+
textensions

tests/textensions.nim

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import ../[
2+
nimlangserver, ls, lstransports, utils
3+
]
4+
import ../protocol/[enums, types]
5+
import std/[options, unittest, json, os, jsonutils, sequtils, strutils, sugar, strformat, osproc]
6+
import json_rpc/[rpcclient]
7+
import chronicles
8+
import lspsocketclient
9+
10+
11+
suite "Nimlangserver":
12+
let cmdParams = CommandLineParams(transport: some socket, port: getNextFreePort())
13+
let ls = main(cmdParams) #we could accesss to the ls here to test against its state
14+
let client = newLspSocketClient()
15+
waitFor client.connect("localhost", cmdParams.port)
16+
client.registerNotification(
17+
"window/showMessage",
18+
"window/workDoneProgress/create",
19+
"workspace/configuration",
20+
"extension/statusUpdate",
21+
"textDocument/publishDiagnostics",
22+
"$/progress"
23+
)
24+
25+
test "calling extension/suggest with restart in the project uri should restart nimsuggest":
26+
let initParams = InitializeParams %* {
27+
"processId": %getCurrentProcessId(),
28+
"rootUri": fixtureUri("projects/hw/"),
29+
"capabilities": {
30+
"window": {
31+
"workDoneProgress": true
32+
},
33+
"workspace": {"configuration": true}
34+
}
35+
}
36+
let initializeResult = waitFor client.initialize(initParams)
37+
38+
check initializeResult.capabilities.textDocumentSync.isSome
39+
40+
let helloWorldUri = fixtureUri("projects/hw/hw.nim")
41+
let helloWorldFile = "projects/hw/hw.nim"
42+
let hwAbsFile = uriToPath(helloWorldFile.fixtureUri())
43+
client.notify("textDocument/didOpen", %createDidOpenParams(helloWorldFile))
44+
45+
let progressParam = %ProgressParams(token: fmt "Creating nimsuggest for {hwAbsFile}")
46+
check waitFor client.waitForNotification("$/progress", (json: JsonNode) => progressParam["token"] == json["token"])
47+
check waitFor client.waitForNotification("$/progress", (json: JsonNode) => json["value"]["kind"].getStr == "begin")
48+
check waitFor client.waitForNotification("$/progress", (json: JsonNode) => json["value"]["kind"].getStr == "end")
49+
50+
client.notify("textDocument/didOpen",
51+
%createDidOpenParams("projects/hw/useRoot.nim"))
52+
53+
let prevSuggestPid = ls.projectFiles[hwAbsFile].waitFor.process.processID
54+
let suggestParams = SuggestParams(action: saRestart, projectFile: hwAbsFile)
55+
let suggestRes = client.call("extension/suggest", %suggestParams).waitFor
56+
let suggestPid = ls.projectFiles[hwAbsFile].waitFor.process.processID
57+
58+
check prevSuggestPid != suggestPid
59+

0 commit comments

Comments
 (0)