Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes bug with textDocument/definition on LanguageClient-neovim #13

Closed
wants to merge 6 commits into from
Closed
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
40 changes: 23 additions & 17 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,28 @@ or if you want debug output
Supported Protocol features
=======

- [x] textDocument/didChange
- [x] textDocument/didClose
- [x] textDocument/didOpen
- [ ] textDocument/didSave

- [ ] textDocument/codeAction
- [x] textDocument/completion
- [x] textDocument/definition
- [ ] textDocument/documentHighlight
- [ ] textDocument/documentSymbol
- [ ] textDocument/executeCommand
- [ ] textDocument/format
- [x] textDocument/hover
- [ ] textDocument/rename
- [x] textDocument/references
- [ ] textDocument/signatureHelp
- [ ] workspace/symbol
====== ================================
Status LSP Command
====== ================================
☑ DONE textDocument/didChange
☑ DONE textDocument/didClose
☑ DONE textDocument/didOpen
☑ DONE textDocument/didSave
☐ TODO textDocument/codeAction
☑ DONE textDocument/completion
☑ DONE textDocument/definition
☐ TODO textDocument/documentHighlight
☐ TODO textDocument/documentSymbol
☐ TODO textDocument/executeCommand
☐ TODO textDocument/format
☑ DONE textDocument/hover
☐ TODO textDocument/rename
☑ DONE textDocument/references
☐ TODO textDocument/signatureHelp
☑ DONE textDocument/publishDiagnostics
☐ TODO workspace/symbol
====== ================================


Setting up `nimlsp`
=======
Expand All @@ -59,6 +64,7 @@ in syntax highlighting and some definitions. If you know how to disable the
overlapping features or achieve this in another way please update this section.

Now in order to set up LSP itself enter it's settings and add this:

.. code:: json

{
Expand Down
84 changes: 70 additions & 14 deletions src/nimlsp.nim
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,15 @@ template textDocumentRequest(message, kind, name, body) {.dirty.} =
filestash = storage / (hash(fileuri).toHex & ".nim" )
debugEcho "Got request for URI: ", fileuri, " copied to " & filestash
let
rawLine = name["position"]["line"].getInt
rawChar = name["position"]["character"].getInt
rawLine = max(name["position"]["line"].getInt, 0)
rawChar = max(name["position"]["character"].getInt, 0)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change means that rawLine and rawChar aren't really raw any longer so it should be renamed to something else.

body

template textDocumentNotification(message, kind, name, body) {.dirty.} =
if message["params"].isSome:
let name = message["params"].unsafeGet
whenValid(name, kind):
if name["textDocument"]["languageId"].getStr == "nim":
if not name["textDocument"].hasKey("languageId") or name["textDocument"]["languageId"].getStr == "nim":
let
fileuri = name["textDocument"]["uri"].getStr
filestash = storage / (hash(fileuri).toHex & ".nim" )
Expand All @@ -77,6 +77,9 @@ proc respond(request: RequestMessage, data: JsonNode) =
proc error(request: RequestMessage, errorCode: int, message: string, data: JsonNode) =
outs.sendJson create(ResponseMessage, "2.0", request["id"].getInt, none(JsonNode), some(create(ResponseError, errorCode, message, data))).JsonNode

proc notify(notification: string, data: JsonNode) =
outs.sendJson create(NotificationMessage, "2.0", notification, some(data)).JsonNode

type Certainty = enum
None,
Folder,
Expand Down Expand Up @@ -106,10 +109,14 @@ proc getProjectFile(file: string): string =
discard
path = dir

template getNimsuggest(fileuri: string): Nimsuggest =
projectFiles[openFiles[fileuri].projectFile].nimsuggest

while true:
try:
debugEcho "Trying to read frame"
let frame = ins.readFrame
debugEcho frame
debugEcho "Got frame:\n" & frame
let message = frame.parseJson
whenValid(message, RequestMessage):
debugEcho "Got valid Request message of type " & message["method"].getStr
Expand All @@ -130,7 +137,7 @@ while true:
change = some(TextDocumentSyncKind.Full.int),
willSave = some(false),
willSaveWaitUntil = some(false),
save = none(SaveOptions)
save = some(create(SaveOptions, some(true)))
)), # ?: TextDocumentSyncOptions or int or float
hoverProvider = some(true), # ?: bool
completionProvider = some(create(CompletionOptions,
Expand Down Expand Up @@ -162,7 +169,7 @@ while true:
)).JsonNode)
of "textDocument/completion":
message.textDocumentRequest(CompletionParams, compRequest):
let suggestions = projectFiles[openFiles[fileuri].projectFile].nimsuggest.sug(fileuri[7..^1], dirtyfile = filestash,
let suggestions = getNimsuggest(fileuri).sug(fileuri[7..^1], dirtyfile = filestash,
rawLine + 1,
openFiles[fileuri].fingerTable[rawLine].utf16to8(rawChar)
)
Expand Down Expand Up @@ -191,7 +198,7 @@ while true:
message.respond completionItems
of "textDocument/hover":
message.textDocumentRequest(TextDocumentPositionParams, hoverRequest):
let suggestions = projectFiles[openFiles[fileuri].projectFile].nimsuggest.def(fileuri[7..^1], dirtyfile = filestash,
let suggestions = getNimsuggest(fileuri).def(fileuri[7..^1], dirtyfile = filestash,
rawLine + 1,
openFiles[fileuri].fingerTable[rawLine].utf16to8(rawChar)
)
Expand Down Expand Up @@ -223,13 +230,13 @@ while true:
message.respond create(Hover, markedString, rangeopt).JsonNode
of "textDocument/references":
message.textDocumentRequest(ReferenceParams, referenceRequest):
let suggestions = projectFiles[openFiles[fileuri].projectFile].nimsuggest.use(fileuri[7..^1], dirtyfile = filestash,
let suggestions = getNimsuggest(fileuri).use(fileuri[7..^1], dirtyfile = filestash,
rawLine + 1,
openFiles[fileuri].fingerTable[rawLine].utf16to8(rawChar)
)
let declarations: seq[Suggestion] =
if referenceRequest["context"]["includeDeclaration"].getBool:
projectFiles[openFiles[fileuri].projectFile].nimsuggest.def(fileuri[7..^1], dirtyfile = filestash,
getNimsuggest(fileuri).def(fileuri[7..^1], dirtyfile = filestash,
rawLine + 1,
openFiles[fileuri].fingerTable[rawLine].utf16to8(rawChar)
)
Expand Down Expand Up @@ -260,11 +267,11 @@ while true:
message.respond response
of "textDocument/definition":
message.textDocumentRequest(TextDocumentPositionParams, definitionRequest):
let suggestions = projectFiles[openFiles[fileuri].projectFile].nimsuggest.def(fileuri[7..^1], dirtyfile = filestash,
let suggestions = getNimsuggest(fileuri).def(fileuri[7..^1], dirtyfile = filestash,
rawLine + 1,
openFiles[fileuri].fingerTable[rawLine].utf16to8(rawChar)
)
let declarations = projectFiles[openFiles[fileuri].projectFile].nimsuggest.def(fileuri[7..^1], dirtyfile = filestash,
let declarations = getNimsuggest(fileuri).def(fileuri[7..^1], dirtyfile = filestash,
rawLine + 1,
openFiles[fileuri].fingerTable[rawLine].utf16to8(rawChar)
)
Expand Down Expand Up @@ -296,7 +303,7 @@ while true:
# let
# rawLine = signRequest["position"]["line"].getInt
# rawChar = signRequest["position"]["character"].getInt
# suggestions = projectFiles[openFiles[fileuri].projectFile].nimsuggest.con(fileuri[7..^1], dirtyfile = filestash, rawLine + 1, rawChar)
# suggestions = getNimsuggest(fileuri).con(fileuri[7..^1], dirtyfile = filestash, rawLine + 1, rawChar)

else:
debugEcho "Unknown request"
Expand Down Expand Up @@ -350,11 +357,60 @@ while true:
projectFiles[projectFile].openFiles -= 1
if projectFiles[projectFile].openFiles == 0:
debugEcho "Trying to stop nimsuggest"
debugEcho "Stopped nimsuggest with code: " & $projectFiles[openFiles[fileuri].projectFile].nimsuggest.stopNimsuggest()
debugEcho "Stopped nimsuggest with code: " & $getNimsuggest(fileuri).stopNimsuggest()
openFiles.del(fileuri)
of "textDocument/didSave":
message.textDocumentNotification(DidSaveTextDocumentParams, textDoc):
if textDoc["text"].isSome:
let file = open(filestash, fmWrite)
debugEcho "Got document change 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()
debugEcho "fileuri: ", fileuri, ", project file: ", openFiles[fileuri].projectFile, ", dirtyfile: ", filestash
let diagnostics = getNimsuggest(fileuri).chk(fileuri[7..^1], dirtyfile = filestash)
debugEcho "Found suggestions: ",
diagnostics[0..(if diagnostics.len > 10: 10 else: diagnostics.high)],
(if diagnostics.len > 10: " and " & $(diagnostics.len-10) & " more" else: "")
if diagnostics.len == 0:
notify("textDocument/publishDiagnostics", create(PublishDiagnosticsParams,
fileuri,
@[]).JsonNode
)
else:
var response: seq[Diagnostic]
for diagnostic in diagnostics:
if diagnostic.line == 0:
continue
# Try to guess the size of the identifier
let
message = diagnostic.nimDocstring
endcolumn = max(diagnostic.column + message.rfind('\'') - message.find('\'') - 1, 0)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't really do much as it got max'ed with diagnostic.column later anyways. I think the problem here is that things with line or column <0 aren't visible in the document and therefore should be returned in some other way.

diagnosticLine = max(diagnostic.line-1, 0)
diagnosticColumn = max(diagnostic.column, endcolumn)
response.add create(Diagnostic,
create(Range,
create(Position, diagnosticLine, diagnosticColumn),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should always be diagnostic.column, and not endColumn. This logic was stolen from the VSCode plugin which tries to find the thing that was wrong and underline it. So the range here is supposed to go from where Nim finds the issue, and to the end of the thing that is in error.

create(Position, diagnosticLine, diagnosticColumn)
),
some(case diagnostic.qualifiedPath:
of "Error": DiagnosticSeverity.Error.int
of "Hint": DiagnosticSeverity.Hint.int
of "Warning": DiagnosticSeverity.Warning.int
else: DiagnosticSeverity.Error.int),
none(int),
some("nimsuggest chk"),
message,
none(seq[DiagnosticRelatedInformation])
)
notify("textDocument/publishDiagnostics", create(PublishDiagnosticsParams,
fileuri,
response).JsonNode
)
else:
debugEcho "Got unknown notification message"
continue
except IOError:
debugEcho "Got IOError: " & getCurrentExceptionMsg()
break
3 changes: 1 addition & 2 deletions src/nimlsppkg/base_protocol.nim
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ proc sendFrame*(s: Stream, frame: string) =
when defined(debugCommunication):
stderr.write(frame)
stderr.write("\n")
s.write "Content-Length: " & $frame.len & "\r\n\r\n" &
frame & "\r\n\r\n"
s.write "Content-Length: " & $frame.len & "\r\n\r\n" & frame
s.flush

proc sendJson*(s: Stream, data: JsonNode) =
Expand Down
2 changes: 1 addition & 1 deletion src/nimlsppkg/mappings.nim
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ func nimSymDetails(suggest: Suggestion): string =
else: suggest.signature

func nimDocstring(suggest: Suggestion): string =
suggest.docstring[1 .. ^2].replace("\\x0A","\n")
suggest.docstring.unescape()
11 changes: 10 additions & 1 deletion src/nimlsppkg/messages2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ jsonSchema:

Range:
start: Position
stop: Position
"end": Position

Location:
uri: string # Note that this is not checked
Expand Down Expand Up @@ -190,6 +190,15 @@ jsonSchema:
TextDocumentPositionParams:
textDocument: TextDocumentIdentifier
position: Position
buftype?: string # Added by LanguageCLient-neovim erroneously
character?: int # Added by LanguageCLient-neovim erroneously
filename?: string # Added by LanguageCLient-neovim erroneously
gotoCmd?: string or nil # Added by LanguageCLient-neovim erroneously
handle?: bool # Added by LanguageCLient-neovim erroneously
languageId?: string # Added by LanguageCLient-neovim erroneously
line?: int # Added by LanguageCLient-neovim erroneously
"method"?: string # Added by LanguageCLient-neovim erroneously
text?: string[] # Added by LanguageCLient-neovim erroneously

DocumentFilter:
language?: string
Expand Down
52 changes: 31 additions & 21 deletions src/nimlsppkg/nimsuggest.nim
Original file line number Diff line number Diff line change
Expand Up @@ -44,30 +44,40 @@ proc stopNimSuggest*(nimsuggest: NimSuggest): int =
nimsuggest.close()
else: result = -1

template createCommand(command: untyped) =
template createCommand(command, body: untyped): untyped =
if not nimsuggest.running:
raise newException(IOError, "nimsuggest process is not running")
body
nimsuggest.inputStream.flush()
var line = ""
while line.len != 0 or result.len == 0:
line = nimsuggest.outputStream.readLine
if line.startsWith(command.astToStr):
result.add line.parseSuggestion

template createFullCommand(command: untyped) {.dirty.} =
proc command*(nimsuggest: NimSuggest, file: string, dirtyfile = "",
line: int, col: int): seq[Suggestion] =
if not nimsuggest.running:
raise newException(IOError, "nimsuggest process is not running")
nimsuggest.inputStream.writeLine("$1 $2$3:$4:$5" %
[command.astToStr, file, (if dirtyfile.len > 0: ";$1" % [dirtyfile] else: ""), $line, $col])
nimsuggest.inputStream.flush()
var line = ""
while line.len != 0 or result.len == 0:
line = nimsuggest.outputStream.readLine
if line.startsWith(command.astToStr):
result.add line.parseSuggestion
createCommand(command):
nimsuggest.inputStream.writeLine("$1 $2$3:$4:$5" %
[command.astToStr, file, (if dirtyfile.len > 0: ";$1" % [dirtyfile] else: ""), $line, $col])

template createFileOnlyCommand(command: untyped) {.dirty.} =
proc command*(nimsuggest: NimSuggest, file: string, dirtyfile = ""): seq[Suggestion] =
createCommand(command):
nimsuggest.inputStream.writeLine("$1 $2$3" %
[command.astToStr, file, (if dirtyfile.len > 0: ";$1" % [dirtyfile] else: "")])

createCommand(sug)
createCommand(con)
createCommand(def)
createCommand(use)
createCommand(dus)
createCommand(chk)
createCommand(`mod`)
createCommand(highlight)
createCommand(outline)
createCommand(known)
createFullCommand(sug)
createFullCommand(con)
createFullCommand(def)
createFullCommand(use)
createFullCommand(dus)
createFileOnlyCommand(chk)
createFileOnlyCommand(`mod`)
createFileOnlyCommand(highlight)
createFileOnlyCommand(outline)
createFileOnlyCommand(known)


when isMainModule:
Expand Down