Skip to content

Commit fa3c9bb

Browse files
authored
fix crash on empty file (#217)
* merge * forgot to add fix back in
1 parent 70751d6 commit fa3c9bb

File tree

2 files changed

+62
-54
lines changed

2 files changed

+62
-54
lines changed

ls.nim

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
import macros, strformat,
1+
import macros, strformat,
22
faststreams/async_backend,
33
os, sugar, sequtils, hashes, osproc,
44
suggestapi, protocol/enums, protocol/types, with, tables, strutils, sets,
55
./utils, chronicles, std/re, uri, "$nim/compiler/pathutils",
66
json_serialization, std/json
77

88

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

17-
const
17+
const
1818
RESTART_COMMAND* = "nimlangserver.restart"
1919
RECOMPILE_COMMAND* = "nimlangserver.recompile"
2020
CHECK_PROJECT_COMMAND* = "nimlangserver.checkProject"
@@ -103,15 +103,15 @@ type
103103
Folder,
104104
Cfg,
105105
Nimble
106-
106+
107107
NimbleDumpInfo* = object
108108
srcDir*: string
109109
name*: string
110110
nimDir*: Option[string]
111111
nimblePath*: Option[string]
112112
entryPoints*: seq[string] #when it's empty, means the nimble version doesnt dump it.
113-
114-
OnExitCallback* = proc (): Future[void] {.gcsafe.} #To be called when the server is shutting down
113+
114+
OnExitCallback* = proc (): Future[void] {.gcsafe.} #To be called when the server is shutting down
115115
NotifyAction* = proc (name: string, params: JsonNode) {. gcsafe.} #Send a notification to the client
116116
CallAction* = proc (name: string, params: JsonNode): Future[JsonNode] {. gcsafe.} #Send a request to the client
117117

@@ -162,7 +162,7 @@ func parameterHintsEnabled*(cnf: NlsConfig): bool =
162162
func inlayHintsEnabled*(cnf: NlsConfig): bool =
163163
typeHintsEnabled(cnf) or exceptionHintsEnabled(cnf) or parameterHintsEnabled(cnf)
164164

165-
proc supportSignatureHelp*(cc: ClientCapabilities): bool =
165+
proc supportSignatureHelp*(cc: ClientCapabilities): bool =
166166
if cc.isNil: return false
167167
let caps = cc.textDocument
168168
caps.isSome and caps.get.signatureHelp.isSome
@@ -182,14 +182,19 @@ proc getNimbleDumpInfo*(ls: LanguageServer, nimbleFile: string): NimbleDumpInfo
182182
result.nimblePath = some line[(1 + line.find '"')..^2]
183183
if line.startsWith("entryPoints"):
184184
result.entryPoints = line[(1 + line.find '"')..^2].split(',').mapIt(it.strip(chars = {' ', '"'}))
185-
185+
186186
var nimbleFile = nimbleFile
187187
if nimbleFile == "" and result.nimblePath.isSome:
188188
nimbleFile = result.nimblePath.get
189189
if nimbleFile != "":
190190
ls.nimDumpCache[nimbleFile] = result
191191

192192
proc parseWorkspaceConfiguration*(conf: JsonNode): NlsConfig =
193+
try:
194+
if conf.kind == JObject and conf["settings"].kind == JObject:
195+
return conf["settings"]["nim"].to(NlsConfig)
196+
except CatchableError:
197+
discard
193198
try:
194199
let nlsConfig: seq[NlsConfig] = (%conf).to(seq[NlsConfig])
195200
result = if nlsConfig.len > 0 and nlsConfig[0] != nil: nlsConfig[0] else: NlsConfig()
@@ -200,32 +205,32 @@ proc parseWorkspaceConfiguration*(conf: JsonNode): NlsConfig =
200205
proc getWorkspaceConfiguration*(ls: LanguageServer): Future[NlsConfig] {.async.} =
201206
parseWorkspaceConfiguration(ls.workspaceConfiguration.await)
202207

203-
proc showMessage*(ls: LanguageServer, message: string, typ: MessageType) =
208+
proc showMessage*(ls: LanguageServer, message: string, typ: MessageType) =
204209
proc notify() =
205210
ls.notify(
206211
"window/showMessage",
207212
%* {
208213
"type": typ.int,
209-
"message": message
214+
"message": message
210215
})
211-
let verbosity =
216+
let verbosity =
212217
ls
213218
.getWorkspaceConfiguration
214219
.waitFor
215220
.notificationVerbosity.get(NlsNotificationVerbosity.nvInfo)
216221
debug "ShowMessage ", message = message
217222
case verbosity:
218-
of nvInfo:
223+
of nvInfo:
219224
notify()
220225
of nvWarning:
221226
if typ.int <= MessageType.Warning.int :
222227
notify()
223228
of nvError:
224-
if typ == MessageType.Error:
229+
if typ == MessageType.Error:
225230
notify()
226231
else: discard
227232

228-
proc getLspStatus*(ls: LanguageServer): NimLangServerStatus =
233+
proc getLspStatus*(ls: LanguageServer): NimLangServerStatus =
229234
result.version = LSPVersion
230235
for projectFile, futNs in ls.projectFiles:
231236
let futNs = ls.projectFiles[projectFile]
@@ -237,9 +242,9 @@ proc getLspStatus*(ls: LanguageServer): NimLangServerStatus =
237242
version: ns.version,
238243
path: ns.nimsuggestPath,
239244
port: ns.port,
240-
)
245+
)
241246
result.nimsuggestInstances.add nsStatus
242-
247+
243248
for openFile in ls.openFiles.keys:
244249
let openFilePath = openFile.uriToPath
245250
result.openFiles.add openFilePath
@@ -298,7 +303,7 @@ proc inlayHintsConfigurationEquals*(a, b: NlsConfig): bool =
298303
result = a.inlayHints.isSome == b.inlayHints.isSome
299304

300305
proc getNimVersion(nimDir: string): string =
301-
let cmd =
306+
let cmd =
302307
if nimDir == "": "nim --version"
303308
else: nimDir / "nim --version"
304309
let info = execProcess(cmd)
@@ -311,20 +316,20 @@ proc getNimSuggestPathAndVersion(ls: LanguageServer, conf: NlsConfig, workingDir
311316
#Attempting to see if the project is using a custom Nim version, if it's the case this will be slower than usual
312317
let nimbleDumpInfo = ls.getNimbleDumpInfo("")
313318
let nimDir = nimbleDumpInfo.nimDir.get ""
314-
315-
var nimsuggestPath = expandTilde(conf.nimsuggestPath.get(""))
319+
320+
var nimsuggestPath = expandTilde(conf.nimsuggestPath.get(""))
316321
var nimVersion = ""
317322
if nimsuggestPath == "":
318323
if nimDir != "" and nimDir.dirExists:
319324
nimVersion = getNimVersion(nimDir) & " from " & nimDir
320-
nimsuggestPath = nimDir / "nimsuggest"
325+
nimsuggestPath = nimDir / "nimsuggest"
321326
else:
322327
nimVersion = getNimVersion("")
323328
nimsuggestPath = findExe "nimsuggest"
324329
else:
325330
nimVersion = getNimVersion(nimsuggestPath.parentDir)
326-
ls.showMessage(fmt "Using {nimVersion}", MessageType.Info)
327-
(nimsuggestPath, nimVersion)
331+
ls.showMessage(fmt "Using {nimVersion}", MessageType.Info)
332+
(nimsuggestPath, nimVersion)
328333

329334
proc getProjectFileAutoGuess*(ls: LanguageServer, fileUri: string): string =
330335
let file = fileUri.decodeUrl
@@ -418,7 +423,7 @@ proc cancelPendingFileChecks*(ls: LanguageServer, nimsuggest: Nimsuggest) =
418423
cancelFileCheck.complete()
419424
fileData.needsChecking = false
420425

421-
proc getNimsuggest*(ls: LanguageServer, uri: string): Future[Nimsuggest] {.async, gcsafe.}
426+
proc getNimsuggest*(ls: LanguageServer, uri: string): Future[Nimsuggest] {.async, gcsafe.}
422427

423428
proc uriStorageLocation*(ls: LanguageServer, uri: string): string =
424429
ls.storageDir / (hash(uri).toHex & ".nim")
@@ -661,7 +666,7 @@ proc getProjectFile*(fileUri: string, ls: LanguageServer): Future[string] {.asyn
661666
rootPath = AbsoluteDir(ls.initializeParams.getRootPath)
662667
pathRelativeToRoot = string(AbsoluteFile(fileUri).relativeTo(rootPath))
663668
mappings = ls.getWorkspaceConfiguration.await().projectMapping.get(@[])
664-
669+
665670
for mapping in mappings:
666671
if find(cstring(pathRelativeToRoot), re(mapping.fileRegex), 0, pathRelativeToRoot.len) != -1:
667672
result = string(rootPath) / mapping.projectFile
@@ -692,7 +697,7 @@ proc getProjectFile*(fileUri: string, ls: LanguageServer): Future[string] {.asyn
692697
if ns.canHandleUnknown and not isKnown:
693698
debug "File is not known by nimsuggest", uri = fileUri, projectFile = result
694699
result = fileUri
695-
700+
696701
if result == "":
697702
result = fileUri
698703

@@ -722,4 +727,4 @@ proc checkFile*(ls: LanguageServer, uri: string): Future[void] {.async.} =
722727

723728
ls.progress(token, "end")
724729

725-
ls.sendDiagnostics(diagnostics, path)
730+
ls.sendDiagnostics(diagnostics, path)

routes.nim

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import macros, strformat,
1+
import macros, strformat,
22
faststreams/async_backend,
33
json_rpc/streamconnection, json_rpc/server, os, sugar, sequtils,
44
suggestapi, protocol/enums, protocol/types, with, tables, strutils,
@@ -73,7 +73,7 @@ proc initialize*(p: tuple[ls: LanguageServer, onExit: OnExitCallback], params: I
7373
documentSymbolProvider: some(true),
7474
codeActionProvider: some(true)
7575
)
76-
)
76+
)
7777
# Support rename by default, but check if we can also support prepare
7878
result.capabilities.renameProvider = %true
7979
if params.capabilities.textDocument.isSome:
@@ -86,7 +86,7 @@ proc initialize*(p: tuple[ls: LanguageServer, onExit: OnExitCallback], params: I
8686
}
8787
debug "Initialize completed. Trying to start nimsuggest instances"
8888
#If we are in a nimble project here, we try to start the entry points
89-
89+
9090
proc toCompletionItem(suggest: Suggest): CompletionItem =
9191
with suggest:
9292
return CompletionItem %* {
@@ -115,7 +115,7 @@ proc completion*(ls: LanguageServer, params: CompletionParams, id: int):
115115
for completion in result:
116116
if completion.label notin unique:
117117
unique[completion.label] = completion
118-
result = unique.values.toSeq
118+
result = unique.values.toSeq
119119
120120
proc toLocation*(suggest: Suggest): Location =
121121
return Location %* {
@@ -192,7 +192,7 @@ proc expand*(ls: LanguageServer, params: ExpandTextDocumentPositionParams):
192192
result = ExpandResult(content: expand[0].doc.fixIdentation(character),
193193
range: expand[0].createRangeFromSuggest())
194194

195-
proc status*(ls: LanguageServer, params: NimLangServerStatusParams): Future[NimLangServerStatus] {.async.} =
195+
proc status*(ls: LanguageServer, params: NimLangServerStatusParams): Future[NimLangServerStatus] {.async.} =
196196
debug "Received status request"
197197
ls.getLspStatus()
198198

@@ -468,9 +468,9 @@ proc executeCommand*(ls: LanguageServer, params: ExecuteCommandParams):
468468
ls.progress(token, "end")
469469
ls.checkProject(projectFile.pathToUri).traceAsyncErrors
470470

471-
result = newJNull()
471+
result = newJNull()
472472

473-
proc toSignatureInformation(suggest: Suggest): SignatureInformation =
473+
proc toSignatureInformation(suggest: Suggest): SignatureInformation =
474474
var fnKind, strParams: string
475475
var params = newSeq[ParameterInformation]()
476476
#TODO handle params. Ideally they are handled in the compiler but as fallback we could handle them as follows
@@ -490,16 +490,16 @@ proc toSignatureInformation(suggest: Suggest): SignatureInformation =
490490
"parameters": newSeq[ParameterInformation](), #notice params is not used
491491
}
492492
493-
proc signatureHelp*(ls: LanguageServer, params: SignatureHelpParams, id: int):
494-
Future[Option[SignatureHelp]] {.async.} =
493+
proc signatureHelp*(ls: LanguageServer, params: SignatureHelpParams, id: int):
494+
Future[Option[SignatureHelp]] {.async.} =
495495
#TODO handle prev signature
496496
# if params.context.activeSignatureHelp.isSome:
497497
# let prevSignature = params.context.activeSignatureHelp.get.signatures.get[params.context.activeSignatureHelp.get.activeSignature.get]
498498
# debug "prevSignature ", prevSignature = $prevSignature.label
499499
# else:
500500
# debug "no prevSignature"
501501
#only support signatureHelp if the client supports it
502-
# if docCaps.signatureHelp.isSome and docCaps.signatureHelp.get.contextSupport.get(false):
502+
# if docCaps.signatureHelp.isSome and docCaps.signatureHelp.get.contextSupport.get(false):
503503
# result.capabilities.signatureHelpProvider = SignatureHelpOptions(
504504
# triggerCharacters: some(@["(", ","])
505505
# )
@@ -509,11 +509,11 @@ proc signatureHelp*(ls: LanguageServer, params: SignatureHelpParams, id: int):
509509
with (params.position, params.textDocument):
510510
let nimsuggest = await ls.getNimsuggest(uri)
511511
if nsCon notin nimSuggest.capabilities:
512-
#support signatureHelp only if the current version of NimSuggest supports it.
512+
#support signatureHelp only if the current version of NimSuggest supports it.
513513
return none[SignatureHelp]()
514514
515515
let completions = await nimsuggest
516-
.con(uriToPath(uri),
516+
.con(uriToPath(uri),
517517
ls.uriToStash(uri),
518518
line + 1,
519519
ls.getCharacter(uri, line, character))
@@ -525,7 +525,7 @@ proc signatureHelp*(ls: LanguageServer, params: SignatureHelpParams, id: int):
525525
activeSignature: some(0),
526526
activeParameter: some(0)
527527
)
528-
else:
528+
else:
529529
return none[SignatureHelp]()
530530
531531
proc workspaceSymbol*(ls: LanguageServer, params: WorkspaceSymbolParams, id: int):
@@ -603,18 +603,21 @@ proc setTrace*(ls: LanguageServer, params: SetTraceParams) {.async.} =
603603
proc didChange*(ls: LanguageServer, params: DidChangeTextDocumentParams):
604604
Future[void] {.async, gcsafe.} =
605605
with params:
606-
let
607-
uri = textDocument.uri
608-
file = open(ls.uriStorageLocation(uri), fmWrite)
606+
let
607+
uri = textDocument.uri
608+
file = open(ls.uriStorageLocation(uri), fmWrite)
609609
610-
ls.openFiles[uri].fingerTable = @[]
611-
ls.openFiles[uri].changed = true
612-
for line in contentChanges[0].text.splitLines:
613-
ls.openFiles[uri].fingerTable.add line.createUTFMapping()
614-
file.writeLine line
615-
file.close()
610+
ls.openFiles[uri].fingerTable = @[]
611+
ls.openFiles[uri].changed = true
612+
if contentChanges.len <= 0:
613+
file.close()
614+
return
615+
for line in contentChanges[0].text.splitLines:
616+
ls.openFiles[uri].fingerTable.add line.createUTFMapping()
617+
file.writeLine line
618+
file.close()
616619

617-
ls.scheduleFileCheck(uri)
620+
ls.scheduleFileCheck(uri)
618621

619622
proc didSave*(ls: LanguageServer, params: DidSaveTextDocumentParams):
620623
Future[void] {.async, gcsafe.} =
@@ -627,7 +630,7 @@ proc didSave*(ls: LanguageServer, params: DidSaveTextDocumentParams):
627630
if ls.getWorkspaceConfiguration().await().checkOnSave.get(true):
628631
debug "Checking project", uri = uri
629632
traceAsyncErrors ls.checkProject(uri)
630-
633+
631634
var toStop = newTable[string, Nimsuggest]()
632635
#We first get the project file for the current file so we can test if this file recently imported another project
633636
let thisProjectFile = await getProjectFile(uri.uriToPath, ls)
@@ -636,9 +639,9 @@ proc didSave*(ls: LanguageServer, params: DidSaveTextDocumentParams):
636639
for projectFile in ls.projectFiles.keys:
637640
if projectFile in ls.entryPoints: continue
638641
let isKnown = await ns.isKnown(projectFile)
639-
if isKnown:
642+
if isKnown:
640643
toStop[projectFile] = await ls.projectFiles[projectFile]
641-
644+
642645
for projectFile, ns in toStop:
643646
ns.stop()
644647
ls.projectFiles.del projectFile
@@ -683,13 +686,13 @@ proc didOpen*(ls: LanguageServer, params: DidOpenTextDocumentParams):
683686
ls.getNimsuggest(uri).addCallback() do (fut: Future[Nimsuggest]) -> void:
684687
if not fut.failed:
685688
discard ls.warnIfUnknown(fut.read, uri, projectFile)
686-
689+
687690
let projectFileUri = projectFile.pathToUri
688691
if projectFileUri notin ls.openFiles:
689692
var params = params
690693
params.textDocument.uri = projectFileUri
691694
await didOpen(ls, params)
692-
695+
693696
debug "Opening project file", uri = projectFile, file = uri
694697
695698
proc didChangeConfiguration*(ls: LanguageServer, conf: JsonNode):

0 commit comments

Comments
 (0)