private async Task AddDeclarationEntriesAsync(DefinitionItem definition) { CancellationToken.ThrowIfCancellationRequested(); // Don't do anything if we already have declaration entries for this definition // (i.e. another thread beat us to this). if (HasDeclarationEntries(definition)) { return; } var definitionBucket = GetOrCreateDefinitionBucket(definition); // We could do this inside the lock. but that would mean async activity in a // lock, and I'd like to avoid that. That does mean that we might do extra // work if multiple threads end up down this path. But only one of them will // win when we access the lock below. var declarations = ArrayBuilder <Entry> .GetInstance(); foreach (var declarationLocation in definition.SourceSpans) { var definitionEntry = await TryCreateDocumentSpanEntryAsync( definitionBucket, declarationLocation, HighlightSpanKind.Definition, propertiesWithMultipleValuesMapOpt : null, additionalProperties : definition.DisplayableProperties).ConfigureAwait(false); declarations.AddIfNotNull(definitionEntry); } var changed = false; lock (Gate) { // Do one final check to ensure that no other thread beat us here. if (!HasDeclarationEntries(definition)) { // We only include declaration entries in the entries we show when // not grouping by definition. EntriesWhenNotGroupingByDefinition = EntriesWhenNotGroupingByDefinition.AddRange(declarations); CurrentVersionNumber++; changed = true; } } if (changed) { // Let all our subscriptions know that we've updated. NotifyChange(); } declarations.Free(); }
protected override async Task OnDefinitionFoundWorkerAsync(DefinitionItem definition) { var definitionBucket = GetOrCreateDefinitionBucket(definition); var entries = ArrayBuilder <Entry> .GetInstance(); if (definition.SourceSpans.Length == 1) { // If we only have a single location, then use the DisplayParts of the // definition as what to show. That way we show enough information for things // methods. i.e. we'll show "void TypeName.MethodName(args...)" allowing // the user to see the type the method was created in. var entry = await TryCreateEntryAsync(definitionBucket, definition).ConfigureAwait(false); entries.AddIfNotNull(entry); } else { // If we have multiple spans (i.e. for partial types), then create a // DocumentSpanEntry for each. That way we can easily see the source // code where each location is to help the user decide which they want // to navigate to. foreach (var sourceSpan in definition.SourceSpans) { var entry = await TryCreateDocumentSpanEntryAsync( definitionBucket, sourceSpan, HighlightSpanKind.Definition, symbolUsageInfo : SymbolUsageInfo.None, additionalProperties : definition.DisplayableProperties) .ConfigureAwait(false); entries.AddIfNotNull(entry); } } if (entries.Count > 0) { lock (Gate) { EntriesWhenGroupingByDefinition = EntriesWhenGroupingByDefinition.AddRange(entries); EntriesWhenNotGroupingByDefinition = EntriesWhenNotGroupingByDefinition.AddRange(entries); } NotifyChange(); } entries.Free(); }
private async ValueTask OnEntryFoundAsync( DefinitionItem definition, Func <RoslynDefinitionBucket, Task <Entry?> > createEntryAsync, bool addToEntriesWhenGroupingByDefinition, bool addToEntriesWhenNotGroupingByDefinition, bool expandedByDefault, CancellationToken cancellationToken) { Debug.Assert(addToEntriesWhenGroupingByDefinition || addToEntriesWhenNotGroupingByDefinition); cancellationToken.ThrowIfCancellationRequested(); // OK, we got a *reference* to some definition item. This may have been a reference for some definition // that we haven't created any declaration entries for (i.e. because it had DisplayIfNoReferences = // false). Because we've now found a reference, we want to make sure all its declaration entries are // added. await AddDeclarationEntriesAsync(definition, expandedByDefault, cancellationToken).ConfigureAwait(false); // First find the bucket corresponding to our definition. var definitionBucket = GetOrCreateDefinitionBucket(definition, expandedByDefault); var entry = await createEntryAsync(definitionBucket).ConfigureAwait(false); // Proceed, even if we didn't create an entry. It's possible that we augmented // an existing entry and we want the UI to refresh to show the results of that. lock (Gate) { if (entry != null) { // Once we can make the new entry, add it to the appropriate list. if (addToEntriesWhenGroupingByDefinition) { EntriesWhenGroupingByDefinition = EntriesWhenGroupingByDefinition.Add(entry); } if (addToEntriesWhenNotGroupingByDefinition) { EntriesWhenNotGroupingByDefinition = EntriesWhenNotGroupingByDefinition.Add(entry); } } CurrentVersionNumber++; } // Let all our subscriptions know that we've updated. NotifyChange(); }
protected async Task OnEntryFoundAsync( DefinitionItem definition, Func <RoslynDefinitionBucket, Task <Entry> > createEntryAsync, bool addToEntriesWhenGroupingByDefinition, bool addToEntriesWhenNotGroupingByDefinition) { Debug.Assert(addToEntriesWhenGroupingByDefinition || addToEntriesWhenNotGroupingByDefinition); CancellationToken.ThrowIfCancellationRequested(); // OK, we got a *reference* to some definition item. This may have been // a reference for some definition that we haven't created any declaration // entries for (i.e. because it had DisplayIfNoReferences = false). Because // we've now found a reference, we want to make sure all its declaration // entries are added. await AddDeclarationEntriesAsync(definition).ConfigureAwait(false); // First find the bucket corresponding to our definition. var definitionBucket = GetOrCreateDefinitionBucket(definition); var entry = await createEntryAsync(definitionBucket).ConfigureAwait(false); if (entry == null) { return; } lock (Gate) { // Once we can make the new entry, add it to the appropriate list. if (addToEntriesWhenGroupingByDefinition) { EntriesWhenGroupingByDefinition = EntriesWhenGroupingByDefinition.Add(entry); } if (addToEntriesWhenNotGroupingByDefinition) { EntriesWhenNotGroupingByDefinition = EntriesWhenNotGroupingByDefinition.Add(entry); } CurrentVersionNumber++; } // Let all our subscriptions know that we've updated. NotifyChange(); }
private async Task AddDeclarationEntriesAsync( DefinitionItem definition, bool expandedByDefault ) { CancellationToken.ThrowIfCancellationRequested(); // Don't do anything if we already have declaration entries for this definition // (i.e. another thread beat us to this). if (HasDeclarationEntries(definition)) { return; } var definitionBucket = GetOrCreateDefinitionBucket(definition, expandedByDefault); // We could do this inside the lock. but that would mean async activity in a // lock, and I'd like to avoid that. That does mean that we might do extra // work if multiple threads end up down this path. But only one of them will // win when we access the lock below. using var _1 = ArrayBuilder <Entry> .GetInstance(out var declarations); using var _2 = PooledHashSet <(string?filePath, TextSpan span)> .GetInstance( out var seenLocations ); foreach (var declarationLocation in definition.SourceSpans) { // Because of things like linked files, we may have a source symbol showing up in multiple // different locations that are effectively at the exact same navigation location for the user. // i.e. they're the same file/span. Showing multiple entries for these is just noisy and // gets worse and worse with shared projects and whatnot. So, we collapse things down to // only show a single entry for each unique file/span pair. Note that we check filepath, // not 'Document' as linked files will have unique Document instances in each project context // they are linked into. if ( !seenLocations.Add( (declarationLocation.Document.FilePath, declarationLocation.SourceSpan) ) ) { continue; } var definitionEntry = await TryCreateDocumentSpanEntryAsync( definitionBucket, declarationLocation, HighlightSpanKind.Definition, SymbolUsageInfo.None, additionalProperties : definition.DisplayableProperties ) .ConfigureAwait(false); declarations.AddIfNotNull(definitionEntry); } var changed = false; lock (Gate) { // Do one final check to ensure that no other thread beat us here. if (!HasDeclarationEntries(definition)) { // We only include declaration entries in the entries we show when // not grouping by definition. EntriesWhenNotGroupingByDefinition = EntriesWhenNotGroupingByDefinition.AddRange(declarations); CurrentVersionNumber++; changed = true; } } if (changed) { // Let all our subscriptions know that we've updated. NotifyChange(); } }