Skip to content

Commit

Permalink
Adds ProjectErrors into Status so the extension can show nimsuggest c…
Browse files Browse the repository at this point in the history
…rashes in the panel
  • Loading branch information
jmgomez committed Sep 18, 2024
1 parent 18cead2 commit 8da38cf
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 36 deletions.
46 changes: 31 additions & 15 deletions ls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ type
startTime*: DateTime
endTime*: DateTime
state*: PendingRequestState

LanguageServer* = ref object
clientCapabilities*: ClientCapabilities
extensionCapabilities*: set[LspExtensionCapability]
Expand Down Expand Up @@ -129,7 +129,8 @@ type
socketTransport*: StreamTransport
of stdio:
outStream*: FileStream
stdinContext*: ptr ReadStdinContext
stdinContext*: ptr ReadStdinContext
projectErrors*: seq[ProjectError] #List of errors (crashes) nimsuggest has had since the lsp session started

Certainty* = enum
None,
Expand Down Expand Up @@ -311,6 +312,7 @@ proc getLspStatus*(ls: LanguageServer): NimLangServerStatus {.raises: [].} =
result.openFiles.add openFilePath

result.pendingRequests = ls.pendingRequests.values.toSeq.map(toPendingRequestStatus)
result.projectErrors = ls.projectErrors

proc sendStatusChanged*(ls: LanguageServer) {.raises: [].} =
let status: NimLangServerStatus = ls.getLspStatus()
Expand Down Expand Up @@ -604,7 +606,30 @@ proc checkProject*(ls: LanguageServer, uri: string): Future[void] {.async, gcsaf
debug "Running delayed check project...", uri = uri
traceAsyncErrors ls.checkProject(uri)

proc createOrRestartNimsuggest*(ls: LanguageServer, projectFile: string, uri = ""): void {.gcsafe, raises: [].} =
proc createOrRestartNimsuggest*(ls: LanguageServer, projectFile: string, uri = "") {.gcsafe, raises: [].}

proc onErrorCallback(args: (LanguageServer, string), project: Project) =
let
ls = args[0]
uri = args[1]
debug "NimSuggest needed to be restarted due to an error "
let configuration = ls.getWorkspaceConfiguration().waitFor()
warn "Server stopped.", projectFile = project.file
try:
if configuration.autoRestart.get(true) and project.ns.completed and project.ns.read.successfullCall:
ls.createOrRestartNimsuggest(project.file, uri)
else:
ls.showMessage(fmt "Server failed with {project.errorMessage}.",
MessageType.Error)
except CatchableError as ex:
error "An error has ocurred while handling nimsuggest err", msg = ex.msg
writeStacktrace(ex)
finally:
ls.projectErrors.add ProjectError(projectFile: project.file, errorMessage: project.errorMessage)
ls.sendStatusChanged()


proc createOrRestartNimsuggest*(ls: LanguageServer, projectFile: string, uri = "") {.gcsafe, raises: [].} =
try:
let
configuration = ls.getWorkspaceConfiguration().waitFor()
Expand All @@ -617,16 +642,7 @@ proc createOrRestartNimsuggest*(ls: LanguageServer, projectFile: string, uri = "
MessageType.Warning)
ls.createOrRestartNimsuggest(projectFile, uri)
ls.sendStatusChanged()
errorCallback = proc (ns: Nimsuggest) {.gcsafe, raises: [].} =
debug "NimSuggest needed to be restarted due to an error "
warn "Server stopped.", projectFile = projectFile
if configuration.autoRestart.get(true) and ns.successfullCall:
ls.createOrRestartNimsuggest(projectFile, uri)
else:
ls.showMessage(fmt "Server failed with {ns.errorMessage}.",
MessageType.Error)
ls.sendStatusChanged()

errorCallback = partial(onErrorCallback, (ls, uri))
#TODO instead of waiting here, this whole function should be async.
projectNext = waitFor createNimsuggest(projectFile, nimsuggestPath, version,
timeout, restartCallback, errorCallback, workingDir, configuration.logNimsuggest.get(false),
Expand All @@ -645,8 +661,8 @@ proc createOrRestartNimsuggest*(ls: LanguageServer, projectFile: string, uri = "
ls.projectFiles[projectFile] = projectNext

projectNext.ns.addCallback do (fut: Future[Nimsuggest]):
if fut.read.failed:
let msg = fut.read.errorMessage
if fut.read.project.failed:
let msg = fut.read.project.errorMessage
ls.showMessage(fmt "Nimsuggest initialization for {projectFile} failed with: {msg}",
MessageType.Error)
else:
Expand Down
20 changes: 10 additions & 10 deletions nimble.lock
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
{
"version": 2,
"packages": {
"nim": {
"version": "2.0.8",
"vcsRevision": "5935c3bfa9fec6505394867b23510eb5cbab3dbf",
"url": "https://github.com/nim-lang/Nim.git",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "46333e8f4bda41dd6d3852a3f5fa4975b96b66a2"
}
},
"unittest2": {
"version": "0.2.2",
"vcsRevision": "e96f3215030cbfa13abc2f5827069b6f8ba87e38",
Expand Down Expand Up @@ -223,6 +213,16 @@
"sha1": "5c451e05b217e19d5a5ebaf087ecbb8576257a73"
}
},
"nim": {
"version": "2.0.8",
"vcsRevision": "5935c3bfa9fec6505394867b23510eb5cbab3dbf",
"url": "https://github.com/nim-lang/Nim.git",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "46333e8f4bda41dd6d3852a3f5fa4975b96b66a2"
}
},
"with": {
"version": "0.5.0",
"vcsRevision": "91c51ec1051bf0cb518cf9bb78114e2a84b03da7",
Expand Down
25 changes: 14 additions & 11 deletions suggestapi.nim
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideType, ideExpand
NimsuggestCallback = proc(self: Nimsuggest): void {.gcsafe, raises: [].}
ProjectCallback = proc(self: Project): void {.gcsafe, raises: [].}

Suggest* = ref object
section*: IdeCmd
Expand Down Expand Up @@ -73,13 +74,10 @@ type
tooltip*: string

NimsuggestImpl* = object
failed*: bool
errorMessage*: string
checkProjectInProgress*: bool
needsCheckProject*: bool
openFiles*: OrderedSet[string]
successfullCall*: bool
errorCallback*: NimsuggestCallback
port*: int
root: string
requestQueue: Deque[SuggestCall]
Expand All @@ -90,13 +88,17 @@ type
capabilities*: set[NimSuggestCapability]
nimSuggestPath*: string
version*: string
project*: Project

NimSuggest* = ref NimsuggestImpl

Project* = ref object
ns*: Future[NimSuggest]
file*: string
process*: AsyncProcessRef
errorCallback*: ProjectCallback
errorMessage*: string
failed*: bool

func canHandleUnknown*(ns: Nimsuggest): bool =
nsUnknownFile in ns.capabilities
Expand Down Expand Up @@ -224,7 +226,7 @@ proc parseSuggestInlayHint*(line: string): SuggestInlayHint =
proc name*(sug: Suggest): string =
return sug.qualifiedPath[^1]

proc markFailed(self: Nimsuggest, errMessage: string) {.raises: [].} =
proc markFailed(self: Project, errMessage: string) {.raises: [].} =
self.failed = true
self.errorMessage = errMessage
if self.errorCallback != nil:
Expand Down Expand Up @@ -296,28 +298,29 @@ proc getNimsuggestCapabilities*(nimsuggestPath: string):
proc logNsError(project: Project) {.async.} =
let err = string.fromBytes(project.process.stderrStream.read().await)
error "NimSuggest Error (stderr)", err = err
# ns.markFailed(err) #TODO Error handling should be at the project level
project.markFailed(err) #TODO Error handling should be at the project level

proc createNimsuggest*(root: string,
nimsuggestPath: string,
version: string,
timeout: int,
timeoutCallback: NimsuggestCallback,
errorCallback: NimsuggestCallback,
errorCallback: ProjectCallback,
workingDir = getCurrentDir(),
enableLog: bool = false,
enableExceptionInlayHints: bool = false): Future[Project] {.async, gcsafe.} =
result = Project(file: root)
result.ns = newFuture[NimSuggest]()
result.errorCallback = errorCallback

let ns = Nimsuggest()
ns.requestQueue = Deque[SuggestCall]()
ns.root = root
ns.timeout = timeout
ns.timeoutCallback = timeoutCallback
ns.errorCallback = errorCallback
ns.nimSuggestPath = nimsuggestPath
ns.version = version
ns.project = result

info "Starting nimsuggest", root = root, timeout = timeout, path = nimsuggestPath,
workingDir = workingDir
Expand Down Expand Up @@ -348,12 +351,12 @@ proc createNimsuggest*(root: string,
result.ns.complete(ns)
else:
error "Unable to start nimsuggest. Unable to find binary on the $PATH", nimsuggestPath = nimsuggestPath
ns.markFailed fmt "Unable to start nimsuggest. `{nimsuggestPath}` is not present on the PATH"
result.markFailed fmt "Unable to start nimsuggest. `{nimsuggestPath}` is not present on the PATH"

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

proc toString*(bytes: openarray[byte]): string =
result = newString(bytes.len)
Expand All @@ -368,7 +371,7 @@ proc processQueue(self: Nimsuggest): Future[void] {.async.}=
command = req.commandString
if req.future.finished:
debug "Call cancelled before executed", command = req.command
elif self.failed:
elif self.project.failed:
debug "Nimsuggest is not working, returning empty result...", port = self.port
req.future.complete @[]
else:
Expand Down Expand Up @@ -407,7 +410,7 @@ proc processQueue(self: Nimsuggest): Future[void] {.async.}=
res.add sug.get

if (content == ""):
self.markFailed "Server crashed/socket closed."
self.project.markFailed "Server crashed/socket closed."
debug "Server socket closed"
if not req.future.finished:
debug "Call cancelled before sending error", command = req.command
Expand Down
6 changes: 6 additions & 0 deletions utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,12 @@ proc partial*[A, B, C](
return proc(b: B): C {.gcsafe, raises: [].} =
return fn(a, b)

proc partial*[A, B](
fn: proc(a: A, b: B): void {.gcsafe, raises: [], nimcall.}, a: A
): proc(b: B): void {.gcsafe, raises: [].} =
return proc(b: B): void {.gcsafe, raises: [].} =
fn(a, b)

proc partial*[A, B, C, D](
fn: proc(a: A, b: B, c: C): D {.gcsafe, raises: [], nimcall.}, a: A
): proc(b: B, c: C): D {.gcsafe, raises: [].} =
Expand Down

0 comments on commit 8da38cf

Please sign in to comment.