You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Use completion.existingImports to filter completion suggestions #1776
@scheglov would you mind scanning over this code to see if my understanding of how this works is correct? I'll optimise the performance, but I want to ensure the logic is correct.
I tested it by having the same class exported in multiple files, but also multiple classes with the same name in each. With this code, it does what I'd expect (the duplicated class shows up 3 times in completion, and two of them will generate prefixes, and the re-exported class appears only once), so I think it's good - it just seems much simpler than I was expecting :-)
It caught some dupes in the SDK too!
Item DannyClassOnlyOne from package:danny/real_def.dart is already imported by package:danny/real_def.dart so filtering out the suggestion for package:danny/proxy2.dart
Item DannyClassOnlyOne from package:danny/real_def.dart is already imported by package:danny/real_def.dart so filtering out the suggestion for package:danny/proxy1.dart
Item Future from dart:async is already imported by dart:core so filtering out the suggestion for dart:async
Item Stream from dart:async is already imported by dart:core so filtering out the suggestion for dart:async
I didn't really add much context to the code... the .filter at the top is filtering the array of suggestions from an included suggestion set. The existingImports variable is the existing imports for the libraryFile in the response.
That said, the comments may explain it better than code - I just want to be sure I haven't misunderstood anything (like I say, it seems to work in my tests - but it's harder to test against Flutter code right now because of the SDK version).
You check that the library that declares the element of the suggestion is already imported.
But in Flutter material.dart re-exports Widget from src/widgets/framework.dart.
And what we want is to avoid suggesting Widget from package:flutter/widgets.dart when there is already package:flutter/material.dart import. Because Widget is already available from an imported library. The uses has already made the decision what to import.
Thanks! Though I'm not sure I understand. Do you mean line 268 is wrong? I believe this is comparing the declared library URI on both sides (so it's resolving the item in the suggestion to its declaration, and also the item that's available in the import to its declaration). This way we only filter out things that are exact dupes (eg. the same declaration).
So in your example, I believe I would get flutter/widgets.dart on both sides (because the re-export for material.dart would return its declaring library as flutter/widgets.dart)? If the user hadn't imported anything, we would still show all of them.
If the user has not imported anything yet, then we show for an element all possible suggestion sets (URIs to import).
If there is an existing import such as package:flutter/material.dart, it provides element package:flutter/src/widgets/framework.dart::Widget, so we don't suggest this element from package:flutter/widgets.dart.
Direct declaration does not take preference when there is already an import that provides the same element.
Look at this as this: each library exports / provides a set of elements. We have already imported libraries, which provide sets of elements. We iterate over available suggestion sets and their suggestions. Each suggestion is based on an element (declaringLibraryUri and label). If this element is already provided by an imported library, we include this suggestion only if the suggestion set is an already imported library.
Sorry, I still don't understand which bit is incorrect :(
I tested it with Flutter master, and here's what I see when there are no imports.. EdgeInsets shows up 5 times from the different libraries that export it:
If I import it from widgets then it only shows up once (with no auto-import tag):
This is because when we found it inside the suggestion sets for other libraries, we noticed that it resolves to the same declaration as the the one in an existing import, so we skipped it. The log looked like this:
Item EdgeInsets from package:flutter/src/painting/edge_insets.dart is already imported by package:flutter/widgets.dart so filtering out the suggestion for package:flutter/material.dart
Item EdgeInsets from package:flutter/src/painting/edge_insets.dart is already imported by package:flutter/widgets.dart so filtering out the suggestion for package:flutter/painting.dart
Item EdgeInsets from package:flutter/src/painting/edge_insets.dart is already imported by package:flutter/widgets.dart so filtering out the suggestion for package:flutter/cupertino.dart
Item EdgeInsets from package:flutter/src/painting/edge_insets.dart is already imported by package:flutter/widgets.dart so filtering out the suggestion for package:flutter/rendering.dart
Note the first URI in my log is the declared URI, the one from the suggestion set is at the end of the line (I realise in my first comment this was scrolled off the edge, so this may have been misleading!).
If this seems wrong to you, can you give an example of a symbol and where I should import it from that you think I'll get the wrong result, and I can do some testing of it. Thanks!
I refactored the code a little so that it now builds into a map so the lookups are faster, which may make it easier to understand (#1789). However, there's no performance benefit yet as existingImports is sent before ever completion request (I think this was discussed on the other PR as a possible optimisation).
Activity
DanTup commentedon Jun 10, 2019
@scheglov would you mind scanning over this code to see if my understanding of how this works is correct? I'll optimise the performance, but I want to ensure the logic is correct.
I tested it by having the same class exported in multiple files, but also multiple classes with the same name in each. With this code, it does what I'd expect (the duplicated class shows up 3 times in completion, and two of them will generate prefixes, and the re-exported class appears only once), so I think it's good - it just seems much simpler than I was expecting :-)
It caught some dupes in the SDK too!
DanTup commentedon Jun 10, 2019
I didn't really add much context to the code... the
.filterat the top is filtering the array of suggestions from an included suggestion set. TheexistingImportsvariable is the existing imports for thelibraryFilein the response.That said, the comments may explain it better than code - I just want to be sure I haven't misunderstood anything (like I say, it seems to work in my tests - but it's harder to test against Flutter code right now because of the SDK version).
Skip completions for symbols that are already imported via another li…
scheglov commentedon Jun 10, 2019
Not quite.
You check that the library that declares the element of the suggestion is already imported.
But in Flutter
material.dartre-exportsWidgetfromsrc/widgets/framework.dart.And what we want is to avoid suggesting
Widgetfrompackage:flutter/widgets.dartwhen there is alreadypackage:flutter/material.dartimport. BecauseWidgetis already available from an imported library. The uses has already made the decision what to import.DanTup commentedon Jun 10, 2019
Thanks! Though I'm not sure I understand. Do you mean line 268 is wrong? I believe this is comparing the declared library URI on both sides (so it's resolving the item in the suggestion to its declaration, and also the item that's available in the import to its declaration). This way we only filter out things that are exact dupes (eg. the same declaration).
So in your example, I believe I would get
flutter/widgets.darton both sides (because the re-export formaterial.dartwould return its declaring library asflutter/widgets.dart)? If the user hadn't imported anything, we would still show all of them.scheglov commentedon Jun 10, 2019
If the user has not imported anything yet, then we show for an element all possible suggestion sets (URIs to import).
If there is an existing import such as
package:flutter/material.dart, it provides elementpackage:flutter/src/widgets/framework.dart::Widget, so we don't suggest this element frompackage:flutter/widgets.dart.Direct declaration does not take preference when there is already an import that provides the same element.
Look at this as this: each library exports / provides a set of elements. We have already imported libraries, which provide sets of elements. We iterate over available suggestion sets and their suggestions. Each suggestion is based on an element (
declaringLibraryUriandlabel). If this element is already provided by an imported library, we include this suggestion only if the suggestion set is an already imported library.DanTup commentedon Jun 11, 2019
Sorry, I still don't understand which bit is incorrect :(
I tested it with Flutter master, and here's what I see when there are no imports..
EdgeInsetsshows up 5 times from the different libraries that export it:If I import it from
widgetsthen it only shows up once (with no auto-import tag):This is because when we found it inside the suggestion sets for other libraries, we noticed that it resolves to the same declaration as the the one in an existing import, so we skipped it. The log looked like this:
Note the first URI in my log is the declared URI, the one from the suggestion set is at the end of the line (I realise in my first comment this was scrolled off the edge, so this may have been misleading!).
If this seems wrong to you, can you give an example of a symbol and where I should import it from that you think I'll get the wrong result, and I can do some testing of it. Thanks!
Pre-compute the existing imports rather than do on every completion r…
Skip completions for symbols that are already imported via another li…
Pre-compute the existing imports rather than do on every completion r…
Skip completions for symbols that are already imported via another li…
Pre-compute the existing imports rather than do on every completion r…
Skip completions for symbols that are already imported via another li…
Pre-compute the existing imports rather than do on every completion r…
DanTup commentedon Jun 11, 2019
I refactored the code a little so that it now builds into a map so the lookups are faster, which may make it easier to understand (#1789). However, there's no performance benefit yet as
existingImportsis sent before ever completion request (I think this was discussed on the other PR as a possible optimisation).scheglov commentedon Jun 11, 2019
The code in the PR looks good to me.
DanTup commentedon Jun 11, 2019
Great, thanks for looking!
Skip completions for symbols that are already imported via another li…
Pre-compute the existing imports rather than do on every completion r…