Handling defaults provided by IAsyncCompletionDefaultsSource #57685
Handling defaults provided by IAsyncCompletionDefaultsSource #57685genlu merged 23 commits intodotnet:mainfrom
Conversation
src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs
Outdated
Show resolved
Hide resolved
|
Sorry, not used to github diffs, I'm too used to code flow and didn't notice the dates associated with the commits.
Changes look good now that I'm looking at the right set of commits.
|
| private readonly RecentItemsManager _recentItemsManager; | ||
| private readonly IGlobalOptionService _globalOptions; | ||
|
|
||
| public const string AggressiveDefaultsMatchingOptionName = "AggressiveDefaultsMatchingOption"; |
There was a problem hiding this comment.
what does 'aggressive defaults' mean?
There was a problem hiding this comment.
From @dpugh:
With aggressive matching turned on, the default was use as long as what the user typed in the applicable to span was a prefix of the item
I believe the purpose of this option is to provide a way to experiment with different selection logic.
There was a problem hiding this comment.
the default was use as long as what the user typed in the applicable to span was a prefix of the item
i still don't know what this means. can we have a comment explaining this somewhere (ideally with examples).
|
Can you update the OP of thei PR to indicate what user perceivable change this has to the completion experience? |
|
@CyrusNajmabadi Added some description of the change. You can also find more examples in the tests. |
|
@dotnet/roslyn-ide Please review, thanks! |
|
@CyrusNajmabadi could you please take a look? |
src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs
Outdated
Show resolved
Hide resolved
src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs
Outdated
Show resolved
Hide resolved
| highlightMatchingPortions, | ||
| completionHelper); | ||
| selectionHint, | ||
| centerSelection: true, |
There was a problem hiding this comment.
what does 'centerSelection' mean?
There was a problem hiding this comment.
this was not answered.
There was a problem hiding this comment.
"Whether selected item should be displayed in the center of the list. Usually, this is true" from the comment
| { | ||
| Contract.ThrowIfNull(document); | ||
| return (itemsWithPatternMatches, text) => completionService.FilterItems(document, itemsWithPatternMatches, text); | ||
| } |
There was a problem hiding this comment.
i have absolutely no idea what is going on here.
| } | ||
|
|
||
| private FilteredCompletionModel? HandleNormalFiltering( | ||
| private (int selectedItemIndex, UpdateSelectionHint selcetionHint, VSCompletionItem? uniqueItem)? HandleNormalFiltering( |
There was a problem hiding this comment.
selcetionHint mispelled.
Also, what does UpdateSelectionHint even mean?
There was a problem hiding this comment.
was this PR comment handled?
| for (var i = 0; (i < itemsWithMatch.Count); ++i) | ||
| { | ||
| var itemWithMatch = itemsWithMatch[i]; | ||
| if (itemWithMatch.RoslynCompletionItem.FilterText == defaultText) |
There was a problem hiding this comment.
why are we looking at filter-text? we should likely be looking at display text right (in other words, we're seeing that two things in the display match up, what is in teh buffer, and waht is shown the user. if these are the same, we should pick ours?
There was a problem hiding this comment.
Makes sense, I changed it to DisplayText. FYI @dpugh
src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs
Outdated
Show resolved
Hide resolved
src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs
Outdated
Show resolved
Hide resolved
|
The complexity in ItemManager is really too much to handle at this point. We shoudl extract out a new type with clear responsibility as to what it's doing, and what the phases are taht it's doing that work in. There should ideally be a very clear top level method that explains something like:
It really needs to be exceptionally simple and clear waht each phase is trying to accomplish, and what minimal data it needs to have access to to perform that. right now, i just see a ton of data/logic intermixed and i really have no idea if things are happening at the right layer or how the ordering of anything impacts the rest. |
No change to the code other than extracting a helper class here.
This is a refactoring only commit
|
@CyrusNajmabadi Did some refactoring around ItemManager as you suggested, there's no other code change in the last two commits. Hopefully it's provide a bit more clarity here. I will do some additional clean-up and adding comments next. |
|
@CyrusNajmabadi Based on your feedback, I have tuned it down to match case-sensitively for default items. FYI @dpugh |
...atures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs
Show resolved
Hide resolved
| var associatedWithUnselectedExpander = false; | ||
| foreach (var itemFilter in item.Filters) | ||
| { | ||
| if (itemFilter is CompletionExpander) |
There was a problem hiding this comment.
what is a completion-expander?
|
this is looking really good. so much easier to understand than before. my top bit of feedback is: ground the algorithms with concrete examples of what sort of cases the user is running into, and why we're doing things. so cases that go "The item is 'Console' and the user has typed 'C'" are fantastic. They really help the reader get a grasp of what is being handled here and give much more of an intuition about why this behavior is desirable. |
We need to clarify the design before implementing it
| // Only use data.Snapshot in the theoretically possible but rare case when all items we are handling are from some non-Roslyn CompletionSource. | ||
| var snapshotForDocument = TryGetInitialTriggerLocation(_data, out var intialTriggerLocation) | ||
| ? intialTriggerLocation.Snapshot | ||
| : _data.Snapshot; |
There was a problem hiding this comment.
non-roslyn completionsource is a terrifying concept to me :) this is not on you, but in cases like this we really need to call out what those cases are. because i genuinely have no idea how we get into this state.
There was a problem hiding this comment.
You are not alone :)
I'm not certain about this either, therefore reluctant to make change here until understand this better.
...atures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs
Outdated
Show resolved
Hide resolved
| } | ||
| EditorAsyncCompletionData.CompletionTriggerReason.InvokeAndCommitIfUnique => CompletionTriggerKind.InvokeAndCommitIfUnique, | ||
| EditorAsyncCompletionData.CompletionTriggerReason.Insertion => CompletionTriggerKind.Insertion, | ||
| EditorAsyncCompletionData.CompletionTriggerReason.Deletion or EditorAsyncCompletionData.CompletionTriggerReason.Backspace => CompletionTriggerKind.Deletion, |
There was a problem hiding this comment.
so this seems to unify Delete/Backspace to just Delete (which is fine). but later on i see roslyn code checkking for backspace as well... which then seems weird to me.
| // Decide the item to be selected for this completion session. | ||
| // The selection is mostly based on how well the item matches with the filter text, but we also need to | ||
| // take into consideration for things like CompletionTrigger, MatchPriority, MRU, etc. | ||
| var initialSelection = InitialTriggerReason == CompletionTriggerReason.Backspace || InitialTriggerReason == CompletionTriggerReason.Deletion |
There was a problem hiding this comment.
checking for Backspace and Delete seems weird to me given that it looked like your earlier helpers collapsed things down so we only see one of these.
There was a problem hiding this comment.
Previously we have mixed usage of Roslyn's CompeltionTriggerKind and Editor's CompletionTriggerReason in the ItemManager, which is confusing. So I changed to use Editor's CompletionTriggerReason only in async completion layer, unless we are trying to interact with APIs in Roslyn's completion system (i.e. In Features layer)
|
|
||
| // Use a dedicated pool to minimize potentially repeated large allocations, | ||
| // since the completion list could be long with import completion enabled. | ||
| var itemsToBeIncluded = s_listOfMatchResultPool.Allocate(); |
| // Convert intial and update trigger reasons to corresponding Roslyn type so | ||
| // we can interact with Roslyn's completion system | ||
| var roslynInitialTriggerKind = Helpers.GetRoslynTriggerKind(InitialTriggerReason); | ||
| var roslynFilterReason = Helpers.GetFilterReason(UpdateTriggerReason); |
There was a problem hiding this comment.
i feel like we should do this up front, and only expose the roslyn side of htings. is there a reason we ever want to work with the non-roslyn side?
...atures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs
Outdated
Show resolved
Hide resolved
| if (_completionRules.DismissIfEmpty) | ||
| { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
how is this different from the line at: https://github.com/dotnet/roslyn/pull/57685/files#diff-e43ef74201be5195fc625ca928b4c20e14e5dd304a4d42302dba2cfcc2c03557R372 ?
There was a problem hiding this comment.
This applies to all initial trigger kinds, where the dismissal in HandleDeletionTrigger applies to deletion trigger only (and we keep all items in the list in that case, same as handling ctrl+j). It seems we might be able to rewrite this so the dismissal in HandleDeletionTrigger can happen earlier (i.e. handled by this line), but I'd need to think about it more and maybe do it in a follow up PR
|
|
||
| // If the user has turned on some filtering states, and we filtered down to | ||
| // nothing, then we do want the UI to show that to them. That way the user | ||
| // can turn off filters they don't want and get the right set of items. |
There was a problem hiding this comment.
this comment doesnt' seem to match the logic in the method. we seem to unilaterally bail out above, even if filters are set.
There was a problem hiding this comment.
we only bail when the update was triggered by insertion. There's a separate trigger reason for FilterChange
|
@CyrusNajmabadi Hey, is this good to merge now? |

IAsyncCompletionDefaultsSourcereturns the first token of whatever whole line prediction is displayed, the goal is to make selected item in completion more consistent with WLC prediction.So, if you see something like:
The suggested default would be “Console”. If you accept Console and type ‘.’, the suggested default would be “WriteLine”.
The basic algorithm for using the suggested default is:
preselected(e.g. this bug ), use that item instead of suggested default.(in a case-insensitive manner), use the suggested default. Otherwise use the original selected item.Using the same example above, if user typed "C", roslyn might select "CancellationToken", but with suggested default, we will end up selecting "Console".
The handling logic is based on the original implementation in VS (see this), with the additional change to fix this bug and some general cleanup