This PR adds preliminary support for inlay hints, as well as support for inlay hints that denote the auto-implicits of a function. Hovering over an auto-implicit displays its type and double-clicking the auto-implicit inserts it into the text document.  This PR is an extension of #3910. ### Known issues - In VS Code, when inserting an inlay hint, the inlay hint may linger for a couple of seconds before it disappears. This is a defect of the VS Code implementation of inlay hints and cannot adequately be resolved by us. - When making a change to the document, it may take a couple of seconds until the inlay hints respond to the change. This is deliberate and intended to reduce the amount of inlay hint flickering while typing. VS Code has a mechanism of its own for this, but in my experience it is still far too sensitive without additional latency. - Inserting an auto-implicit inlay hint that depends on an auto-implicit meta-variable causes a "failed to infer binder type" error. We can't display these meta-variables in the inlay hint because they don't have a user-displayable name, so it is not clear how to resolve this problem. - Inlay hints are currently always resolved eagerly, i.e. we do not support the `textDocument/inlayHint/resolve` request yet. Implementing support for this request is future work. ### Other changes - Axioms did not support auto-implicits due to an oversight in the implementation. This PR ensures they do. - In order to reduce the amount of inlay hint flickering when making a change to the document, the language server serves old inlay hints for parts of the file that have not been processed yet. This requires LSP request handler state (that sometimes must be invalidated on `textDocument/didChange`), so this PR introduces the notion of a stateful LSP request handler. - The partial response mechanism that we use for semantic tokens, where we simulate incremental LSP responses by periodically emitting refresh requests to the client, is generalized to accommodate both inlay hints and semantic tokens. Additionally, it is made more robust to ensure that we never emit refresh requests while a corresponding request is in flight, which causes VS Code to discard the respond of the request, as well as to ensure that we keep prompting VS Code to send another request if it spuriously decides not to respond to one of our refresh requests. - The synthetic identifier of an `example` had the full declaration as its (non-canonical synthetic) range. Since we need a reasonable position for the identifier to insert an inlay hint for the auto-implicits of an `example`, we change the (canonical synthetic) range of the synthetic identifier to that of the `example` keyword. - The semantic highlighting request handling is moved to a separate file. ### Breaking changes - The semantic highlighting request handler is not a pure request handler anymore, but a stateful one. Notably, this means that clients that extend the semantic highlighting of the Lean language server with the `chainLspRequestHandler` function must now use the `chainStatefulLspRequestHandler` function instead.
71 lines
2.6 KiB
Text
71 lines
2.6 KiB
Text
/-
|
|
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Authors: Leonardo de Moura
|
|
-/
|
|
prelude
|
|
import Lean.Log
|
|
import Lean.Parser.Command
|
|
import Lean.DeclarationRange
|
|
import Lean.Data.Lsp.Utf16
|
|
|
|
namespace Lean.Elab
|
|
|
|
def getDeclarationRange? [Monad m] [MonadFileMap m] (stx : Syntax) : m (Option DeclarationRange) := do
|
|
let some range := stx.getRange?
|
|
| return none
|
|
let fileMap ← getFileMap
|
|
return some <| .ofStringPositions fileMap range.start range.stop
|
|
|
|
/--
|
|
For most builtin declarations, the selection range is just its name, which is stored in the second position.
|
|
Example:
|
|
```
|
|
"def " >> declId >> optDeclSig >> declVal
|
|
```
|
|
If the declaration name is absent, we use the keyword instead.
|
|
This function converts the given `Syntax` into one that represents its "selection range".
|
|
-/
|
|
def getDeclarationSelectionRef (stx : Syntax) : Syntax :=
|
|
if stx.isOfKind ``Lean.Parser.Command.instance then
|
|
-- must skip `attrKind` and `optPrio` for `instance`
|
|
if !stx[3].isNone then
|
|
stx[3][0]
|
|
else
|
|
stx[1]
|
|
else
|
|
if stx[1][0].isIdent then
|
|
stx[1][0] -- `declId`
|
|
else if stx[1].isIdent then
|
|
stx[1] -- raw `ident`
|
|
else
|
|
stx[0]
|
|
|
|
/--
|
|
Derives and adds declaration ranges from given syntax trees. If `rangeStx` does not have a range,
|
|
nothing is added. If `selectionRangeStx` does not have a range, it is defaulted to that of
|
|
`rangeStx`.
|
|
-/
|
|
def addDeclarationRangesFromSyntax [Monad m] [MonadEnv m] [MonadFileMap m] (declName : Name)
|
|
(rangeStx : Syntax) (selectionRangeStx : Syntax := .missing) : m Unit := do
|
|
-- may fail on partial syntax, ignore in that case
|
|
let some range ← getDeclarationRange? rangeStx | return
|
|
let selectionRange ← (·.getD range) <$> getDeclarationRange? selectionRangeStx
|
|
Lean.addDeclarationRanges declName { range, selectionRange }
|
|
|
|
/--
|
|
Stores the `range` and `selectionRange` for `declName` where `modsStx` is the modifier part and
|
|
`cmdStx` the remaining part of the syntax tree for `declName`.
|
|
|
|
This method is for the builtin declarations only. User-defined commands should use
|
|
`Lean.Elab.addDeclarationRangesFromSyntax` or `Lean.addDeclarationRanges` to store this information
|
|
for their commands.
|
|
-/
|
|
def addDeclarationRangesForBuiltin [Monad m] [MonadEnv m] [MonadFileMap m] (declName : Name)
|
|
(modsStx : TSyntax ``Parser.Command.declModifiers) (declStx : Syntax) : m Unit := do
|
|
if declStx.getKind == ``Parser.Command.«example» then
|
|
return ()
|
|
let stx := mkNullNode #[modsStx, declStx]
|
|
addDeclarationRangesFromSyntax declName stx (getDeclarationSelectionRef declStx)
|
|
|
|
end Lean.Elab
|