-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Use completion.existingImports to filter completion suggestions #665
Use completion.existingImports to filter completion suggestions #665
Conversation
} | ||
|
||
String name = strings.get(names.get(index)); | ||
uriToNames.get(uri).add(name); |
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 it be more efficient to use the usual pattern "get, if null then put" here?
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.
Will do, thanks!
List<Integer> uris = new ArrayList<>(); | ||
elements.getAsJsonArray("uris").forEach(item -> uris.add(item.getAsInt())); | ||
List<Integer> names = new ArrayList<>(); | ||
elements.getAsJsonArray("names").forEach(item -> names.add(item.getAsInt())); |
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 could just as well decode directly into String(s) and avoid Integer
wrappers.
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.
Then we would need to use hashmaps instead of lists for constant lookup though, right?
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.
No, but yes, in some sense :-)
List<String> uris
maps int
index to String
value.
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.
But then if we get a String from the uris or names lookup we have to use it as an index into strings?
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.
Okay, discussed offline and fixed :)
getListener().computedExistingImports(file, uriToNames); | ||
} | ||
|
||
private Map<String, Set<String>> buildUriNamesMap(JsonObject existingImports) { |
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 this is wrong.
This method should return something like Map<importedLibraryUri, Map<declaringLibraryUri, declaredName>>
. What I see as a single Map<declaringLibraryUri, declaredName>
, so we know what is imported into the target library, but not how. This is important for re-exports. For example both "material.dart" and "cupertino.dart" re-export Widget
, and we need to know which suggestion set to use for Widget
.
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.
Thanks @scheglov! I got confused and missed that one library can re-export multiple names.
Please add a test to the PR. |
Dart/src/com/jetbrains/lang/dart/ide/completion/DartServerCompletionContributor.java
Outdated
Show resolved
Hide resolved
Map<String, Set<String>> importedLibrary = entry.getValue(); | ||
// Checks whether any of the libraries exported by this import declares the same name as this suggestion. | ||
if (importedLibrary.values().stream().anyMatch(names -> names.contains(suggestion.getLabel()))) { | ||
declaringLibraries.add(importedLibraryUri); |
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.
declaringLibraries
-> importedLibraries
like "libraries which import provide the suggestion".
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.
String importedLibraryUri = entry.getKey(); | ||
Map<String, Set<String>> importedLibrary = entry.getValue(); | ||
// Checks whether any of the libraries exported by this import declares the same name as this suggestion. | ||
if (importedLibrary.values().stream().anyMatch(names -> names.contains(suggestion.getLabel()))) { |
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 is not precise.
We need to use the URI and the name of the suggestion, something like importedLibrary[suggestion.declaringLibraryUri][suggestion.name]
. However it seems that we don't have URI in AvailableSuggestion
. So, we might have to extend AvailableSuggestionSet
to provide URIs.
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.
Good catch! We do have uri on AvailableSuggestionSet (not on AvailableSuggestion) so I used that.
53f014b
to
95f9049
Compare
95f9049
to
6d8ba07
Compare
@alexander-doroshko I wrote a unit test but wasn't able to run it since my intellij-community is broken after updating. If you can check it as part of merging, I would appreciate the help! |
What kind of problems do you have? First of all, make sure that you use git pull/commit/push right from the IDE, not from the command line. IDE integration works transparently with multiple repositories and also, much fewer chances to commit red code or not-related files or to forget to commit related files, etc. There are no reasons not to use IntelliJ's Git integration. As for the test, I guess you need to use |
@scheglov thank you for the review! Does the PR LGTY in its current state? |
String importedLibraryUri = entry.getKey(); | ||
Map<String, Set<String>> importedLibrary = entry.getValue(); | ||
// Checks whether any of the libraries exported by this import declares the same name as this suggestion. | ||
if (importedLibrary.entrySet().stream().anyMatch( |
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 this is very inefficient.
We don't use the fact that this is a map, and just iterate over entries, checking the key.
We should rewrite it to something like importedElements[suggestion.uri].contains(suggestion.label)
.
This is especially scary because we do this for every suggestion.
And I need to add uri
to AvailableSuggestion
.
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.
Whoops, thanks @scheglov! Updated.
// Checks whether any of the libraries exported by this import declares the same name as this suggestion. | ||
if (importedLibrary.entrySet().stream().anyMatch( | ||
importEntry -> doesImportSuggestion(importEntry, suggestionSet.getUri(), suggestion.getLabel()))) { | ||
// TODO: Use individual suggestion uris instead of suggestion set uri. |
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.
DAS does provide AvailableSuggestion.declaringLibraryUri
with https://dart-review.googlesource.com/c/sdk/+/104808
Is there something that prevents us from using it in this PR?
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.
Nope, I just pushed this before you landed that change :). Updating now!
@lambdabaa, compilation broken. And please check that the test works. |
Sorry, fixed the compilation issue! |
@lambdabaa I'm getting NPEs with SDK 2.3 or older. We still support SDK 1.12 as a minimal version. |
@alexander-doroshko Thanks for the heads up! I went ahead and updated the logic that reads AvailableSuggestion and CompletionSuggestion to not assume the existence of the new declaringLibraryUri and libraryFile fields. |
Something strange is happening in the test that I am not fully understanding. This works outside of the context of a test, but the ExistingImports notification is different in test mode (missing the symbols re-exported from ExistingImportLibrary.dart). Any ideas about how to fix it @alexander-doroshko? |
Probably because there's only one file in the actual test project (see
|
Ah that was it, thanks @alexander-doroshko! |
Dart/src/com/jetbrains/lang/dart/ide/completion/DartServerCompletionContributor.java
Outdated
Show resolved
Hide resolved
I tested the feature with today's nightly SDK. Completing in an empty file: I noticed that we've lost some classes from BTW, it sounds like a good scenario for a one more test! |
Dart/src/com/jetbrains/lang/dart/analyzer/DartAnalysisServerService.java
Outdated
Show resolved
Hide resolved
Do you get the same? |
Dart/testSrc/com/jetbrains/dart/analysisServer/DartServerCompletionTest.java
Show resolved
Hide resolved
Fixed! |
Yes that's correct, done! |
@alexander-doroshko Gentle ping :) |
Sorry for delay, I had a few days off. The PR is merged now, thanks! The commit will appear here shortly. |
@lambdabaa Is there any option to disable this feature in the IDE? |
@maRci002, there's no option to disable the feature. I think we'd rather fix strange completion ordering than add an option. Can you share the details of the problem? |
@alexander-doroshko thanks for quick answer. My config:
My problem is that when a constructor (let's say Here's a simplified example of this issue, the padding argument shoud implemet EdgeInsetsGeometry abstract class but autocomplete shows me random methods / keywords...: Autocomplete should know there are some classes which implement So the expected behaviour should be like this (this is old behaviour when Moreover the problem exists in simple situations for example argument should be a So to sum up, I think this feature significantly slows down development time because I don't want to manually check type hierarchy every single time. 3 months ago I made a VS Code issue and @DanTup figured out the problem occours when I think I should have created a new Issue on youtrack but there wasn't option to create new account, and I am using Android Studio, but I think the problem occours in every Intellij based IDE which using the newest Dart plugin. (confirm?) I really appreciate that you try to satisfy the community's request, so keep it up 💪. I also hope you can fix this problem or at least give an option to disable this feature while working on fix. |
Possibly we should move Dart-Code/Dart-Code#1742 to the SDK repo. I do think the ranking is worse with this enabled, though I think it's a known trade-off (see Dart-Code/Dart-Code#1742 (comment)). That said, VS Code ignores our sorting as soon as the user starts typing anyway (microsoft/vscode#79516), so it's possible that even if fixed in the server it won't be perfect in VS Code because the chances of there being other items VS Code thinks are "more relevant" are increased with the unimported items 😞 |
I manually downgraded Dart plugin to 191.8026.36, this is the last update which doesn't include this |
There definitely is. You can log in using JetBrains account, as well as GitHub, Google account and more. But at the moment there's no need for YouTrack issue, I think we first need to continue the discussion with the Analysis Server team. |
@scheglov, are there new ideas about improving type-aware completion sorting? Is there anything we are not doing right on the Dart plugin end? |
There are several problems here.
|
[email protected] Bug: JetBrains/intellij-plugins#665 (comment) Change-Id: I347822b84311ed2b94171231597aeceebd900015 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/114563 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
I would like to highlight
this is not true if I write |
[email protected] Bug: JetBrains/intellij-plugins#665 (comment) Change-Id: I254639a1582ca5691e96e15612bd2078c65e0b41 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/114969 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
Thanks @scheglov your work so far, today I was able to test this feature on flutter dev channel.
If I create a new empty dart file like you did a However I created a new project When I trigger autocomplete in a local method then global variables/classes relevance is still boosted as well as local variables ( |
This CL processes
completion.existingImports
notifications and uses them to build and maintain a map from file onto import URI onto names exported from the import. Then, at completion time,CompletionSuggestion.libraryFile
is used to lookup a map of all imported libraries and their declared names. Then only libraries which are already imported are allowed to suggest it if some library has already imported the name. This prevents the scenario where I've already imported 'package:foo/foo.dart' which providesFoo
and not-yet-imported 'package:bar/bar' suggestsFoo
causing you to import a new package unnecessarily.Related to flutter/flutter-intellij#3415.
/cc @alexander-doroshko @scheglov @jwren @devoncarew