Skip to content
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
17 changes: 15 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Setup Nimble
uses: nim-lang/setup-nimble-action@v1
with:
Expand All @@ -26,7 +26,7 @@ jobs:

- name: Restore nimble dependencies from cache
id: nimble_deps
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
~/.nimble
Expand All @@ -42,3 +42,16 @@ jobs:
- name: Nimble Test
shell: bash
run: nimble test

lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check `nph` formatting
uses: arnetheduck/nph-action@v1
with:
version: 0.6.2
options: "*.nim protocol"
fail: true
suggest: true
2 changes: 1 addition & 1 deletion asyncprocmonitor.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ when defined(posix):
import posix_utils
import posix

type Callback* = proc() {.closure, gcsafe, raises: [].}
type Callback* = proc() {.gcsafe, raises: [].}

when defined(windows):
import winlean, sugar
Expand Down
49 changes: 27 additions & 22 deletions ls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ type
nimDumpCache*: Table[string, NimbleDumpInfo] #path to NimbleDumpInfo
entryPoints*: seq[string]
responseMap*: TableRef[string, Future[JsonNode]]
testRunProcess*: Option[AsyncProcessRef] #There is only one test run process at a time
testRunProcess*: Option[AsyncProcessRef]
#There is only one test run process at a time

#id to future. Represents the pending requests as result of calling ls.call
srv*: RpcSocketServer
Expand All @@ -178,7 +179,7 @@ type
failTable*: Table[string, int]
#Project file to fail count
#List of errors (crashes) nimsuggest has had since the lsp session started
checkInProgress*: bool
checkInProgress*: bool

Certainty* = enum
None
Expand Down Expand Up @@ -256,7 +257,9 @@ proc supportSignatureHelp*(cc: ClientCapabilities): bool =
let caps = cc.textDocument
caps.isSome and caps.get.signatureHelp.isSome

proc getNimbleDumpInfo*(ls: LanguageServer, nimbleFile: string): Future[NimbleDumpInfo] {.async.}=
proc getNimbleDumpInfo*(
ls: LanguageServer, nimbleFile: string
): Future[NimbleDumpInfo] {.async.} =
if nimbleFile in ls.nimDumpCache:
return ls.nimDumpCache.getOrDefault(nimbleFile)
var process: AsyncProcessRef
Expand Down Expand Up @@ -290,10 +293,9 @@ proc getNimbleDumpInfo*(ls: LanguageServer, nimbleFile: string): Future[NimbleDu
ls.nimDumpCache[nimbleFile] = result
except OSError, IOError:
debug "Failed to get nimble dump info", nimbleFile = nimbleFile
finally:
finally:
await shutdownChildProcess(process)


proc parseWorkspaceConfiguration*(conf: JsonNode): NlsConfig =
try:
if conf.kind == JObject and conf["settings"].kind == JObject:
Expand Down Expand Up @@ -487,7 +489,7 @@ proc getNimVersion(nimDir: string): string =

proc getNimSuggestPathAndVersion(
ls: LanguageServer, conf: NlsConfig, workingDir: string
): Future[(string, string)] {.async.}=
): Future[(string, string)] {.async.} =
#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 = await ls.getNimbleDumpInfo("")
let nimDir = nimbleDumpInfo.nimDir.get ""
Expand Down Expand Up @@ -518,7 +520,9 @@ proc getNimPath*(conf: NlsConfig): Option[string] =
warn "Failed to find nim path"
none(string)

proc getProjectFileAutoGuess*(ls: LanguageServer, fileUri: string): Future[string] {.async.}=
proc getProjectFileAutoGuess*(
ls: LanguageServer, fileUri: string
): Future[string] {.async.} =
let file = fileUri.decodeUrl
debug "Auto-guessing project file for", file = file
result = file
Expand Down Expand Up @@ -742,7 +746,7 @@ proc sendDiagnostics*(

proc warnIfUnknown*(
ls: LanguageServer, ns: Nimsuggest, uri: string, projectFile: string
): Future[void] {.async, gcsafe.} =
): Future[void] {.async.} =
let path = uri.uriToPath
let isFileKnown = await ns.isKnown(path)
if not isFileKnown and not ns.canHandleUnknown:
Expand Down Expand Up @@ -799,7 +803,7 @@ proc tryGetNimsuggest*(

proc checkFile*(ls: LanguageServer, uri: string): Future[void] {.raises: [], gcsafe.}

proc didCloseFile*(ls: LanguageServer, uri: string): Future[void] {.async, gcsafe.} =
proc didCloseFile*(ls: LanguageServer, uri: string): Future[void] {.async.} =
debug "Closed the following document:", uri = uri

if ls.openFiles[uri].changed:
Expand All @@ -808,22 +812,18 @@ proc didCloseFile*(ls: LanguageServer, uri: string): Future[void] {.async, gcsaf

ls.openFiles.del uri

proc makeIdleFile*(
ls: LanguageServer, file: NlsFileInfo
): Future[void] {.async, gcsafe.} =
proc makeIdleFile*(ls: LanguageServer, file: NlsFileInfo): Future[void] {.async.} =
let uri = file.textDocument.uri
if uri in ls.openFiles:
await ls.didCloseFile(uri)
ls.idleOpenFiles[uri] = file
ls.openFiles.del(uri)

proc getProjectFile*(
fileUri: string, ls: LanguageServer
): Future[string] {.raises: [], gcsafe.}
proc getProjectFile*(fileUri: string, ls: LanguageServer): Future[string] {.async.}

proc didOpenFile*(
ls: LanguageServer, textDocument: TextDocumentItem
): Future[void] {.async, gcsafe.} =
): Future[void] {.async.} =
with textDocument:
debug "New document opened for URI:", uri = uri
let
Expand Down Expand Up @@ -891,11 +891,12 @@ proc tryGetNimsuggest*(
debug "Nimsuggest not found after retries", uri = uri
return none(NimSuggest)

proc checkProject*(ls: LanguageServer, uri: string): Future[void] {.async, gcsafe.} =
proc checkProject*(ls: LanguageServer, uri: string): Future[void] {.async.} =
if ls.checkInProgress:
return
ls.checkInProgress = true
defer: ls.checkInProgress = false
defer:
ls.checkInProgress = false

if not ls.getWorkspaceConfiguration.await().autoCheckProject.get(true):
return
Expand Down Expand Up @@ -950,7 +951,7 @@ proc checkProject*(ls: LanguageServer, uri: string): Future[void] {.async, gcsaf
ls.workDoneProgressCreate(token)
ls.progress(token, "begin", fmt "Checking project {uri.uriToPath}")
nimsuggest.checkProjectInProgress = true
defer:
defer:
nimsuggest.checkProjectInProgress = false
ls.progress(token, "end")

Expand Down Expand Up @@ -1021,7 +1022,8 @@ proc createOrRestartNimsuggest*(
let
configuration = ls.getWorkspaceConfiguration().waitFor()
workingDir = ls.getWorkingDir(projectFile).waitFor()
(nimsuggestPath, version) = ls.getNimSuggestPathAndVersion(configuration, workingDir).waitFor()
(nimsuggestPath, version) =
ls.getNimSuggestPathAndVersion(configuration, workingDir).waitFor()
timeout = configuration.timeout.get(REQUEST_TIMEOUT)
restartCallback = proc(ns: Nimsuggest) {.gcsafe, raises: [].} =
warn "Restarting the server due to requests being to slow",
Expand Down Expand Up @@ -1089,7 +1091,9 @@ proc maybeRegisterCapabilityDidChangeConfiguration*(ls: LanguageServer) =
)
ls.didChangeConfigurationRegistrationRequest =
ls.call("client/registerCapability", %registrationParams)
ls.didChangeConfigurationRegistrationRequest.addCallback do(res: Future[JsonNode]):
ls.didChangeConfigurationRegistrationRequest.addCallback do(res: Future[JsonNode]) {.
gcsafe
.}:
debug "Got response for the didChangeConfiguration registration:",
res = res.read()

Expand Down Expand Up @@ -1184,7 +1188,8 @@ proc getProjectFile*(fileUri: string, ls: LanguageServer): Future[string] {.asyn
let shouldSpawn = await ls.shouldSpawnNimsuggest()
if not shouldSpawn:
result = ls.projectFiles.keys.toSeq[0]
debug "Reached the maximum instances of nimsuggest, reusing the first nimsuggest instance", project = result
debug "Reached the maximum instances of nimsuggest, reusing the first nimsuggest instance",
project = result
return result

result = await ls.getProjectFileAutoGuess(fileUri)
Expand Down
1 change: 1 addition & 0 deletions lstransports.nim
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ proc startSocketServer*(ls: LanguageServer, port: Port) =
return
while ls.socketTransport.isNil:
await sleepAsync(0)

debug "Waiting for socket server to be ready"
waitFor waitUntilSocketTransportIsReady(ls)
debug "Socket server started"
41 changes: 22 additions & 19 deletions nimcheck.nim
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ proc parseCheckResults(lines: seq[string]): seq[CheckResult] =
stacktrace: seq[CheckStacktrace]
lastFile, lastLineStr, lastCharStr: string
m: RegexMatch2

let dotsPattern = re2"^\.+$"
let errorPattern = re2"^([^(]+)\((\d+),\s*(\d+)\)\s*(\w+):\s*(.*)$"

for line in lines:
let line = line.strip()

if line.startsWith("Hint: used config file") or line == "" or line.match(dotsPattern):
if line.startsWith("Hint: used config file") or line == "" or line.match(
dotsPattern
):
continue

if not find(line, errorPattern, m):
Expand All @@ -48,24 +50,25 @@ proc parseCheckResults(lines: seq[string]): seq[CheckResult] =
charStr = line[m.captures[2]]
severity = line[m.captures[3]]
msg = line[m.captures[4]]
let

let
lineNum = parseInt(lineStr)
colNum = parseInt(charStr)

result.add(CheckResult(
file: file,
line: lineNum,
column: colNum,
msg: msg,
severity: severity,
stacktrace: @[]
))

result.add(
CheckResult(
file: file,
line: lineNum,
column: colNum,
msg: msg,
severity: severity,
stacktrace: @[],
)
)
except Exception as e:
error "Error processing line", line = line, msg = e.msg
continue

if messageText.len > 0 and result.len > 0:
result[^1].msg &= "\n" & messageText

Expand All @@ -89,13 +92,13 @@ proc nimCheck*(filePath: string, nimPath: string): Future[seq[CheckResult]] {.as
let res = await process.waitForExit(15.seconds)
# debug "nimCheck exit", res = res
var output = ""
if res == 0: #Nim check return 0 if there are no errors but we still need to check for hints and warnings
output = string.fromBytes(process.stdoutStream.read().await)
if res == 0:
#Nim check return 0 if there are no errors but we still need to check for hints and warnings
output = string.fromBytes(process.stdoutStream.read().await)
else:
output = string.fromBytes(process.stderrStream.read().await)

let lines = output.splitLines()
parseCheckResults(lines)

finally:
await shutdownChildProcess(process)
28 changes: 15 additions & 13 deletions nimexpand.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,29 @@ import utils
import suggestapi
import std/[strformat]


proc extractMacroExpansion*(output: string, targetLine: int): string =
var start = false
for line in output.split({'\n', '\r'}):
if line.len == 0: continue
if line.len == 0:
continue
debug "extractMacroExpansion", line = line, condMet = &".nim({targetLine}," in line
if &".nim({targetLine}," in line:
start = true
elif &".nim" in line and start:
break
if start:
result.add line & "\n"

if result.len > 0:
let macroStart = result.find("macro: ")
if macroStart != -1:
result = result.substr(macroStart + "macro: ".len)
result = result.replace("[ExpandMacro]", "")

proc nimExpandMacro*(nimPath: string, suggest: Suggest, filePath: string): Future[string] {.async.} =
let
proc nimExpandMacro*(
nimPath: string, suggest: Suggest, filePath: string
): Future[string] {.async.} =
let
macroName = suggest.qualifiedPath[suggest.qualifiedPath.len - 1]
line = suggest.line
debug "nimExpandMacro", macroName = macroName, line = line, filePath = filePath
Expand All @@ -39,12 +41,11 @@ proc nimExpandMacro*(nimPath: string, suggest: Suggest, filePath: string): Futur
)
try:
let res = await process.waitForExit(10.seconds)
let output = string.fromBytes(process.stdoutStream.read().await)
let output = string.fromBytes(process.stdoutStream.read().await)
result = extractMacroExpansion(output, line)
finally:
await shutdownChildProcess(process)


proc extractArcExpansion*(output: string, procName: string): string =
var start = false
let cond = &"--expandArc: {procName}"
Expand All @@ -56,12 +57,13 @@ proc extractArcExpansion*(output: string, procName: string): string =
break
if start:
result.add line & "\n"

if result.len > 0:
result = result.replace(cond, "").strip()


proc nimExpandArc*(nimPath: string, suggest: Suggest, filePath: string): Future[string] {.async.} =
proc nimExpandArc*(
nimPath: string, suggest: Suggest, filePath: string
): Future[string] {.async.} =
let procName = suggest.qualifiedPath[suggest.qualifiedPath.len - 1]
debug "nimExpandArc", procName = procName, filePath = filePath
let process = await startProcess(
Expand All @@ -72,11 +74,11 @@ proc nimExpandArc*(nimPath: string, suggest: Suggest, filePath: string): Future[
)
try:
let res = await process.waitForExit(10.seconds)
let output = string.fromBytes(process.stdoutStream.read().await)
let output = string.fromBytes(process.stdoutStream.read().await)
result = extractArcExpansion(output, procName)
# debug "nimExpandArc", output = output, result = result
if result.len == 0:
result = &"#Couldnt expand arc for `{procName}`. Showing raw output instead \n"
result.add output
result.add output
finally:
await shutdownChildProcess(process)
await shutdownChildProcess(process)
Loading