Skip to content

Don't hold onto documents in 'Find' features#55635

Merged
CyrusNajmabadi merged 38 commits intodotnet:mainfrom
CyrusNajmabadi:holdDocs
Aug 26, 2021
Merged

Don't hold onto documents in 'Find' features#55635
CyrusNajmabadi merged 38 commits intodotnet:mainfrom
CyrusNajmabadi:holdDocs

Conversation

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

@CyrusNajmabadi CyrusNajmabadi commented Aug 16, 2021

Instead of using a Document+Span as the internal currency for locations, use a DocumentId+Span.

Note: features like FAR did not actually need the Document that was passed along after the initial creation of the entry in the UI.

Fixes AB#1371724

@CyrusNajmabadi CyrusNajmabadi requested review from a team as code owners August 16, 2021 15:47
@CyrusNajmabadi CyrusNajmabadi requested a review from a team August 16, 2021 15:47
@ghost ghost added the Area-IDE label Aug 16, 2021
return threadingContext.JoinableTaskFactory.Run(() =>
streamingPresenter.TryNavigateToOrPresentItemsAsync(
threadingContext, workspace, title, definitions, cancellationToken));
threadingContext, solution, title, definitions, cancellationToken));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this method went away as it was identical to the one above (which already took a solution).


var definitionItem = overriddenSymbol.ToNonClassifiedDefinitionItem(document.Project.Solution, true);
await context.OnDefinitionFoundAsync(definitionItem, cancellationToken).ConfigureAwait(false);
await context.OnDefinitionFoundAsync(solution, definitionItem, cancellationToken).ConfigureAwait(false);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

instead of passing along a DefinitionItem (or SourceReferenceItem) which holds onto the Solution, we now pass along a lighterweight object with just the DocId and we also pass along the SOlution as a method-param for clients that need it. but that way they can grab what they need and hold onto the DefItem without rooting the solution.

/// for cases like partial types/members.
/// </summary>
public ImmutableArray<DocumentSpan> SourceSpans { get; }
public ImmutableArray<SerializableDocumentSpan> SourceSpans { get; }
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is the core part of the change that ensures that anything holding onto a DefItem doesn't root the solution.

/// The location of the source item.
/// </summary>
public DocumentSpan SourceSpan { get; }
public SerializableDocumentSpan SourceSpan { get; }
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is the otehr part that ensures we're not rooting the solution if someone holds onto a RefItem.

[Obsolete("Override CanNavigateToAsync instead", error: false)]
public abstract bool CanNavigateTo(Workspace workspace, CancellationToken cancellationToken);
[Obsolete("Override TryNavigateToAsync instead", error: false)]
public abstract bool TryNavigateTo(Workspace workspace, bool showInPreviewTab, bool activateTab, CancellationToken cancellationToken);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

would like to remove thse, but they are used by typescript. @DanielRosenwasser can you add appropriate contacts so i can walk you guys through moving to the new async members?

projectName,
projectFlavor,
document.FilePath,
documentSpan.SourceSpan,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

i didn't want to padd 'documentSpan' along (in case it got accidentally captured in the future), so i explicitly broke out the data we computed from it and passed it along here insetad.

ImmutableDictionary<string, string> customColumnsData)
{
var document = documentSpan.Document;
var (guid, projectName, projectFlavor) = GetGuidAndProjectInfo(document);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

these moved to the caller so we could avoid passing DocumentSpan into this method.


base.PreprocessNavigate(entry, e);
await supportsNavigation.TryNavigateToAsync(e.IsPreview, context.UserCancellationToken).ConfigureAwait(false);
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this logic changed pretty heavily. previously we would navigate synchronously (which coudl block the UI). Now we navigate asynchronously, with a cancellable TWD

protected override Task FindActionAsync(IGoToBaseService service, Document document, int caretPosition, IFindUsagesContext context, CancellationToken cancellationToken)
=> service.FindBasesAsync(document, caretPosition, context, cancellationToken);

protected override Task<Solution> GetSolutionAsync(Document document, CancellationToken cancellationToken)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Often completes synchronously with a non-cached result

Suggested change
protected override Task<Solution> GetSolutionAsync(Document document, CancellationToken cancellationToken)
protected override ValueTask<Solution> GetSolutionAsync(Document document, CancellationToken cancellationToken)

}
return CanNavigateToMetadataSymbol(solution.Workspace, symbolKey);

if (await this.SourceSpans[0].TryRehydrateAsync(solution, cancellationToken).ConfigureAwait(false) is not DocumentSpan span)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Can use implicit type here

Suggested change
if (await this.SourceSpans[0].TryRehydrateAsync(solution, cancellationToken).ConfigureAwait(false) is not DocumentSpan span)
if (await this.SourceSpans[0].TryRehydrateAsync(solution, cancellationToken).ConfigureAwait(false) is not { } span)


public static SerializableDocumentSpan Dehydrate(DocumentSpan documentSpan)
=> new(documentSpan.Document.Id, documentSpan.SourceSpan);
public static SerializableDocumentSpan Dehydrate(DocumentIdSpan documentSpan)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Can you file a follow-up issue to remove SerializableDocumentSpan? I don't want to clutter this pull request, but it should be unnecessary now.

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

Note: i'd like #55696 to go in first. We can then take this second, but also revert it if it turns out we run into any issues.

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

going to move this to 17.1 to ensure we have lots of time to validate this.

CyrusNajmabadi added a commit that referenced this pull request Aug 26, 2021
Extract async navigation portions of #55635
@CyrusNajmabadi CyrusNajmabadi merged commit 9ee36e0 into dotnet:main Aug 26, 2021
@ghost ghost added this to the Next milestone Aug 26, 2021
@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

i didn't merge this. investigating what happened here.

@CyrusNajmabadi CyrusNajmabadi deleted the holdDocs branch August 26, 2021 17:38
@dibarbet dibarbet modified the milestones: Next, 17.0.P4 Aug 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants