Skip to content
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

Generate Documentation - Add loading state #77718

Merged

Conversation

akhera99
Copy link
Member

Loading_State_Generate_Docs.mp4

@akhera99 akhera99 requested a review from a team as a code owner March 20, 2025 23:05
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead labels Mar 20, 2025
@akhera99 akhera99 requested a review from genlu March 20, 2025 23:05
var intellicodeLineCompletionsDisposable = await _suggestionManager!.DisableProviderAsync(SuggestionServiceNames.IntelliCodeLineCompletions, cancellationToken).ConfigureAwait(false);
var intelliCodeLineCompletionsDisposable = await _suggestionManager!.DisableProviderAsync(SuggestionServiceNames.IntelliCodeLineCompletions, cancellationToken).ConfigureAwait(false);
var suggestion = new DocumentationCommentSuggestion(this, _suggestionManager, intelliCodeLineCompletionsDisposable);
var suggestionSessionStarted = await suggestion.StartSuggestionSessionAsync(cancellationToken).ConfigureAwait(false);
Copy link
Member

Choose a reason for hiding this comment

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

nit: i feel this unnecessarily exposed the internal of DocumentationCommentSuggestion, (like you need to make sure the order of calls, etc.) consider passing in a lambda to calculate the proposal to StartSuggestionSessionAsync and let suggestion handles display/dismiss in it.

Copy link
Member

Choose a reason for hiding this comment

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

thoughts on this comment Ankita?

Copy link
Member

@genlu genlu left a comment

Choose a reason for hiding this comment

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

:shipit:

}
});

if (!taskCompletionSource.Task.IsCompleted)
Copy link
Member

Choose a reason for hiding this comment

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

this seems superfluous. just await the task. if it's complete, then it will no op anyways.

Copy link
Member Author

Choose a reason for hiding this comment

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

commented in the code, but added that the pattern is consistent with what the platform expects.

Copy link

Choose a reason for hiding this comment

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

The idea was to avoid the cost of switching to the background thread if the task wasn't complete.

"DismissSuggestionSession",
async () => await _suggestionSession!.DismissAsync(ReasonForDismiss.DismissedDueToInvalidProposal, cancellationToken).ConfigureAwait(false),
cancellationToken).ConfigureAwait(false);
}
Copy link
Member

Choose a reason for hiding this comment

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

why is thre DIsmiss and Dispose? WHy not just one concept?

Copy link
Member Author

Choose a reason for hiding this comment

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

Dismiss is on the SuggestionSession (platform owned), which is what we start to give us exclusive rights to display grey text later. Dispose is on us to allow line completions to start being displayed again since we took away their right to when we started our SuggestionSession.

Copy link
Member

Choose a reason for hiding this comment

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

so, generally speaking, cmments like this in the PR are worth putting into the code, because the code isn't clear as to why that's going on.

var taskCompletionSource = new TaskCompletionSource<bool>();

await providerInstance.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancel);
SuggestionManager.EnqueueAction(description, async () =>
Copy link
Member

Choose a reason for hiding this comment

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

this is super weird. they expect you to pass in an async function. but then they dont' make this function itself task-returning so you can track it?

Copy link
Member Author

Choose a reason for hiding this comment

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

talked offline

@CyrusNajmabadi
Copy link
Member

done with pass.


var taskCompletionSource = new TaskCompletionSource<bool>();

await providerInstance.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancel);
Copy link
Member

Choose a reason for hiding this comment

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

why are we switching to the UI thread? if this is all async, why do they not do this?

@akhera99 akhera99 requested a review from CyrusNajmabadi March 27, 2025 01:06
{
Logger.Log(FunctionId.Copilot_Generate_Documentation_Displayed, logLevel: LogLevel.Information);
}
await DisposeAsync().ConfigureAwait(false);
Copy link
Member

Choose a reason for hiding this comment

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

i def feel like this shouldbe commented to explain what's going on. ou could even consider having a helper method with a clearer name that hten internallyc alls into DisposeAsync.

/// </summary>
public async Task DismissSuggestionSessionAsync(CancellationToken cancellationToken)
{
await DisposeAsync().ConfigureAwait(false);
Copy link
Member

Choose a reason for hiding this comment

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

same point about commenting this.

@@ -46,37 +48,37 @@ public async Task InitializeAsync(ITextView textView, SuggestionServiceBase sugg
public async Task GenerateDocumentationProposalAsync(DocumentationCommentSnippet snippet,
ITextSnapshot oldSnapshot, VirtualSnapshotPoint oldCaret, CancellationToken cancellationToken)
{
await Task.Yield().ConfigureAwait(false);
await TaskScheduler.Default;
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe wait until after the !Enabled check before yielding the thread to your caller, in case you don't need to yield at all.


var taskCompletionSource = new TaskCompletionSource<T>();

await providerInstance.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
Copy link
Contributor

Choose a reason for hiding this comment

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

Does SuggestionManager.EnqueueAction require enqueuing to take place on the main thread? As a scheduler, that's a weird requirement. And if it isn't a requirement, this seems wasteful to do. If you can remove the switch, you can make this whole method non-async, which improves efficiency.

Copy link
Member Author

Choose a reason for hiding this comment

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

I believe so based on the pattern in the platform, but @dpugh would know more about this particular requirement.

{
Assumes.NotNull(SuggestionManager);

var taskCompletionSource = new TaskCompletionSource<T>();
Copy link
Contributor

Choose a reason for hiding this comment

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

Given the (slightly odd) design that this queue doesn't provide a way to track work, and you're hacking one in, you're potentially opening it up to a problem. When action() completes, continuations that your returned Task adds on (by awaiting the result of this method) may run inline with the action itself, holding up the queue even longer before it can work on the next thing.
You may want to create this TCS with TaskCreationOptions.RunContinuationsAsynchronously to prevent accidentally inlining arbitrary work into the queue.

{
try
{
var result = await action().ConfigureAwait(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

Since all you do here is mark a TCS complete, you can potentially improve efficiency by using ConfigureAwaitRunInline here instead.
Just be sure to combine it with my other suggestion to prevent other continuations from being inlined.

@akhera99 akhera99 enabled auto-merge (squash) March 27, 2025 21:32
@akhera99 akhera99 merged commit 3cff987 into dotnet:main Mar 28, 2025
25 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Mar 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead VSCode
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants