Skip to content
Open
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
78 changes: 67 additions & 11 deletions src/nimlsp.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import nimlsppkg / [baseprotocol, utfmapping, suggestlib, logger]
import nimlsppkg / [baseprotocol, utfmapping, suggestlib, logger, pnode_parse]
include nimlsppkg / messages
import algorithm
import streams
Expand Down Expand Up @@ -28,7 +28,7 @@ const
type
UriParseError* = object of Defect
uri: string

FileTuple = tuple[projectFile: string, fingerTable: seq[seq[tuple[u16pos, offset: int]]], syntaxOk: bool, error: ref Exception]
var nimpath = explicitSourcePath
discard existsOrCreateDir(storage)
infoLog("Version: ", version)
Expand All @@ -42,7 +42,7 @@ var
gotShutdown = false
initialized = false
projectFiles = initTable[string, tuple[nimsuggest: NimSuggest, openFiles: OrderedSet[string]]]()
openFiles = initTable[string, tuple[projectFile: string, fingerTable: seq[seq[tuple[u16pos, offset: int]]]]]()
openFiles = initTable[string, FileTuple]()

template whenValid(data, kind, body) =
if data.isValid(kind, allowExtra = true):
Expand Down Expand Up @@ -134,6 +134,41 @@ proc error(request: RequestMessage, errorCode: int, message: string, data: JsonN
proc notify(notification: string, data: JsonNode) {.async.} =
await outs.sendJson create(NotificationMessage, "2.0", notification, some(data)).JsonNode

proc sendParseError(request: RequestMessage, err: ref Exception) {.async.} =
await outs.sendJson create(ResponseMessage, "2.0", parseId(request["id"]), none(JsonNode), none(ResponseError)).JsonNode

proc docUri[T](p: T): string =
p["textDocument"]["uri"].getStr

template pushError(p: untyped, error: ref Exception) =
var response: seq[Diagnostic]
let stack = error.getStackTraceEntries
debugEcho "push Error stack:" & repr stack
if stack.len > 0:
let diagnostic = stack[0]
response.add create(Diagnostic,
create(Range,
create(Position, diagnostic.line-1,0),
create(Position, diagnostic.line-1, 0)
),
some(DiagnosticSeverity.Error.int),
none(int),
some("compiler parser"),
error.msg,
none(seq[DiagnosticRelatedInformation])
)
await notify("textDocument/publishDiagnostics", create(PublishDiagnosticsParams,
p.docUri,
response).JsonNode
)

template syntaxCheck(request: RequestMessage, p: untyped) =
if openFiles.hasKey(p.docUri):
if openFiles[p.docUri].syntaxOk == false:
pushError(p,openFiles[p.docUri].error)
await request.sendParseError(openFiles[p.docUri].error)
continue

type Certainty = enum
None,
Folder,
Expand Down Expand Up @@ -253,6 +288,7 @@ proc main(){.async.} =
)).JsonNode)
of "textDocument/completion":
message.textDocumentRequest(CompletionParams, compRequest):
message.syntaxCheck(compRequest)
debugLog "Running equivalent of: sug ", uriToPath(fileuri), ";", filestash, ":",
rawLine + 1, ":",
openFiles[fileuri].fingerTable[rawLine].utf16to8(rawChar)
Expand Down Expand Up @@ -301,6 +337,7 @@ proc main(){.async.} =
await message.respond completionItems
of "textDocument/hover":
message.textDocumentRequest(TextDocumentPositionParams, hoverRequest):
message.syntaxCheck(hoverRequest)
debugLog "Running equivalent of: def ", uriToPath(fileuri), ";", filestash, ":",
rawLine + 1, ":",
openFiles[fileuri].fingerTable[rawLine].utf16to8(rawChar)
Expand Down Expand Up @@ -392,6 +429,7 @@ proc main(){.async.} =
).JsonNode
of "textDocument/definition":
message.textDocumentRequest(TextDocumentPositionParams, definitionRequest):
message.syntaxCheck(definitionRequest)
debugLog "Running equivalent of: def ", uriToPath(fileuri), ";", filestash, ":",
rawLine + 1, ":",
openFiles[fileuri].fingerTable[rawLine].utf16to8(rawChar)
Expand All @@ -417,6 +455,7 @@ proc main(){.async.} =
await message.respond response
of "textDocument/documentSymbol":
message.textDocumentRequest(DocumentSymbolParams, symbolRequest):
message.syntaxCheck(symbolRequest)
debugLog "Running equivalent of: outline ", uriToPath(fileuri), ";", filestash
let syms = getNimsuggest(fileuri).outline(uriToPath(fileuri), dirtyfile = filestash)
debugLog "Found outlines: ",
Expand Down Expand Up @@ -488,18 +527,22 @@ proc main(){.async.} =
let
file = open(filestash, fmWrite)
projectFile = getProjectFile(uriToPath(fileuri))
text = textDoc["textDocument"]["text"].getStr
syntax = parsePNodeStr(text,textDoc.docUri.uriToPath)
debugLog "New document opened for URI: ", fileuri, " saving to " & filestash
openFiles[fileuri] = (
#nimsuggest: initNimsuggest(uriToPath(fileuri)),
projectFile: projectFile,
fingerTable: @[]
)

if not projectFiles.hasKey(projectFile):
debugLog "Initialising project with ", projectFile, ":", nimpath
projectFiles[projectFile] = (nimsuggest: initNimsuggest(projectFile, nimpath), openFiles: initOrderedSet[string]())
projectFiles[projectFile].openFiles.incl(fileuri)

else:
projectFiles[projectFile].openFiles.incl(fileuri)
var t: FileTuple = (
projectFile: projectFile,
fingerTable: @[],
syntaxOk: syntax.ok,
error: default(ref Exception)
)
openFiles[textDoc.docUri] = t
for line in textDoc["textDocument"]["text"].getStr.splitLines:
openFiles[fileuri].fingerTable.add line.createUTFMapping()
file.writeLine line
Expand All @@ -509,7 +552,12 @@ proc main(){.async.} =
let file = open(filestash, fmWrite)
debugLog "Got document change for URI: ", fileuri, " saving to " & filestash
openFiles[fileuri].fingerTable = @[]
for line in textDoc["contentChanges"][0]["text"].getStr.splitLines:
let text = textDoc["contentChanges"][0]["text"].getStr
let syntax = parsePNodeStr(text,textDoc.docUri.uriToPath)
openFiles[textDoc.docUri].syntaxOk = syntax.ok
if syntax.ok == false:
openFiles[textDoc.docUri].error = syntax.error
for line in text.splitLines:
openFiles[fileuri].fingerTable.add line.createUTFMapping()
file.writeLine line
file.close()
Expand All @@ -530,12 +578,20 @@ proc main(){.async.} =
message.textDocumentNotification(DidSaveTextDocumentParams, textDoc):
if textDoc["text"].isSome:
let file = open(filestash, fmWrite)
let text = textDoc["text"].unsafeGet.getStr
let syntax = parsePNodeStr(text,textDoc.docUri.uriToPath)
openFiles[textDoc.docUri].syntaxOk = syntax.ok
if syntax.ok == false:
openFiles[textDoc.docUri].error = syntax.error
debugLog "Got document save for URI: ", fileuri, " saving to ", filestash
openFiles[fileuri].fingerTable = @[]
for line in textDoc["text"].unsafeGet.getStr.splitLines:
openFiles[fileuri].fingerTable.add line.createUTFMapping()
file.writeLine line
file.close()
if not syntax.ok:
pushError(textDoc,syntax.error)
continue
debugLog "fileuri: ", fileuri, ", project file: ", openFiles[fileuri].projectFile, ", dirtyfile: ", filestash

let diagnostics = getNimsuggest(fileuri).chk(uriToPath(fileuri), dirtyfile = filestash)
Expand Down
71 changes: 71 additions & 0 deletions src/nimlsppkg/pnode_parse.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from os import nil
import macros, os
import encodings
const explicitSourcePath {.strdefine.} = getCurrentCompilerExe().parentDir.parentDir

macro mImport(path: static[string]): untyped =
result = nnkImportStmt.newTree(newLit(path))

mImport(os.joinPath("compiler", "parser.nim"))
mImport(os.joinPath("compiler", "llstream.nim"))
mImport(os.joinPath("compiler", "idents.nim"))
mImport(os.joinPath("compiler", "options.nim"))
mImport(os.joinPath("compiler", "pathutils.nim"))
mImport(os.joinPath("compiler", "lineinfos.nim"))
# mImport(os.joinPath( "compiler" , "ast.nim"))

type ParseError = ref object of CatchableError
const DevNullDir = when defined(windows): "c:\\" else: "/dev"
const DevNullFile = when defined(windows): "nul" else: "null"

proc parsePNodeStr*(str: string, filePath: string, needConvert = false): tuple[ok: bool, error: ref Exception] =
# utf16 -> utf8
var mStr = str
if needConvert:
mStr = convert(str, "utf-8", "utf-16")
result.ok = true
let cache: IdentCache = newIdentCache()
let config: ConfigRef = newConfigRef()
var pars: Parser
pars.lex.errorHandler = proc(conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) =
if msg notin {hintLineTooLong}:
raise ParseError(msg: arg)

config.verbosity = 0
config.options.excl optHints
when defined(nimpretty):
config.outDir = toAbsoluteDir(DevNullDir)
config.outFile = RelativeFile(DevNullFile)
try:
openParser(
p = pars,
filename = AbsoluteFile(filePath),
inputStream = llStreamOpen(mStr),
cache = cache,
config = config
)
except Exception as e:
closeParser(pars)
result.error = e
result.ok = false

if result.ok == false:
return result

try:
discard parseAll(pars)
closeParser(pars)
except ParseError as e:
result.ok = false
result.error = e
except Exception as e:
result.error = e
result.ok = false

when isMainModule:
let file = currentSourcePath.parentDir.parentDir / "nimlsp.nim"
let r = parsePNodeStr(readFile(file), file, false)
echo r.ok
if not r.ok:
echo r.error.name
echo r.error.msg