-
Notifications
You must be signed in to change notification settings - Fork 1.8k
vscode: redesign inlay hints to be capable of handling multiple editors for one source file #3378
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
vscode: redesign inlay hints to be capable of handling multiple editors for one source file #3378
Conversation
editors/code/src/inlay_hints.ts
Outdated
if (contentChanges.length === 0) return; | ||
if (!isRustTextDocument(document)) return; | ||
|
||
hintsUpdater.refreshRustDocument(document); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should refresh all visible editors: change to one file might affect hints in another file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! 995ad75
editors/code/src/inlay_hints.ts
Outdated
// FIXME: ctx.config may have not been refreshed at this point of time, i.e. | ||
// it's on onDidChangeConfiguration() handler may've not executed yet | ||
// (order of invokation is unspecified) | ||
// To fix this we should expose an event emitter from our `Config` itself. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess a better approach is to just make ctx.config
always reflect the most recent config? Ie, we should rid of config.onDidChange
handler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we will get rid of cache
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though, we won't get rid of config.onDidChange
, since it triggerses (option requires reload
) message and updates logging setEnabled()
. Switching to event emitter for config is actually very simple, vscode exposes vscode.EventEmitter
for this.
editors/code/src/inlay_hints.ts
Outdated
this.setParameterDecorations(editor, newParameterDecorations); | ||
/** | ||
* This class encapsulates a map of file uris to respective inlay hints | ||
* request cancellation token source (cts) and an array of editors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haven't read into this yet, but I think it should be possible to solve this without storing editors. I would think that the current queryHints
logic is enough here, it should handle per-document in-flight requests correctly (modulo bugs).
It seems it should be enought to tweak refresh
function logic such that it first groups editors by the document they are displaying, and then refreshes each group, without maintaing any additional state or datastructures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The state keeps track of editors with already rendered inlay hints (some cache to prevent requests when opening duplicate editors) and prevents interaction with disposed editors (see
this line) (vscode issues a warning for this otherwise).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And it also prevents requests for inlay hints when the user browses unrelated files (i.e. #3319), windows (output/console/preview windows etc.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would the following work as a way to get rid of the extra state here?
class RustSourceFile {
constructor(
public readonly document: vscode.TextDocument,
public inlaysRequest: null | vscode.CancellationTokenSource = null,
public decorations: null | {
type: vscode.DecorationOptions[];
param: vscode.DecorationOptions[];
} = null
) { }
get editors(): vscode.TextEditor[] {
return vscode.window.visibleTextEditors.filter((it) => it.document.uri.toString == this.document.uri.toString)
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will remove the extra state, though we will looze the info about which editors have decorations applied and which don't. So removing the state like this we will still rerender the decorations while the user is browsing editors/outputs/terminal widnows, but we will at least have decorations
cache in order not to issue requests for inlay hints to the lsp server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can think of dirty hack where we add boolean properties to editors
to put the state in them instead of our custom map.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can think of dirty hack where we add boolean property areDecorationsApplied
to editors
to put the state in them instead of our custom map.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or better use a symbol
. This will do work and maybe would be more efficient in terms of performance. However, the stability of a reference to the editor (i.e. pointer to it) is not documented by vscode, though in practice we can rely on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the following example will make this more clear:
const IS_DECORATED = Symbol("inlay hints decorations");
get editorsToRender() {
return vscode.window.visibleTextEditrs.fitler(
suspect => !suspect[IS_DECORATED] && <uris equal>...
);
}
rerender(editor) {
editor.setDecorations(...);
editor[IS_DECORATED] = true;
}
Why is **just** applying the decorations bad?
…On Monday, 2 March 2020, Veetaha ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In editors/code/src/inlay_hints.ts
<#3378 (comment)>
:
> - this.setTypeDecorations(editor, newTypeDecorations);
-
- const newParameterDecorations = newHints
- .filter(hint => hint.kind === ra.InlayKind.ParameterHint)
- .map(hint => ({
- range: this.ctx.client.protocol2CodeConverter.asRange(hint.range),
- renderOptions: {
- before: {
- contentText: `${hint.label}: `,
- },
- },
- }));
- this.setParameterDecorations(editor, newParameterDecorations);
+/**
+ * This class encapsulates a map of file uris to respective inlay hints
+ * request cancellation token source (cts) and an array of editors.
I think the following example will make this more clear:
const DEC = Symbol("decorations");
get editorToRender() {
return vscode.window.visibleTextEditrs.fitler(suspect => suspect[DEC] && <uris equal>...)
}
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#3378>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AANB3M23ZJTGJR2GLNYEYW3RFQBHPANCNFSM4K64VUWA>
.
|
It's "bad" in terms of rerendering inlays decorations when not modifying the code, but it's "good" in terms of not storing the state and, anyway, inlay hints are updated on each character typed, so rerendering them on browsing unrelated editors would be shadowed by this fact. |
I would expect setting decorations to be a very cheap operation, especially
in comparisons to IPC we do to get this infor in the first place, and to
actually type inference we do to compute it
…On Mon, 2 Mar 2020 at 21:07, Veetaha ***@***.***> wrote:
It's *"bad"* in terms of rerendering inlays decorations when not
modifying the code, but it's *"good"* in terms of not storing the state
and, anyway, inlay hints are updated on each character typed, so
rerendering them on browsing unrelated editors would be shadowed by this
fact.
I wish vscode guys took over the implementation themselves for us here, so
that they could access their private APIs and this state could already be
accessible by them...
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#3378>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AANB3M63BBRDK34SUSKX45DRFQGZFANCNFSM4K64VUWA>
.
|
Don't forget that the extension communicates with the editor via IPC too, anyway. Let me try to simplify this with a boolean-per-editor and if you don't like it we will remove the state at all... |
I pretty strongly feel that any state management is premature optimization, and a non-insignificant amount of complexity. |
3388: Remove inlay hint in diff views r=matklad a=vbfox If the left side of a diff view that contain the old version of the file apply inlays they are misplaced and produce a weird display:  After the change:  The detection is done by blacklisting the url schemes used by git and subversion scm extensions, whitelisting `file` is also possible but neither is perfect as VSCode now support both pluggable scm extensions and pluggable remote filesystems. But I suspect that the list of scm extensions is more easily manageable. **Note**: I can rebase on #3378 if needed as it touches the same lines of code Co-authored-by: Julien Roncaglia <julien@roncaglia.fr>
@kjeremy, haven't looked into it. I am not sure I caught your intent, do you mean we should make changes to the LSP server side first before merging this PR? |
@Veetaha what I mean is that the code lense feature is similar to our inlay hints feature and already supported by vscode. We should see how the client side typescript solves the cases that we also need to solve. caveat: I'm not terribly familiar with the ts side of inlay hints so could be completely off the mark :) |
995ad75
to
ef52fd5
Compare
r=me |
bors d+ |
✌️ Veetaha can now approve this pull request. To approve and merge a pull request, simply reply with |
@kjeremy I've looked at CodeLens implementation and it uses codelens LRU cache along with registering the |
Also, @matklad, please note that the first request seems to get an invalid (maybe partial) inlay hits response. Is this the expected behaviour? (I also find myself re-setting up those debug logs in the following demo a lot, but I guess we should remove them anyway) |
I'll merge this because partial inlay hints results at bootstrap are definitely another issue that is server-side. |
Build succeeded |
@Veetaha I really appreciate you looking at how codelens works and why we can't go that route. I wonder if we can file an issue to open up the relevant API. It may be useful to others and when we upstream this. Would that have made this work easier? |
It's actually hard to tell, there are some more topics I'd need to go through like viewport optimization and the universal shape of inlays data. However, inlays do resemble codelens to some degree (except there is no need to actually resolve an inlay hint), so some of their design may be used for them. I don't fill confident enough in LSP, but I might try to think of an api proposal once I get to it! |
Fixes: #3008 (inlay corruption with multiple editors for one file).
Fixes: #3319 (unnecessary requests for inlay hints when switching unrelated source files or output/log/console windows)
Also, I don't know how, but the problem described in #3057 doesn't appear for me anymore (maybe it was some fix on the server-side, idk), the inlay hints are displaying right away. Though the last time I checked this it was caused by the server returning an empty array of hints and responding with a very big latency, I am not sure that this redesign actually fixed #3057....
We didn't handle the case when one rust source file is open in multiple editors in vscode (e.g. by manually adding another editor for the same file or by opening an inline git diff view or just any kind of preview within the same file).
The git diff preview is actually quite special because it causes memory leaks in vscode (microsoft/vscode#91782). It is not removed from
visibleEditors
once it is closed. However, this bug doesn't affect the inlay hints anymore, because we don't issue a request and set inlay hints for each editor in isolation. Editors are grouped by their respective files and we issue requests only for files and then update all duplicate editors using the results (so we just update the decorations for already closed git diff preview read-only editors).Also, note on a hack I had to use.
vscode.TextEdtior
identity is not actually public, itsid
field is not exposed to us. I created a dedicated upstream issue for this (microsoft/vscode#91788).Regarding #3319: the newly designed hints client doesn't issue requests for type hints when switching the visible editors if it has them already cached (though it does rerender things anyway, but this could be optimized in future if so wanted).
Before
After