-
Notifications
You must be signed in to change notification settings - Fork 4.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 item InsertionText in the debugger, even before any non-debugger completion sessions have run #26361
Use completion item InsertionText in the debugger, even before any non-debugger completion sessions have run #26361
Conversation
_nameToProvider.TryGetValue(name, out provider); | ||
if (!_nameToProvider.TryGetValue(name, out provider)) | ||
{ | ||
CreateRoleProviders(ImmutableHashSet.Create<string>()); |
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 this is normally initialized by the call to GetProviders? Why isn't that getting called?
if (!_nameToProvider.TryGetValue(name, out provider)) | ||
{ | ||
// Sometimes the _nameToProvider map hasn't been fully initialized in | ||
// debugger scenarios. If we couldn't find the provider, ensure everything |
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.
hrmm.. that makes me feel like that's an issue in that code path. How can the debugger sidestep the initialization of the providers?
cf970e9
to
0367baf
Compare
0367baf
to
28f9a82
Compare
/// debugger before ever doing completion in a regular open document, then the completion | ||
/// service being used to commit items hasn't been initialized (that is, | ||
/// ShouldTriggerCompletion has never been called). So, when it's time to lookup the | ||
/// provider for the committed item, the cache has not been created, so we don't find |
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'm trying to reconcile these bits:
In debugger scenarios, the completion service specific to that instance is used to trigger completion, but the one created for regular editing scenarios is used for showing the Description and for Committing.
Isn't that super wonky. We're working across two completion services instead of using the actual service that created the items. To me it feels like the bug is there. How/Why do we get into this situation in the first place?
/// a problem for entries like "List<>", where the insertion text and DisplayText | ||
/// differ. | ||
/// Refactoring such that the same completion service is used for triggering and | ||
/// descriptions/committing in debugger scenarios is risky, so we're taking a more |
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.
really? it seems super strange that we can even get into this situatoin in the first place, and it makes me very quesy about how these services are created and then used. i mean, it basically hopes/prays htat the one service will have no problem and have no expectations when getting data from another service. As htis bug indicates, that's clearly not a good assumption to make :)
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.
can this be answered? It seems super weird to me that we even got into this situatoin in the first place. What codepath it working with multiple CompletionServices... adn why not just fix that codepath? That seems like teh real fix to have here...
@@ -194,6 +218,12 @@ internal protected CompletionProvider GetProvider(CompletionItem item) | |||
{ | |||
lock (_gate) | |||
{ | |||
if (!_roleProvidersInitialized) | |||
{ | |||
CreateRoleProviders(ImmutableHashSet.Create<string>()); |
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.
Why is ImmutableHashSet.Create<string>()
the right value to use here? Can you explain/comment why?
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.
Also, still not quite understanding how this won't still have a problem elsewhere. for example, say that the item references a provider that thsi service doesn't know about. Won't this just return null, causing a crash elsewhere? or are we resilient to that?
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.
Can this be answered?
CreateRoleProviders(ImmutableHashSet.Create<string>()); | ||
_roleProvidersInitialized = true; | ||
} | ||
|
||
_nameToProvider.TryGetValue(name, out provider); |
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.
❓ Why not change this to a GetOrAdd
call that initializes the requested provider on demand, similar to how _rolesToProviders
is used above? It seems this would be a clean (non-hacky) way to address the problem.
28f9a82
to
e4db5ae
Compare
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.
Nice, I definitely like this more than the first revision. Perhaps update the escrow template now that the PR has been cleaned up?
} | ||
} | ||
|
||
return provider; | ||
} | ||
|
||
private CompletionProvider GetProviderByName(string providerName) | ||
{ | ||
var providers = GetProviders(roles: null); |
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.
can a comment be provided explaining why passing null for roles is correct here. I'm not quite understanding.
…rticular provider Fixes dotnet#24112
e4db5ae
to
7ede59e
Compare
retest windows_debug_spanish_unit32_prtest please |
} | ||
} | ||
|
||
return provider; | ||
} | ||
|
||
private CompletionProvider GetProviderByName(string providerName) | ||
{ | ||
var providers = GetAllProviders(roles: ImmutableHashSet<string>.Empty); |
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.
a comment here explaining why this is the right set of roles would be good. i actually do not know why this is correct.
Tagging @jinujoseph for consideration. |
@dpoeschl can you retarget this to master (15.8.Preview2 ) |
Approved to merge for 15.8.Preview2 (once we have green test ) |
It concerns me that none of this has any documentation explaining waht is going on. This sort of fix invariably leads to problems that get compounded as more and more patches are piled on. |
Fixes #24112
This uncovered a weird issue in how completion services are used / shared between the normal editor and debugger scenarios. #26394 tracks a fix for that larger issue, while this PR addresses the immediate concern of uninitialized data.
Escrow Template
Customer scenario
The customer uses completion in the debugger (Watch window, etc.) before ever using completion in the regular editor. Items like
List<>
that are supposed to commit as justList
will commit asList<>
, and you then have to go back and clean it up.Bugs this fixes
#24112
Workarounds, if any
Either
Risk
The risk of this change is pretty low. We're making sure a cache is populated when it is first needed.
Performance impact
Basically none. No normal scenarios are impacted, and in the case of debugger-completion-before-editor-completion, we do a little bit of mef work when the completion list is shown where we didn't before, but that's necessary for correctness.
Is this a regression from a previous update?
No
Root cause analysis
The architecture of this partially lazily initialized type relies on an assumption that isn't being met currently.
How was the bug found?
Ad hoc testing