Skip to content
New issue

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

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

Already on GitHub? # to your account

Introduces extension/status #208

Merged
merged 1 commit into from
Jun 12, 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
71 changes: 49 additions & 22 deletions nimlangserver.nim
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ type
nimDir: Option[string]
nimblePath: Option[string]

proc getVersionFromNimble(): string =
#We should static run nimble dump instead
const content = staticRead("nimlangserver.nimble")
for v in content.splitLines:
if v.startsWith("version"):
return v.split("=")[^1].strip(chars = {' ', '"'})
return "unknown"

const LSPVersion = getVersionFromNimble()

createJsonFlavor(LSPFlavour, omitOptionalFields = true)
Option.useDefaultSerializationIn LSPFlavour

Expand Down Expand Up @@ -700,27 +710,30 @@ proc getNimVersion(nimDir: string): string =
if line.startsWith(NimCompilerVersion):
return line

proc getNimSuggestPath(ls: LanguageServer, conf: NlsConfig, workingDir: string): string =
proc getNimSuggestPathAndVersion(ls: LanguageServer, conf: NlsConfig, workingDir: string): (string, string) =
#Attempting to see if the project is using a custom Nim version, if it's the case this will be slower than usual
let nimbleDumpInfo = ls.getNimbleDumpInfo("")
let nimDir = nimbleDumpInfo.nimDir.get ""

result = expandTilde(conf.nimsuggestPath.get(""))
var nimsuggestPath = expandTilde(conf.nimsuggestPath.get(""))
var nimVersion = ""
if result == "":
if nimsuggestPath == "":
if nimDir != "" and nimDir.dirExists:
nimVersion = getNimVersion(nimDir) & " from " & nimDir
result = nimDir / "nimsuggest"
nimsuggestPath = nimDir / "nimsuggest"
else:
nimVersion = getNimVersion("")
result = findExe "nimsuggest"
ls.showMessage(fmt "Using {nimVersion}", MessageType.Info)
nimsuggestPath = findExe "nimsuggest"
else:
nimVersion = getNimVersion(nimsuggestPath.parentDir)
ls.showMessage(fmt "Using {nimVersion}", MessageType.Info)
(nimsuggestPath, nimVersion)

proc createOrRestartNimsuggest(ls: LanguageServer, projectFile: string, uri = ""): void {.gcsafe.} =
let
configuration = ls.getWorkspaceConfiguration().waitFor()
workingDir = ls.getWorkingDir(projectFile).waitFor()
nimsuggestPath = ls.getNimSuggestPath(configuration, workingDir)
(nimsuggestPath, version) = ls.getNimSuggestPathAndVersion(configuration, workingDir)
timeout = configuration.timeout.get(REQUEST_TIMEOUT)
restartCallback = proc (ns: Nimsuggest) {.gcsafe.} =
warn "Restarting the server due to requests being to slow", projectFile = projectFile
Expand All @@ -735,7 +748,7 @@ proc createOrRestartNimsuggest(ls: LanguageServer, projectFile: string, uri = ""
ls.showMessage(fmt "Server failed with {ns.errorMessage}.",
MessageType.Error)

nimsuggestFut = createNimsuggest(projectFile, nimsuggestPath,
nimsuggestFut = createNimsuggest(projectFile, nimsuggestPath, version,
timeout, restartCallback, errorCallback, workingDir, configuration.logNimsuggest.get(false),
configuration.exceptionHintsEnabled)
token = fmt "Creating nimsuggest for {projectFile}"
Expand Down Expand Up @@ -772,11 +785,11 @@ proc restartAllNimsuggestInstances(ls: LanguageServer) =
proc warnIfUnknown(ls: LanguageServer, ns: Nimsuggest, uri: string, projectFile: string):
Future[void] {.async, gcsafe.} =
let path = uri.uriToPath
let sug = await ns.known(path)
if sug[0].forth == "false":
ls.showMessage(fmt """{path} is not compiled as part of project {projectFile}.
In orde to get the IDE features working you must either configure nim.projectMapping or import the module.""",
MessageType.Warning)
let isFileKnown = await ns.isKnown(path)
if not isFileKnown: #TODO only warn when ns doesnt have the unknownFile capability
ls.showMessage(fmt """{path} is not compiled as part of project {projectFile}.
In orde to get the IDE features working you must either configure nim.projectMapping or import the module.""",
MessageType.Warning)

proc didOpen(ls: LanguageServer, params: DidOpenTextDocumentParams):
Future[void] {.async, gcsafe.} =
Expand Down Expand Up @@ -979,6 +992,27 @@ proc expand(ls: LanguageServer, params: ExpandTextDocumentPositionParams):
result = ExpandResult(content: expand[0].doc.fixIdentation(character),
range: expand[0].createRangeFromSuggest())

proc status(ls: LanguageServer, params: NimLangServerStatusParams): Future[NimLangServerStatus] {.async.} =
var status = NimLangServerStatus()
status.version = LSPVersion
for projectFile in ls.projectFiles.keys:
let ns = await ls.projectFiles[projectFile]
var nsStatus = NimSuggestStatus(
projectFile: projectFile,
capabilities: ns.capabilities.toSeq.mapIt($it),
version: ns.version,
path: ns.nimsuggestPath
)
for openFile in ns.openFiles:
let openFilePath = openFile.uriToPath
let isKnown = await ns.isKnown(openFilePath)
if isKnown:
nsStatus.knownFiles.add openFilePath
else:
nsStatus.unknownFiles.add openFilePath
status.nimsuggestInstances.add nsStatus
status

proc typeDefinition(ls: LanguageServer, params: TextDocumentPositionParams, id: int):
Future[seq[Location]] {.async.} =
with (params.position, params.textDocument):
Expand Down Expand Up @@ -1367,6 +1401,7 @@ proc registerHandlers*(connection: StreamConnection,
connection.register("workspace/symbol", partial(workspaceSymbol, ls))
connection.register("textDocument/documentHighlight", partial(documentHighlight, ls))
connection.register("extension/macroExpand", partial(expand, ls))
connection.register("extension/status", partial(status, ls))
connection.register("shutdown", partial(shutdown, ls))
connection.register("exit", partial(exit, (ls: ls, pipeInput: pipeInput)))

Expand All @@ -1390,17 +1425,9 @@ var

when isMainModule:

proc getVersionFromNimble(): string =
const content = staticRead("nimlangserver.nimble")
for v in content.splitLines:
if v.startsWith("version"):
return v.split("=")[^1].strip(chars = {' ', '"'})
return "unknown"

proc handleParams(): CommandLineParams =
if paramCount() > 0 and paramStr(1) in ["-v", "--version"]:
const version = getVersionFromNimble()
echo version
echo LSPVersion
quit()
var i = 1
while i <= paramCount():
Expand Down
16 changes: 16 additions & 0 deletions protocol/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -971,3 +971,19 @@ type
paddingLeft*: Option[bool]
paddingRight*: Option[bool]
#data*: OptionalNode

NimSuggestStatus* = object
projectFile*: string
capabilities*: seq[string]
version*: string
path*: string
pid*: int
knownFiles*: seq[string]
unknownFiles*: seq[string]

NimLangServerStatus* = ref object
version*: string
nimsuggestInstances*: seq[NimSuggestStatus]

NimLangServerStatusParams* = ref object

12 changes: 11 additions & 1 deletion suggestapi.nim
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ type
timeoutCallback: NimsuggestCallback
protocolVersion*: int
capabilities*: set[NimSuggestCapability]
nimSuggestPath*: string
version*: string


template benchmark(benchmarkName: string, code: untyped) =
Expand Down Expand Up @@ -309,6 +311,7 @@ proc getNimsuggestCapabilities*(nimsuggestPath: string):

proc createNimsuggest*(root: string,
nimsuggestPath: string,
version: string,
timeout: int,
timeoutCallback: NimsuggestCallback,
errorCallback: NimsuggestCallback,
Expand All @@ -330,6 +333,8 @@ proc createNimsuggest*(root: string,
result.timeout = timeout
result.timeoutCallback = timeoutCallback
result.errorCallback = errorCallback
result.nimSuggestPath = nimsuggestPath
result.version = version

if nimsuggestPath != "":
result.protocolVersion = detectNimsuggestVersion(root, nimsuggestPath, workingDir)
Expand All @@ -342,6 +347,7 @@ proc createNimsuggest*(root: string,
if enableLog:
args.add("--log")
result.capabilities = getNimsuggestCapabilities(nimsuggestPath)
debug "Nimsuggest Capabilities", capabilities = result.capabilities
if nsExceptionInlayHints in result.capabilities:
if enableExceptionInlayHints:
args.add("--exceptionInlayHints:on")
Expand Down Expand Up @@ -370,7 +376,7 @@ proc createNimsuggest*(root: string,
result.markFailed fmt "Unable to start nimsuggest. `{nimsuggestPath}` is not present on the PATH"

proc createNimsuggest*(root: string): Future[Nimsuggest] {.gcsafe.} =
result = createNimsuggest(root, "nimsuggest", REQUEST_TIMEOUT,
result = createNimsuggest(root, "nimsuggest", "", REQUEST_TIMEOUT,
proc (ns: Nimsuggest) = discard,
proc (ns: Nimsuggest) = discard)

Expand Down Expand Up @@ -495,3 +501,7 @@ createRangeCommand(inlayHints)

proc `mod`*(nimsuggest: Nimsuggest, file: string, dirtyfile = ""): Future[seq[Suggest]] =
return nimsuggest.call("ideMod", file, dirtyfile, 0, 0)

proc isKnown*(nimsuggest: Nimsuggest, filePath: string): Future[bool] {.async.} =
let sug = await nimsuggest.known(filePath)
return sug.len > 0 and sug[0].forth == "true"
9 changes: 9 additions & 0 deletions tests/tprojectsetup.nim
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,15 @@ suite "nimble setup":
res = client.call("textDocument/completion", %completionParams).waitFor
let completionList = res.to(seq[CompletionItem]).mapIt(it.label)
check completionList.len > 0

var resStatus = client.call("extension/status", %()).waitFor
let status = resStatus.to(NimLangServerStatus)[]
check status.nimsuggestInstances.len == 1
let nsInfo = status.nimsuggestInstances[0]
check nsInfo.projectFile == entryPoint
check nsInfo.knownFiles.len == 1
check nsInfo.knownFiles[0] == entryPoint
check nsInfo.unknownFiles.len == 0

# test "`submodule.nim` should not be part of the nimble project file":
# let testProjectDir = absolutePath "tests" / "projects" / "testproject"
Expand Down
Loading