Skip to content

Improve type hints behavior #1652

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

Merged
merged 6 commits into from
Aug 6, 2019
Merged

Conversation

SomeoneToIgnore
Copy link
Contributor

This PR fixed the following type hints issues:

editor!
);
} else {
await editor!.setDecorations(
Copy link
Member

Choose a reason for hiding this comment

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

What if the file changed outside of the control of vscode?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On my machine (macOS) vscode is fine with tracking external file changes in rust-analyzer project: it emits vscode.TextDocumentChangeEvent which we handle via the updateHints method.

I've also found some old issue that hints that vscode devs try to support the file watch as better as possible: microsoft/vscode#31600

If vscode fails to track the external file change at some point, we mess up code coloring and current inlay hints from master branch anyway, so my PR does not make it worse :)

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I think VS Code should work strictly in terms of in-memory representation of the file. If in memory and on-disk contents are different and an extension observes it, than that's the bug in VS Code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

so my PR does not make it worse

That's actually incorrect: master branch would have made a server request and updated the hints, while this version displays incorrect old hints from drawnDecorations.

Luckily, the new hints will be queried after the document is changed in the vscode, so it is easy to override.

I think this kind of situations should not be frequent in real life and a fair price for not querying the server for every .rs file opened in the editor (as we do it now)

Copy link
Member

Choose a reason for hiding this comment

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

I think this kind of situations should not be frequent in real life and a fair price for not querying the server for every .rs file opened in the editor (as we do it now)

We actually should query the server for each of .rs files: change in one file can affect type-hints in another file. If we want to save computation here, we should add an ability to filter type-hints by the viewport (so that we query only visible things).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, how could I miss it?

What if we still keep the cache and clean it when the next edit is made?
Decorations disappear every time the editor is closed, but we might reuse the ones already queried if no changes are made?

editor!
);
} else {
await editor!.setDecorations(
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I think VS Code should work strictly in terms of in-memory representation of the file. If in memory and on-disk contents are different and an extension observes it, than that's the bug in VS Code.

@@ -21,32 +21,44 @@ const typeHintDecorationType = vscode.window.createTextEditorDecorationType({

export class HintsUpdater {
private displayHints = true;
private drawnDecorations = new Map<string, vscode.DecorationOptions[]>();
Copy link
Member

Choose a reason for hiding this comment

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

So, we never clear this map unless we turn the hints of? This is (a small) memory leak than. I don't think by itself it is a huge problem, but it hints that the general lifetime cycle is not ideal here. I wonder if we can do better?

I think we can do one of the two things:

  • VsCode seems to use subscriptions/disposable pattern to clean up various observers automatically. I wonder if there's a disposable array, associated with the vscode.TextEditor, which we can use to react on editor opend/closed.
  • alternatively, a pure JS solution would be to use a WeakMap, keyed by an actual editor here. Not sure if WeakMap is available in VsCode

Copy link
Member

Choose a reason for hiding this comment

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

Also, I've seen this problem quite a few times: if you have an observer, that observes an object X, it's usually a hard problem to decide when you should stop observing (because there's a reference cycle between observer and an observee). I wonder if there's a blog post about this somewhere...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice catch, had forgotten to tell my concerns about it.

There's a onDidCloseTextDocument that is triggered whenever a tab is closed, but that does not help in the current situation much: the whole idea is to quickly redraw the decorations when it's reopened, so we cannot remove them from the drawnDecorations yet.

WeakMap sounds like a nice idea, I will try it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looks like both Map and WeakMap compare vscode.Uri instances by reference and those change every time we close and open the editor tab for the file.
The uri string stays the same, but WeakMap does not accept string types as keys, so I had to use the Map again.

But now, since we clear the map every time an edit is made, the leak gets even smaller, doesn't it?

@matklad
Copy link
Member

matklad commented Aug 5, 2019

Hm, I am still not quite satisfied with the way we manage state here. I wonder if we can avoid it? Would the following work?:

  • we connect to window.onDidChangeVisibleTextEditors event and issue update on the event
  • we connect to workspace.onDidChangeTextDocument event and issue update on any .rs file event
  • In the update, we walk through all window.activeTextEditor window.visibleTextEditors editors which show .rs documents, issue a server request for them (ideally, in parallel), and set decorations
  • On disable event, we walk through all editors and set decorations to an empty set

It seems like we don't need to store any state at all with these approach?

EDIT: I think we even don't need a separate disable method, update can just look at the current value of setting.

@SomeoneToIgnore
Copy link
Contributor Author

Hm, I am still not quite satisfied with the way we manage state here.

Would be great to know what concerns you, just out of curiosity :)

  • we connect to window.onDidChangeVisibleTextEditors event and issue update on the event
  • In the update, we walk through all window.activeTextEditor window.visibleTextEditors editors which show .rs documents, issue a server request for them (ideally, in parallel), and set decorations

If I understood you correctly, you propose to issue a server request for every visible text editor whenever the visible editors change (some get closed or open).

I agree with the overall idea, but I wanted to optimize one corner case: currently vscode "forgets" the previous decorations that were set to the editor after some other editor is opened.
This means that we need to set the decorations every time a file editor is shown again, even if we close and open the same file.

If we follow your proposal (or the way it's done in master), we will be querying the server to set those decorations, in the current PR we reuse the already queried decorations if there were no changes to the *.rs files in the project.

My reasoning for caching those results was the idea that it's completely useless to make another server request in such cases: since the project did not change, we know that the same file reopened needs the same hints.
Since the hints we store are cleared on any document edit, I thought that it's fine if we don't care about the cache size that much.

@SomeoneToIgnore
Copy link
Contributor Author

Nice idea with the window.visibleTextEditors, currently only an active editor is updated, I will adjust this.

@matklad
Copy link
Member

matklad commented Aug 5, 2019

Would be great to know what concerns you, just out of curiosity :)

Mutable state :) Specifically, when observer tries to maintain current state by observing changes, it's very easy to arrive at an inconsistent state.

If I understood you correctly, you propose to issue a server request for every visible text editor whenever the visible editors change (some get closed or open).

Yeah, exactly.

This means that we need to set the decorations every time a file editor is shown again, even if we close and open the same file.

I think it's fine, this stuff is pretty fast, and the situation is pretty rare. If we do optimizations here, I think we should focus more on debouncing sevral changes (ie, don't send requests faster than once in 100ms). For that to work, we'll need a per-editor state which we could store in a weak map, keyed by the editor itself.

@SomeoneToIgnore
Copy link
Contributor Author

Thanks for the explanations.
Had applied the changes you've suggested and let's consider the optimizations in some other PR.

@matklad
Copy link
Member

matklad commented Aug 6, 2019

bors r+

Thanks!

bors bot added a commit that referenced this pull request Aug 6, 2019
1652: Improve type hints behavior r=matklad a=SomeoneToIgnore

This PR fixed the following type hints issues:

* Restructures the `InlayKind` enum contents based on the discussion here: #1606 (comment)
* Races described in #1639 
* Caches the latest decorations received for each file to show them the next time the file is opened (instead of a new server request)

Co-authored-by: Kirill Bulatov <[email protected]>
@bors
Copy link
Contributor

bors bot commented Aug 6, 2019

Build succeeded

@bors bors bot merged commit c5598d9 into rust-lang:master Aug 6, 2019
@SomeoneToIgnore SomeoneToIgnore deleted the avoid-hints-races branch August 6, 2019 17:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants