20
20
uri,
21
21
json_serialization,
22
22
json_rpc/ [servers/ socketserver],
23
- regex
23
+ regex,
24
+ nimcheck
24
25
25
26
proc getVersionFromNimble(): string =
26
27
# We should static run nimble dump instead
38
39
LSPVersion* = getVersionFromNimble()
39
40
CRLF* = " \r\n "
40
41
CONTENT_LENGTH* = " Content-Length: "
42
+ USE_NIM_CHECK_BY_DEFAULT* = false
41
43
type
42
44
NlsNimsuggestConfig* = ref object of RootObj
43
45
projectFile* : string
83
85
notificationVerbosity* : Option[NlsNotificationVerbosity]
84
86
formatOnSave* : Option[bool ]
85
87
nimsuggestIdleTimeout* : Option[int ] # idle timeout in ms
88
+ useNimCheck* : Option[bool ]
86
89
87
90
NlsFileInfo* = ref object of RootObj
88
91
projectFile* : Future[string ]
@@ -287,9 +290,19 @@ proc getWorkspaceConfiguration*(
287
290
# TODO review and handle project specific confs when received instead of reliying in this func
288
291
if ls.workspaceConfiguration.finished:
289
292
return parseWorkspaceConfiguration(ls.workspaceConfiguration.read)
290
- else :
291
- return NlsConfig()
293
+ return NlsConfig()
292
294
except CatchableError as ex:
295
+ error " Failed to get workspace configuration" , error = ex.msg
296
+ writeStackTrace(ex)
297
+
298
+ proc getAndWaitForWorkspaceConfiguration* (
299
+ ls: LanguageServer
300
+ ): Future[NlsConfig] {.async.} =
301
+ try :
302
+ let conf = await ls.workspaceConfiguration
303
+ return parseWorkspaceConfiguration(conf)
304
+ except CatchableError as ex:
305
+ error " Failed to get workspace configuration" , error = ex.msg
293
306
writeStackTrace(ex)
294
307
295
308
proc showMessage* (
@@ -462,6 +475,17 @@ proc getNimSuggestPathAndVersion(
462
475
ls.showMessage(fmt " Using {nimVersion}" , MessageType.Info)
463
476
(nimsuggestPath, nimVersion)
464
477
478
+ proc getNimPath* (conf: NlsConfig): Option[string ] =
479
+ if conf.nimSuggestPath.isSome:
480
+ some(conf.nimSuggestPath.get.parentDir / " nim" )
481
+ else :
482
+ let path = findExe " nim"
483
+ if path != " " :
484
+ some(path)
485
+ else :
486
+ warn " Failed to find nim path"
487
+ none(string )
488
+
465
489
proc getProjectFileAutoGuess* (ls: LanguageServer, fileUri: string ): string =
466
490
let file = fileUri.decodeUrl
467
491
debug " Auto-guessing project file for" , file = file
@@ -580,6 +604,18 @@ proc toUtf16Pos*(
580
604
if pos.isSome:
581
605
result .column = pos.get()
582
606
607
+ proc toUtf16Pos* (checkResult: CheckResult, ls: LanguageServer): CheckResult =
608
+ result = checkResult
609
+ let uri = pathToUri(checkResult.file)
610
+ let pos = toUtf16Pos(ls, uri, checkResult.line - 1 , checkResult.column)
611
+ if pos.isSome:
612
+ result .column = pos.get()
613
+
614
+ for i in 0 ..< result .stacktrace.len:
615
+ let stPos = toUtf16Pos(ls, uri, result .stacktrace[i].line - 1 , result .stacktrace[i].column)
616
+ if stPos.isSome:
617
+ result .stacktrace[i].column = stPos.get()
618
+
583
619
proc range * (startLine, startCharacter, endLine, endCharacter: int ): Range =
584
620
return
585
621
Range %* {
@@ -619,13 +655,39 @@ proc toDiagnostic(suggest: Suggest): Diagnostic =
619
655
}
620
656
return node.to(Diagnostic)
621
657
622
- proc sendDiagnostics* (ls: LanguageServer, diagnostics: seq [Suggest], path: string ) =
658
+ proc toDiagnostic(checkResult: CheckResult): Diagnostic =
659
+ let
660
+ textStart = checkResult.msg.find('\' ' )
661
+ textEnd = checkResult.msg.rfind('\' ' )
662
+ endColumn =
663
+ if textStart >= 0 and textEnd >= 0 and textEnd > textStart:
664
+ checkResult.column + utf16Len(checkResult.msg[textStart + 1 ..< textEnd])
665
+ else :
666
+ checkResult.column + 1
667
+
668
+ let node =
669
+ %* {
670
+ " uri" : pathToUri(checkResult.file),
671
+ " range" : range (checkResult.line - 1 , max(0 , checkResult.column), checkResult.line - 1 , max(0 , endColumn)),
672
+ " severity" :
673
+ case checkResult.severity
674
+ of " Error" : DiagnosticSeverity.Error.int
675
+ of " Hint" : DiagnosticSeverity.Hint.int
676
+ of " Warning" : DiagnosticSeverity.Warning.int
677
+ else : DiagnosticSeverity.Error.int
678
+ ,
679
+ " message" : checkResult.msg,
680
+ " source" : " nim" ,
681
+ " code" : " nim check" ,
682
+ }
683
+ return node.to(Diagnostic)
684
+
685
+ proc sendDiagnostics* (ls: LanguageServer, diagnostics: seq [Suggest] | seq [CheckResult], path: string ) =
623
686
debug " Sending diagnostics" , count = diagnostics.len, path = path
624
687
let params =
625
688
PublishDiagnosticsParams %*
626
689
{" uri" : pathToUri(path), " diagnostics" : diagnostics.map(x => x.toUtf16Pos(ls).toDiagnostic)}
627
690
ls.notify(" textDocument/publishDiagnostics" , % params)
628
-
629
691
if diagnostics.len != 0 :
630
692
ls.filesWithDiags.incl path
631
693
else :
@@ -638,6 +700,40 @@ proc tryGetNimsuggest*(
638
700
proc checkProject* (ls: LanguageServer, uri: string ): Future[void ] {.async, gcsafe.} =
639
701
if not ls.getWorkspaceConfiguration.await().autoCheckProject.get(true ):
640
702
return
703
+ let conf = await ls.getAndWaitForWorkspaceConfiguration()
704
+ let useNimCheck = conf.useNimCheck.get(USE_NIM_CHECK_BY_DEFAULT)
705
+
706
+ let nimPath = getNimPath(conf)
707
+
708
+ if useNimCheck and nimPath.isSome:
709
+ proc getFilePath(c: CheckResult): string = c.file
710
+
711
+ let token = fmt " Checking {uri}"
712
+ ls.workDoneProgressCreate(token)
713
+ ls.progress(token, " begin" , fmt " Checking project {uri}" )
714
+ if uri == " " :
715
+ warn " Checking project with empty uri" , uri = uri
716
+ ls.progress(token, " end" )
717
+ return
718
+ let diagnostics = await nimCheck(uriToPath(uri), nimPath.get)
719
+ let filesWithDiags = diagnostics.map(r => r.file).toHashSet
720
+
721
+ ls.progress(token, " end" )
722
+
723
+ debug " Found diagnostics" , file = filesWithDiags
724
+ for (path, diags) in groupBy(diagnostics, getFilePath):
725
+ ls.sendDiagnostics(diags, path)
726
+
727
+ # clean files with no diags
728
+ for path in ls.filesWithDiags:
729
+ if not filesWithDiags.contains path:
730
+ debug " Sending zero diags" , path = path
731
+ let params =
732
+ PublishDiagnosticsParams %* {" uri" : pathToUri(path), " diagnostics" : @ []}
733
+ ls.notify(" textDocument/publishDiagnostics" , % params)
734
+ ls.filesWithDiags = filesWithDiags
735
+ return
736
+
641
737
debug " Running diagnostics" , uri = uri
642
738
let ns = ls.tryGetNimsuggest(uri).await
643
739
if ns.isNone:
@@ -923,15 +1019,22 @@ proc warnIfUnknown*(
923
1019
)
924
1020
925
1021
proc checkFile* (ls: LanguageServer, uri: string ): Future[void ] {.async.} =
926
- debug " Checking" , uri = uri
1022
+ let conf = await ls.getAndWaitForWorkspaceConfiguration()
1023
+ let useNimCheck = conf.useNimCheck.get(USE_NIM_CHECK_BY_DEFAULT)
1024
+ let nimPath = conf.getNimPath()
927
1025
let token = fmt " Checking file {uri}"
928
1026
ls.workDoneProgressCreate(token)
929
1027
ls.progress(token, " begin" , fmt " Checking {uri.uriToPath}" )
930
1028
931
- let
932
- path = uriToPath(uri)
933
- ns = await ls.tryGetNimsuggest(uri)
1029
+ let path = uriToPath(uri)
1030
+
1031
+ if useNimCheck and nimPath.isSome:
1032
+ let checkResults = await nimCheck(uriToPath(uri), nimPath.get)
1033
+ ls.progress(token, " end" )
1034
+ ls.sendDiagnostics(checkResults, path)
1035
+ return
934
1036
1037
+ let ns = await ls.tryGetNimsuggest(uri)
935
1038
if ns.isSome:
936
1039
let diagnostics = ns.get().chkFile(path, ls.uriToStash(uri)).await()
937
1040
ls.progress(token, " end" )
0 commit comments