Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements restart nimsuggest extension #231

Merged
merged 1 commit into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions ls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import macros, strformat,
chronos, chronos/threadsync,
os, sugar, sequtils, hashes, osproc,
suggestapi, protocol/enums, protocol/types, with, tables, strutils, sets,
./utils, chronicles, std/re, uri,
./utils, chronicles, std/re, uri, std/setutils,
json_serialization, std/json, streams, json_rpc/[servers/socketserver]

proc getVersionFromNimble(): string =
Expand Down Expand Up @@ -87,9 +87,13 @@ type
onStdReadSignal*: ThreadSignalPtr #used by the thread to notify it read from the std
onMainReadSignal*: ThreadSignalPtr #used by the main thread to notify it read the value from the signal
value*: cstring


LspExtensionCapability* = enum #List of extensions this server support. Useful for clients
excRestartSuggest = "RestartSuggest"

LanguageServer* = ref object
clientCapabilities*: ClientCapabilities
extensionCapabilities*: set[LspExtensionCapability]
initializeParams*: InitializeParams
notify*: NotifyAction
call*: CallAction
Expand Down Expand Up @@ -148,6 +152,7 @@ proc initLs*(params: CommandLineParams, storageDir: string): LanguageServer =
responseMap: newTable[string, Future[JsonNode]](),
storageDir: storageDir,
cmdLineClientProcessId: params.clientProcessId,
extensionCapabilities: LspExtensionCapability.items.toSet
)

proc getNimbleEntryPoints*(dumpInfo: NimbleDumpInfo, nimbleProjectPath: string): seq[string] =
Expand Down
13 changes: 8 additions & 5 deletions nimlangserver.nim
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@ proc registerRoutes(srv: RpcSocketServer, ls: LanguageServer) =
srv.register(
"textDocument/documentHighlight", ls.addRpcToCancellable(wrapRpc(partial(documentHighlight, ls)))
)
srv.register("extension/macroExpand", wrapRpc(partial(expand, ls)))
srv.register("extension/status", wrapRpc(partial(status, ls)))
srv.register("shutdown", wrapRpc(partial(shutdown, ls)))
srv.register("exit", wrapRpc(partial(exit, (ls: ls, onExit: ls.onExit))))
#Extension
srv.register("extension/macroExpand", wrapRpc(partial(expand, ls)))
srv.register("extension/status", wrapRpc(partial(status, ls)))
srv.register("extension/capabilities", wrapRpc(partial(extensionCapabilities, ls)))
srv.register("extension/suggest", wrapRpc(partial(extensionSuggest, ls)))

#Notifications
srv.register("$/cancelRequest", wrapRpc(partial(cancelRequest, ls)))
Expand Down Expand Up @@ -65,9 +68,9 @@ proc handleParams(): CommandLineParams =
stderr.writeLine("Invalid client process ID: ", pidStr)
quit 1
if param == "--stdio":
result.transport = some stdio
result.transport = some TransportMode.stdio
if param == "--socket":
result.transport = some socket
result.transport = some TransportMode.socket
if param.startsWith "--port":
let port = param.substr(7)
try:
Expand Down Expand Up @@ -105,7 +108,7 @@ proc registerProcMonitor(ls: LanguageServer) =
hookAsyncProcMonitor(ls.cmdLineClientProcessId.get, onCmdLineClientProcessExit)

proc main*(cmdLineParams: CommandLineParams): LanguageServer =
debug "Starting nimlangserver", params = cmdLineParams
debug "Starting nimlangserver", version = LSPVersion, params = cmdLineParams
#[
`nimlangserver` supports both transports: stdio and socket. By default it uses stdio transport.
But we do construct a RPC socket server even in stdio mode, so that we can reuse the same code for both transports.
Expand Down
2 changes: 1 addition & 1 deletion nimlangserver.nimble
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mode = ScriptMode.Verbose

packageName = "nimlangserver"
version = "1.5.1"
version = "1.5.2"
author = "The core Nim team"
description = "Nim language server for IDEs"
license = "MIT"
Expand Down
Binary file removed nimlangserverold
Binary file not shown.
11 changes: 10 additions & 1 deletion protocol/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -992,4 +992,13 @@ type
openFiles*: seq[string]

NimLangServerStatusParams* = object


SuggestAction* = enum
saNone = "none", saRestart = "restart"

SuggestParams* = object
action*: SuggestAction
projectFile*: string #Absolute path to file

SuggestResult* = object
actionPerformed*: SuggestAction
30 changes: 30 additions & 0 deletions routes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,36 @@ proc status*(ls: LanguageServer, params: NimLangServerStatusParams): Future[NimL
debug "Received status request"
ls.getLspStatus()

proc extensionCapabilities*(ls: LanguageServer, _: JsonNode): Future[seq[string]] {.async.} =
ls.extensionCapabilities.toSeq.mapIt($it)

proc extensionSuggest*(ls: LanguageServer, params: SuggestParams): Future[SuggestResult] {.async.} =
debug "Extension Suggest ", params = params
let projectFile = params.projectFile
if projectFile != "*" and projectFile notin ls.projectFiles:
error "Project file must exists ", params = params
return SuggestResult()
template restart() =
ls.showMessage(fmt "Restarting nimsuggest {projectFile}", MessageType.Info)
ns.stop()
ls.createOrRestartNimsuggest(projectFile, projectFile.pathToUri)
ls.sendStatusChanged()

case params.action:
of saRestart:
if projectFile == "*":
for projectFile, nsFut in ls.projectFiles:
let ns = await nsFut
restart
else:
let ns = await ls.projectFiles[projectFile]
restart

SuggestResult(actionPerformed: saRestart)
of saNone:
error "An action must be specified", params = params
SuggestResult()

proc typeDefinition*(ls: LanguageServer, params: TextDocumentPositionParams, id: int):
Future[seq[Location]] {.async.} =
with (params.position, params.textDocument):
Expand Down
2 changes: 1 addition & 1 deletion suggestapi.nim
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ type
openFiles*: OrderedSet[string]
successfullCall*: bool
errorCallback: NimsuggestCallback
process: Process
process*: Process
port*: int
root: string
requestQueue: Deque[SuggestCall]
Expand Down
3 changes: 2 additions & 1 deletion tests/all.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import
tsuggestapi,
tnimlangserver,
tprojectsetup
tprojectsetup,
textensions
59 changes: 59 additions & 0 deletions tests/textensions.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import ../[
nimlangserver, ls, lstransports, utils
]
import ../protocol/[enums, types]
import std/[options, unittest, json, os, jsonutils, sequtils, strutils, sugar, strformat, osproc]
import json_rpc/[rpcclient]
import chronicles
import lspsocketclient


suite "Nimlangserver":
let cmdParams = CommandLineParams(transport: some socket, port: getNextFreePort())
let ls = main(cmdParams) #we could accesss to the ls here to test against its state
let client = newLspSocketClient()
waitFor client.connect("localhost", cmdParams.port)
client.registerNotification(
"window/showMessage",
"window/workDoneProgress/create",
"workspace/configuration",
"extension/statusUpdate",
"textDocument/publishDiagnostics",
"$/progress"
)

test "calling extension/suggest with restart in the project uri should restart nimsuggest":
let initParams = InitializeParams %* {
"processId": %getCurrentProcessId(),
"rootUri": fixtureUri("projects/hw/"),
"capabilities": {
"window": {
"workDoneProgress": true
},
"workspace": {"configuration": true}
}
}
let initializeResult = waitFor client.initialize(initParams)

check initializeResult.capabilities.textDocumentSync.isSome

let helloWorldUri = fixtureUri("projects/hw/hw.nim")
let helloWorldFile = "projects/hw/hw.nim"
let hwAbsFile = uriToPath(helloWorldFile.fixtureUri())
client.notify("textDocument/didOpen", %createDidOpenParams(helloWorldFile))

let progressParam = %ProgressParams(token: fmt "Creating nimsuggest for {hwAbsFile}")
check waitFor client.waitForNotification("$/progress", (json: JsonNode) => progressParam["token"] == json["token"])
check waitFor client.waitForNotification("$/progress", (json: JsonNode) => json["value"]["kind"].getStr == "begin")
check waitFor client.waitForNotification("$/progress", (json: JsonNode) => json["value"]["kind"].getStr == "end")

client.notify("textDocument/didOpen",
%createDidOpenParams("projects/hw/useRoot.nim"))

let prevSuggestPid = ls.projectFiles[hwAbsFile].waitFor.process.processID
let suggestParams = SuggestParams(action: saRestart, projectFile: hwAbsFile)
let suggestRes = client.call("extension/suggest", %suggestParams).waitFor
let suggestPid = ls.projectFiles[hwAbsFile].waitFor.process.processID

check prevSuggestPid != suggestPid

Loading