Skip to content

Move to a single simple type for internal NavTo results#52315

Merged
CyrusNajmabadi merged 15 commits intodotnet:mainfrom
CyrusNajmabadi:simpleNavToSerialization
Apr 2, 2021
Merged

Move to a single simple type for internal NavTo results#52315
CyrusNajmabadi merged 15 commits intodotnet:mainfrom
CyrusNajmabadi:simpleNavToSerialization

Conversation

@CyrusNajmabadi
Copy link
Contributor

This change makes two important changes to how navto internally reports results when searching C# and VB projects.

First, it decouples the result from having an actual Document instance. We want this in the future as we want to be able to search OOP without necessarily hydrating Document values on the remote side (which saves the synchronization cost).

Second, it removes a couple of intermediary conversion steps. THese were left around from multiple rewrites to this area over the years. NOw, we have a simple internal format for a navto result, and we only convert it to the final form we need as the very last step before giving it to the editor.

@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner March 31, 2021 21:22
@ghost ghost added the Area-IDE label Mar 31, 2021
public static Task SearchProjectInCurrentProcessAsync(
Project project, ImmutableArray<Document> priorityDocuments, string searchPattern,
IImmutableSet<string> kinds, Func<INavigateToSearchResult, Task> onResultFound, bool isFullyLoaded, CancellationToken cancellationToken)
IImmutableSet<string> kinds, Func<RoslynNavigateToItem, Task> onResultFound, bool isFullyLoaded, CancellationToken cancellationToken)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

First change is that internally we use this simple data type RoslynNavigateToItem as the only currency for producing and passing along nav-to results.

kind, matchKind,
isCaseSensitive,
matchedSpans.ToImmutable(),
ComputSecondarySort(declaredSymbolInfo));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

the type only holds onto the basic data that is needed to rehydrate the UI at the end. importantly, it also doesn't need a Document to hold onto. A doc-id is sufficiient. This is important for a followup PR where we allow searchign just by doc-ids.

ComputSecondarySort(declaredSymbolInfo));
}

private static string ComputSecondarySort(DeclaredSymbolInfo declaredSymbolInfo)
Copy link
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Mar 31, 2021

Choose a reason for hiding this comment

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

these function moved from AbstractNavigateToSearchService.SearchResult.cs, but are otherwise almost hte same.


await SearchDocumentInCurrentProcessAsync(
document, searchPattern, kinds, onResultFound, isFullyLoaded, cancellationToken).ConfigureAwait(false);
document, searchPattern, kinds, onItemFound, isFullyLoaded, cancellationToken).ConfigureAwait(false);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

both the remote side and the local side only deal with RoslynNavigateToItem internally. Only on the boundary (this method) do we convert it to the INavigateToResult that consumers need.

public readonly ImmutableArray<TextSpan> NameMatchSpans;

[DataMember(Order = 15)]
public readonly string SecondarySort;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's definitely a lot of fields. but all are used.

var project = solution.GetProject(DocumentId.ProjectId);
if (project != null)
document = project.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(DocumentId);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jasonmalinowski is this the right way to map a doc id back to a possibly source-generated document?

Copy link
Member

Choose a reason for hiding this comment

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

Is it possible to just make this path async? Because then you can just call GetDocumentAsync().

Copy link
Member

Choose a reason for hiding this comment

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

And the longer answer: that API I really wish I could mark as friend of the other parts of the workspace so it wasn't available but we don't have that in C#. This might work though, but asyncifying is preferrable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is it possible to just make this path async? Because then you can just call GetDocumentAsync().

i don't want this to be async. if it references a source generated document, then we better have that document. i do not want the documents to be generated.

Copy link
Member

Choose a reason for hiding this comment

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

So two things to be aware of:

  1. If this type being serialized across a wire, we might have a source generated document generated in-proc but not OOP yet or vice versa if one side is ahead of the other. We do want to get it updated so a generators only run OOP and in-proc will just pull the contents over, but that would have to be behind an async call.
  2. This method will not return stale results, at all. I don't know what your lifetime model is, but if say project B depends on A, and A is updated, there's no guarantees this will work.

I haven't reviewed the rest of this PR to figure out what's going on and whether this is safe to use. The intent of the API here was only to implement GetDocument() that takes a syntax tree. I've always been switching code over to GetDocumentAsync() in any sort of OOP-rehydration because calling the async method is either:

  1. fast and cheap if the document does exist
  2. won't crash if the document doesn't exist

internal readonly struct RoslynNavigateToItem
{
[DataMember(Order = 0)]
public readonly bool IsStale;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

stale-ness is a concept that will be in the next pr. currently all results are non-stale. however, in the next PR it will be ok for a stale-result to be returned. the host can then filter it out if that doc doesn't exist in teh current solution. Navigation will also have to be resilient to the span possibly being out of date.

}
}
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this logic moved from teh main searcher. It depends on actual Document/Project info, so we don't want to do it in OOP, where i want NavigateTo to not depend on those. Instead, it does it on the host side before passing back out to vs.


Glyph INavigableItem.Glyph => GetGlyph(_item.DeclaredSymbolInfoKind, _item.DeclaredSymbolInfoAccessibility);

private static Glyph GetPublicGlyph(DeclaredSymbolInfoKind kind)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

moved from NavigableItemFactory.DeclaredSymbolNavigableItem.cs

{
internal partial class NavigableItemFactory
{
internal class DeclaredSymbolNavigableItem : INavigableItem
Copy link
Contributor Author

Choose a reason for hiding this comment

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

another pointless intermediary type. removed.

#region NavigateTo

[DataContract]
internal readonly struct SerializableNavigateToSearchResult
Copy link
Contributor Author

Choose a reason for hiding this comment

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

more remvoed marshalling types.

Copy link
Contributor

@sharwell sharwell left a comment

Choose a reason for hiding this comment

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

LGTM on condition that you've tried it in experimental instance on Roslyn for each of the various types we support

Comment on lines +147 to +150
Intern(stringTable, name),
Intern(stringTable, nameSuffix),
Intern(stringTable, containerDisplayName),
Intern(stringTable, fullyQualifiedContainerName),
Copy link
Contributor

Choose a reason for hiding this comment

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

❔ Do we not want to move this into the constructor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no. i wanted to be able to use thsi as a DataContract. so the pure data part is in the tyep/constructor. but the types that create tehse on the OOP side go through teh factory method to share stirngs there.

@CyrusNajmabadi
Copy link
Contributor Author

@jasonmalinowski is this a better way to do things?

Copy link
Member

@dibarbet dibarbet left a comment

Choose a reason for hiding this comment

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

lgtm, but I can't comment on the source generators bit.

matchedSpans.ToImmutable());
}

private static string ComputeCombinedProjectName(Document document, ImmutableArray<Project> additionalMatchingProjects)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this logic is the same. t he method just moved.

@CyrusNajmabadi CyrusNajmabadi enabled auto-merge April 1, 2021 20:33
@CyrusNajmabadi CyrusNajmabadi merged commit b969565 into dotnet:main Apr 2, 2021
@ghost ghost added this to the Next milestone Apr 2, 2021
@CyrusNajmabadi CyrusNajmabadi deleted the simpleNavToSerialization branch April 11, 2021 18:20
@dibarbet dibarbet modified the milestones: Next, 16.10.P3 Apr 26, 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.

4 participants