From 5eaae79c9a2addbafa9827438ef6f96b42191a28 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 31 Jan 2017 19:36:18 -0800 Subject: [PATCH] Cleanup code. --- .../FindReferencesCommandHandler.cs | 2 +- .../Host/IStreamingFindReferencesPresenter.cs | 11 +- .../WithReferencesFindUsagesContext.cs | 385 +++++++++--------- .../WithoutReferencesFindUsagesContext.cs | 115 +++--- .../Entries/AbstractDocumentSpanEntry.cs | 109 +++-- .../Entries/DefinitionItemEntry.cs | 47 +-- .../Entries/DocumentSpanEntry.cs | 41 +- .../StreamingFindUsagesPresenter.cs | 8 +- 8 files changed, 367 insertions(+), 351 deletions(-) diff --git a/src/EditorFeatures/Core/FindReferences/FindReferencesCommandHandler.cs b/src/EditorFeatures/Core/FindReferences/FindReferencesCommandHandler.cs index db1819b1cea..a19120fee4c 100644 --- a/src/EditorFeatures/Core/FindReferences/FindReferencesCommandHandler.cs +++ b/src/EditorFeatures/Core/FindReferences/FindReferencesCommandHandler.cs @@ -126,7 +126,7 @@ private IStreamingFindUsagesPresenter GetStreamingPresenter() // Let the presented know we're starging a search. It will give us back // the context object that the FAR service will push results into. var context = presenter.StartSearch( - EditorFeaturesResources.Find_References, canShowReferences: true); + EditorFeaturesResources.Find_References, supportsReferences: true); await findUsagesService.FindReferencesAsync(document, caretPosition, context).ConfigureAwait(false); // Note: we don't need to put this in a finally. The only time we might not hit diff --git a/src/EditorFeatures/Core/Host/IStreamingFindReferencesPresenter.cs b/src/EditorFeatures/Core/Host/IStreamingFindReferencesPresenter.cs index 7622cba2cc3..4d5a053139c 100644 --- a/src/EditorFeatures/Core/Host/IStreamingFindReferencesPresenter.cs +++ b/src/EditorFeatures/Core/Host/IStreamingFindReferencesPresenter.cs @@ -21,7 +21,13 @@ internal interface IStreamingFindUsagesPresenter /// search completes should be called. /// etc. etc. /// - FindUsagesContext StartSearch(string title, bool canShowReferences); + /// A title to display to the user in the presentation of the results. + /// Whether or not showing references is supported. + /// If true, then the presenter can group by definition, showing references underneath. + /// It can also show messages about no references being found at the end of the search. + /// If false, the presenter will not group by definitions, and will show the definition + /// items in isolation. + FindUsagesContext StartSearch(string title, bool supportsReferences); } internal static class IStreamingFindUsagesPresenterExtensions @@ -65,8 +71,7 @@ internal static class IStreamingFindUsagesPresenterExtensions { // We have multiple definitions, or we have definitions with multiple locations. // Present this to the user so they can decide where they want to go to. - - var context = presenter.StartSearch(title, canShowReferences: false); + var context = presenter.StartSearch(title, supportsReferences: false); foreach (var definition in nonExternalItems) { await context.OnDefinitionFoundAsync(definition).ConfigureAwait(false); diff --git a/src/VisualStudio/Core/Next/FindReferences/Contexts/WithReferencesFindUsagesContext.cs b/src/VisualStudio/Core/Next/FindReferences/Contexts/WithReferencesFindUsagesContext.cs index 5cde213ebaa..660c5d93ee8 100644 --- a/src/VisualStudio/Core/Next/FindReferences/Contexts/WithReferencesFindUsagesContext.cs +++ b/src/VisualStudio/Core/Next/FindReferences/Contexts/WithReferencesFindUsagesContext.cs @@ -14,244 +14,249 @@ namespace Microsoft.VisualStudio.LanguageServices.FindUsages { - internal class WithReferencesFindUsagesContext : AbstractTableDataSourceFindUsagesContext + /// + /// Context to be used for FindAllReferences (as opposed to FindImplementations/GoToDef). + /// This context supports showing reference items, and will display appropriate messages + /// about no-references being found for a definition at the end of the search. + /// + internal class WithReferencesFindUsagesContext : AbstractTableDataSourceFindUsagesContext + { + public WithReferencesFindUsagesContext( + StreamingFindUsagesPresenter presenter, + IFindAllReferencesWindow findReferencesWindow) + : base(presenter, findReferencesWindow) { - public WithReferencesFindUsagesContext( - StreamingFindUsagesPresenter presenter, - IFindAllReferencesWindow findReferencesWindow) - : base(presenter, findReferencesWindow) - { - } + } - protected override async Task OnDefinitionFoundWorkerAsync(DefinitionItem definition) + protected override async Task OnDefinitionFoundWorkerAsync(DefinitionItem definition) + { + // If this is a definition we always want to show, then create entries + // for all the declaration locations immediately. Otherwise, we'll + // create them on demand when we hear about references for this definition. + if (definition.DisplayIfNoReferences) { - // If this is a definition we always want to show, then create entries - // for all the declaration locations immediately. Otherwise, we'll - // create them on demand when we hear about references for this definition. - if (definition.DisplayIfNoReferences) - { - await AddDeclarationEntriesAsync(definition).ConfigureAwait(false); - } + await AddDeclarationEntriesAsync(definition).ConfigureAwait(false); } + } - 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 htis path. But only one of them will - // win when we access the lock below. - var declarations = ArrayBuilder.GetInstance(); - foreach (var declarationLocation in definition.SourceSpans) - { - var definitionEntry = await CreateDocumentSpanEntryAsync( - definitionBucket, declarationLocation, isDefinitionLocation: true).ConfigureAwait(false); - if (definitionEntry != null) - { - declarations.Add(definitionEntry); - } - } + private async Task AddDeclarationEntriesAsync(DefinitionItem definition) + { + CancellationToken.ThrowIfCancellationRequested(); - 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; - } - } + // 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; + } - declarations.Free(); + var definitionBucket = GetOrCreateDefinitionBucket(definition); - if (changed) + // 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 htis path. But only one of them will + // win when we access the lock below. + var declarations = ArrayBuilder.GetInstance(); + foreach (var declarationLocation in definition.SourceSpans) + { + var definitionEntry = await CreateDocumentSpanEntryAsync( + definitionBucket, declarationLocation, isDefinitionLocation: true).ConfigureAwait(false); + if (definitionEntry != null) { - // Let all our subscriptions know that we've updated. - NotifyChange(); + declarations.Add(definitionEntry); } } - private bool HasDeclarationEntries(DefinitionItem definition) + var changed = false; + lock (_gate) { - lock (_gate) + // Do one final check to ensure that no other thread beat us here. + if (!HasDeclarationEntries(definition)) { - return _entriesWhenNotGroupingByDefinition.Any( - e => e.DefinitionBucket.DefinitionItem == definition); + // We only include declaration entries in the entries we show when + // not grouping by definition. + _entriesWhenNotGroupingByDefinition = _entriesWhenNotGroupingByDefinition.AddRange(declarations); + CurrentVersionNumber++; + changed = true; } } - protected override Task OnReferenceFoundWorkerAsync(SourceReferenceItem reference) + declarations.Free(); + + if (changed) { - // Normal references go into both sets of entries. - return OnEntryFoundAsync( - reference.Definition, - bucket => CreateDocumentSpanEntryAsync( - bucket, reference.SourceSpan, isDefinitionLocation: false), - addToEntriesWhenGroupingByDefinition: true, - addToEntriesWhenNotGroupingByDefinition: true); + // Let all our subscriptions know that we've updated. + NotifyChange(); } + } - protected async Task OnEntryFoundAsync( - DefinitionItem definition, - Func> createEntryAsync, - bool addToEntriesWhenGroupingByDefinition, - bool addToEntriesWhenNotGroupingByDefinition) + private bool HasDeclarationEntries(DefinitionItem definition) + { + lock (_gate) { - 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. becuase 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); + return _entriesWhenNotGroupingByDefinition.Any( + e => e.DefinitionBucket.DefinitionItem == definition); + } + } - // First find the bucket corresponding to our definition. - var definitionBucket = GetOrCreateDefinitionBucket(definition); - var entry = await createEntryAsync(definitionBucket).ConfigureAwait(false); + protected override Task OnReferenceFoundWorkerAsync(SourceReferenceItem reference) + { + // Normal references go into both sets of entries. + return OnEntryFoundAsync( + reference.Definition, + bucket => CreateDocumentSpanEntryAsync( + bucket, reference.SourceSpan, isDefinitionLocation: false), + addToEntriesWhenGroupingByDefinition: true, + addToEntriesWhenNotGroupingByDefinition: true); + } - lock (_gate) - { - // Once we can make the new entry, add it to the appropriate list. - if (addToEntriesWhenGroupingByDefinition) - { - _entriesWhenGroupingByDefinition = _entriesWhenGroupingByDefinition.Add(entry); - } + protected async Task OnEntryFoundAsync( + DefinitionItem definition, + Func> createEntryAsync, + bool addToEntriesWhenGroupingByDefinition, + bool addToEntriesWhenNotGroupingByDefinition) + { + Debug.Assert(addToEntriesWhenGroupingByDefinition || addToEntriesWhenNotGroupingByDefinition); + CancellationToken.ThrowIfCancellationRequested(); - if (addToEntriesWhenNotGroupingByDefinition) - { - _entriesWhenNotGroupingByDefinition = _entriesWhenNotGroupingByDefinition.Add(entry); - } + // 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. becuase 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); - CurrentVersionNumber++; + // First find the bucket corresponding to our definition. + var definitionBucket = GetOrCreateDefinitionBucket(definition); + var entry = await createEntryAsync(definitionBucket).ConfigureAwait(false); + + lock (_gate) + { + // Once we can make the new entry, add it to the appropriate list. + if (addToEntriesWhenGroupingByDefinition) + { + _entriesWhenGroupingByDefinition = _entriesWhenGroupingByDefinition.Add(entry); } - // Let all our subscriptions know that we've updated. - NotifyChange(); + if (addToEntriesWhenNotGroupingByDefinition) + { + _entriesWhenNotGroupingByDefinition = _entriesWhenNotGroupingByDefinition.Add(entry); + } + + CurrentVersionNumber++; } - protected override async Task OnCompletedAsyncWorkerAsync() + // Let all our subscriptions know that we've updated. + NotifyChange(); + } + + protected override async Task OnCompletedAsyncWorkerAsync() + { + // Now that we know the search is over, create and display any error messages + // for definitions that were not found. + await CreateMissingReferenceEntriesIfNecessaryAsync().ConfigureAwait(false); + await CreateNoResultsFoundEntryIfNecessaryAsync().ConfigureAwait(false); + } + + private async Task CreateMissingReferenceEntriesIfNecessaryAsync() + { + await CreateMissingReferenceEntriesIfNecessaryAsync(whenGroupingByDefinition: true).ConfigureAwait(false); + await CreateMissingReferenceEntriesIfNecessaryAsync(whenGroupingByDefinition: false).ConfigureAwait(false); + } + + private async Task CreateMissingReferenceEntriesIfNecessaryAsync( + bool whenGroupingByDefinition) + { + // Go through and add dummy entries for any definitions that + // that we didn't find any references for. + + var definitions = GetDefinitionsToCreateMissingReferenceItemsFor(whenGroupingByDefinition); + foreach (var definition in definitions) { - // Now that we know the search is over, create and display any error messages - // for definitions that were not found. - await CreateMissingReferenceEntriesIfNecessaryAsync().ConfigureAwait(false); - await CreateNoResultsFoundEntryIfNecessaryAsync().ConfigureAwait(false); + // Create a fake reference to this definition that says + // "no references found to ". + await OnEntryFoundAsync(definition, + bucket => SimpleMessageEntry.CreateAsync( + bucket, GetMessage(bucket.DefinitionItem)), + addToEntriesWhenGroupingByDefinition: whenGroupingByDefinition, + addToEntriesWhenNotGroupingByDefinition: !whenGroupingByDefinition).ConfigureAwait(false); } + } - private async Task CreateMissingReferenceEntriesIfNecessaryAsync() + private static string GetMessage(DefinitionItem definition) + { + if (definition.IsExternal) { - await CreateMissingReferenceEntriesIfNecessaryAsync(whenGroupingByDefinition: true).ConfigureAwait(false); - await CreateMissingReferenceEntriesIfNecessaryAsync(whenGroupingByDefinition: false).ConfigureAwait(false); + return ServicesVisualStudioNextResources.External_reference_found; } - private async Task CreateMissingReferenceEntriesIfNecessaryAsync( - bool whenGroupingByDefinition) - { - // Go through and add dummy entries for any definitions that - // that we didn't find any references for. + return string.Format( + ServicesVisualStudioNextResources.No_references_found_to_0, + definition.NameDisplayParts.JoinText()); + } - var definitions = GetDefinitionsToCreateMissingReferenceItemsFor(whenGroupingByDefinition); - foreach (var definition in definitions) + private ImmutableArray GetDefinitionsToCreateMissingReferenceItemsFor( + bool whenGroupingByDefinition) + { + lock (_gate) + { + var entries = whenGroupingByDefinition + ? _entriesWhenGroupingByDefinition + : _entriesWhenNotGroupingByDefinition; + + // Find any definitions that we didn't have any references to. But only show + // them if they want to be displayed without any references. This will + // ensure that we still see things like overrides and whatnot, but we + // won't show property-accessors. + var seenDefinitions = entries.Select(r => r.DefinitionBucket.DefinitionItem).ToSet(); + var q = from definition in _definitions + where !seenDefinitions.Contains(definition) && + definition.DisplayIfNoReferences + select definition; + + // If we find at least one of these types of definitions, then just return those. + var result = ImmutableArray.CreateRange(q); + if (result.Length > 0) { - // Create a fake reference to this definition that says - // "no references found to ". - await OnEntryFoundAsync(definition, - bucket => SimpleMessageEntry.CreateAsync( - bucket, GetMessage(bucket.DefinitionItem)), - addToEntriesWhenGroupingByDefinition: whenGroupingByDefinition, - addToEntriesWhenNotGroupingByDefinition: !whenGroupingByDefinition).ConfigureAwait(false); + return result; } - } - private static string GetMessage(DefinitionItem definition) - { - if (definition.IsExternal) + // We found no definitions that *want* to be displayed. However, we still + // want to show something. So, if necessary, show at lest the first definition + // even if we found no references and even if it would prefer to not be seen. + if (entries.Count == 0 && _definitions.Count > 0) { - return ServicesVisualStudioNextResources.External_reference_found; + return ImmutableArray.Create(_definitions.First()); } - return string.Format( - ServicesVisualStudioNextResources.No_references_found_to_0, - definition.NameDisplayParts.JoinText()); + return ImmutableArray.Empty; } + } - private ImmutableArray GetDefinitionsToCreateMissingReferenceItemsFor( - bool whenGroupingByDefinition) + private async Task CreateNoResultsFoundEntryIfNecessaryAsync() + { + bool noDefinitions; + lock (_gate) { - lock (_gate) - { - var entries = whenGroupingByDefinition - ? _entriesWhenGroupingByDefinition - : _entriesWhenNotGroupingByDefinition; - - // Find any definitions that we didn't have any references to. But only show - // them if they want to be displayed without any references. This will - // ensure that we still see things like overrides and whatnot, but we - // won't show property-accessors. - var seenDefinitions = entries.Select(r => r.DefinitionBucket.DefinitionItem).ToSet(); - var q = from definition in _definitions - where !seenDefinitions.Contains(definition) && - definition.DisplayIfNoReferences - select definition; - - // If we find at least one of these types of definitions, then just return those. - var result = ImmutableArray.CreateRange(q); - if (result.Length > 0) - { - return result; - } - - // We found no definitions that *want* to be displayed. However, we still - // want to show something. So, if necessary, show at lest the first definition - // even if we found no references and even if it would prefer to not be seen. - if (entries.Count == 0 && _definitions.Count > 0) - { - return ImmutableArray.Create(_definitions.First()); - } - - return ImmutableArray.Empty; - } + noDefinitions = this._definitions.Count == 0; } - private async Task CreateNoResultsFoundEntryIfNecessaryAsync() + if (noDefinitions) { - bool noDefinitions; - lock (_gate) - { - noDefinitions = this._definitions.Count == 0; - } - - if (noDefinitions) - { - // Create a fake definition/reference called "search found no results" - await OnEntryFoundAsync(NoResultsDefinitionItem, - bucket => SimpleMessageEntry.CreateAsync( - bucket, ServicesVisualStudioNextResources.Search_found_no_results), - addToEntriesWhenGroupingByDefinition: true, - addToEntriesWhenNotGroupingByDefinition: true).ConfigureAwait(false); - } + // Create a fake definition/reference called "search found no results" + await OnEntryFoundAsync(NoResultsDefinitionItem, + bucket => SimpleMessageEntry.CreateAsync( + bucket, ServicesVisualStudioNextResources.Search_found_no_results), + addToEntriesWhenGroupingByDefinition: true, + addToEntriesWhenNotGroupingByDefinition: true).ConfigureAwait(false); } - - private static readonly DefinitionItem NoResultsDefinitionItem = - DefinitionItem.CreateNonNavigableItem( - GlyphTags.GetTags(Glyph.StatusInformation), - ImmutableArray.Create(new TaggedText( - TextTags.Text, - ServicesVisualStudioNextResources.Search_found_no_results))); } + + private static readonly DefinitionItem NoResultsDefinitionItem = + DefinitionItem.CreateNonNavigableItem( + GlyphTags.GetTags(Glyph.StatusInformation), + ImmutableArray.Create(new TaggedText( + TextTags.Text, + ServicesVisualStudioNextResources.Search_found_no_results))); + } } \ No newline at end of file diff --git a/src/VisualStudio/Core/Next/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs b/src/VisualStudio/Core/Next/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs index 3c8c46af96b..53428bf4190 100644 --- a/src/VisualStudio/Core/Next/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs +++ b/src/VisualStudio/Core/Next/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs @@ -9,75 +9,82 @@ namespace Microsoft.VisualStudio.LanguageServices.FindUsages { - internal class WithoutReferencesFindUsagesContext : AbstractTableDataSourceFindUsagesContext + /// + /// Context to be used for FindImplementations/GoToDef (as opposed to FindReferences). + /// This context will not group entries by definition, and will instead just create + /// entries for the definitions themselves. + /// + internal class WithoutReferencesFindUsagesContext : AbstractTableDataSourceFindUsagesContext + { + public WithoutReferencesFindUsagesContext( + StreamingFindUsagesPresenter presenter, + IFindAllReferencesWindow findReferencesWindow) + : base(presenter, findReferencesWindow) { - public WithoutReferencesFindUsagesContext( - StreamingFindUsagesPresenter presenter, - IFindAllReferencesWindow findReferencesWindow) - : base(presenter, findReferencesWindow) - { - } + } - protected override Task OnReferenceFoundWorkerAsync(SourceReferenceItem reference) - => throw new InvalidOperationException(); + // We should never be called in a context where we get references. + protected override Task OnReferenceFoundWorkerAsync(SourceReferenceItem reference) + => throw new InvalidOperationException(); - protected override Task OnCompletedAsyncWorkerAsync() - => SpecializedTasks.EmptyTask; + // Nothing to do on completion. + protected override Task OnCompletedAsyncWorkerAsync() + => SpecializedTasks.EmptyTask; - protected override async Task OnDefinitionFoundWorkerAsync(DefinitionItem definition) - { - var definitionBucket = GetOrCreateDefinitionBucket(definition); + protected override async Task OnDefinitionFoundWorkerAsync(DefinitionItem definition) + { + var definitionBucket = GetOrCreateDefinitionBucket(definition); - var entries = ArrayBuilder.GetInstance(); - try + var entries = ArrayBuilder.GetInstance(); + try + { + if (definition.SourceSpans.Length == 1) { - 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 CreateEntryAsync(definitionBucket, definition).ConfigureAwait(false); + entries.Add(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) { - // 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 CreateEntryAsync(definitionBucket, definition).ConfigureAwait(false); + var entry = await CreateDocumentSpanEntryAsync( + definitionBucket, sourceSpan, isDefinitionLocation: true).ConfigureAwait(false); entries.Add(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 CreateDocumentSpanEntryAsync( - definitionBucket, sourceSpan, isDefinitionLocation: true).ConfigureAwait(false); - entries.Add(entry); - } - } + } - if (entries.Count > 0) + if (entries.Count > 0) + { + lock (_gate) { - lock (_gate) - { - _entriesWhenGroupingByDefinition = _entriesWhenGroupingByDefinition.AddRange(entries); - _entriesWhenNotGroupingByDefinition = _entriesWhenNotGroupingByDefinition.AddRange(entries); - } - - this.NotifyChange(); + _entriesWhenGroupingByDefinition = _entriesWhenGroupingByDefinition.AddRange(entries); + _entriesWhenNotGroupingByDefinition = _entriesWhenNotGroupingByDefinition.AddRange(entries); } - } - finally - { - entries.Free(); + + this.NotifyChange(); } } - - private async Task CreateEntryAsync( - RoslynDefinitionBucket definitionBucket, DefinitionItem definition) + finally { - var documentSpan = definition.SourceSpans[0]; - var (guid, sourceText) = await GetGuidAndSourceTextAsync(documentSpan.Document).ConfigureAwait(false); - - return new DefinitionItemEntry(this, definitionBucket, documentSpan, guid, sourceText); + entries.Free(); } } + + private async Task CreateEntryAsync( + RoslynDefinitionBucket definitionBucket, DefinitionItem definition) + { + var documentSpan = definition.SourceSpans[0]; + var (guid, sourceText) = await GetGuidAndSourceTextAsync(documentSpan.Document).ConfigureAwait(false); + + return new DefinitionItemEntry(this, definitionBucket, documentSpan, guid, sourceText); + } + } } \ No newline at end of file diff --git a/src/VisualStudio/Core/Next/FindReferences/Entries/AbstractDocumentSpanEntry.cs b/src/VisualStudio/Core/Next/FindReferences/Entries/AbstractDocumentSpanEntry.cs index 362a09cfec3..c02db1fadc2 100644 --- a/src/VisualStudio/Core/Next/FindReferences/Entries/AbstractDocumentSpanEntry.cs +++ b/src/VisualStudio/Core/Next/FindReferences/Entries/AbstractDocumentSpanEntry.cs @@ -1,59 +1,86 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Documents; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Shell.TableControl; using Microsoft.VisualStudio.Shell.TableManager; namespace Microsoft.VisualStudio.LanguageServices.FindUsages { - internal abstract class AbstractDocumentSpanEntry : Entry + /// + /// Base type of all s that represent some source location in + /// a . Navigation to that location is provided by this type. + /// Subclasses can be used to provide customized line text to display in the entry. + /// + internal abstract class AbstractDocumentSpanEntry : Entry + { + private readonly AbstractTableDataSourceFindUsagesContext _context; + + private readonly DocumentSpan _documentSpan; + private readonly object _boxedProjectGuid; + protected readonly SourceText _sourceText; + + protected AbstractDocumentSpanEntry( + AbstractTableDataSourceFindUsagesContext context, + RoslynDefinitionBucket definitionBucket, + DocumentSpan documentSpan, + Guid projectGuid, + SourceText sourceText) + : base(definitionBucket) { - private readonly AbstractTableDataSourceFindUsagesContext _context; - - private readonly DocumentSpan _documentSpan; - private readonly object _boxedProjectGuid; - protected readonly SourceText _sourceText; - - protected AbstractDocumentSpanEntry( - AbstractTableDataSourceFindUsagesContext context, - RoslynDefinitionBucket definitionBucket, - DocumentSpan documentSpan, - Guid projectGuid, - SourceText sourceText) - : base(definitionBucket) - { - _context = context; + _context = context; - _documentSpan = documentSpan; - _boxedProjectGuid = projectGuid; - _sourceText = sourceText; - } + _documentSpan = documentSpan; + _boxedProjectGuid = projectGuid; + _sourceText = sourceText; + } - protected StreamingFindUsagesPresenter Presenter => _context.Presenter; + protected StreamingFindUsagesPresenter Presenter => _context.Presenter; - protected Document Document => _documentSpan.Document; - protected TextSpan SourceSpan => _documentSpan.SourceSpan; + protected Document Document => _documentSpan.Document; + protected TextSpan SourceSpan => _documentSpan.SourceSpan; - protected override object GetValueWorker(string keyName) + protected override object GetValueWorker(string keyName) + { + switch (keyName) { - switch (keyName) - { - case StandardTableKeyNames.DocumentName: - return Document.FilePath; - case StandardTableKeyNames.Line: - return _sourceText.Lines.GetLinePosition(SourceSpan.Start).Line; - case StandardTableKeyNames.Column: - return _sourceText.Lines.GetLinePosition(SourceSpan.Start).Character; - case StandardTableKeyNames.ProjectName: - return Document.Project.Name; - case StandardTableKeyNames.ProjectGuid: - return _boxedProjectGuid; - case StandardTableKeyNames.Text: - return _sourceText.Lines.GetLineFromPosition(SourceSpan.Start).ToString().Trim(); - } - - return null; + case StandardTableKeyNames.DocumentName: + return Document.FilePath; + case StandardTableKeyNames.Line: + return _sourceText.Lines.GetLinePosition(SourceSpan.Start).Line; + case StandardTableKeyNames.Column: + return _sourceText.Lines.GetLinePosition(SourceSpan.Start).Character; + case StandardTableKeyNames.ProjectName: + return Document.Project.Name; + case StandardTableKeyNames.ProjectGuid: + return _boxedProjectGuid; + case StandardTableKeyNames.Text: + return _sourceText.Lines.GetLineFromPosition(SourceSpan.Start).ToString().Trim(); } + + return null; } + + public sealed override bool TryCreateColumnContent(string columnName, out FrameworkElement content) + { + if (columnName == StandardTableColumnDefinitions2.LineText) + { + var inlines = CreateLineTextInlines(); + var textBlock = inlines.ToTextBlock(Presenter.TypeMap, wrap: false); + + content = textBlock; + return true; + } + + content = null; + return false; + } + + protected abstract IList CreateLineTextInlines(); + } } \ No newline at end of file diff --git a/src/VisualStudio/Core/Next/FindReferences/Entries/DefinitionItemEntry.cs b/src/VisualStudio/Core/Next/FindReferences/Entries/DefinitionItemEntry.cs index d92a5d8bc3d..f34b4efcc93 100644 --- a/src/VisualStudio/Core/Next/FindReferences/Entries/DefinitionItemEntry.cs +++ b/src/VisualStudio/Core/Next/FindReferences/Entries/DefinitionItemEntry.cs @@ -1,39 +1,32 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Windows; +using System.Collections.Generic; +using System.Windows.Documents; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio.Shell.TableControl; namespace Microsoft.VisualStudio.LanguageServices.FindUsages { - internal class DefinitionItemEntry : AbstractDocumentSpanEntry + /// + /// Shows a DefinitionItem as a Row in the FindReferencesWindow. Only used for + /// GoToDefinition/FindImplementations. In these operations, we don't want to + /// create a DefinitionBucket. So we instead just so the symbol as a normal row. + /// + internal class DefinitionItemEntry : AbstractDocumentSpanEntry + { + public DefinitionItemEntry( + AbstractTableDataSourceFindUsagesContext context, + RoslynDefinitionBucket definitionBucket, + DocumentSpan documentSpan, + Guid projectGuid, + SourceText sourceText) + : base(context, definitionBucket, documentSpan, projectGuid, sourceText) { - public DefinitionItemEntry( - AbstractTableDataSourceFindUsagesContext context, - RoslynDefinitionBucket definitionBucket, - DocumentSpan documentSpan, - Guid projectGuid, - SourceText sourceText) - : base(context, definitionBucket, documentSpan, projectGuid, sourceText) - { - } - - public override bool TryCreateColumnContent(string columnName, out FrameworkElement content) - { - if (columnName == StandardTableColumnDefinitions2.LineText) - { - var inlines = DefinitionBucket.DefinitionItem.DisplayParts.ToInlines(Presenter.TypeMap); - var textBlock = inlines.ToTextBlock(Presenter.TypeMap, wrap: false); - - content = textBlock; - return true; - } - - content = null; - return false; - } } + + protected override IList CreateLineTextInlines() + => DefinitionBucket.DefinitionItem.DisplayParts.ToInlines(Presenter.TypeMap); + } } \ No newline at end of file diff --git a/src/VisualStudio/Core/Next/FindReferences/Entries/DocumentSpanEntry.cs b/src/VisualStudio/Core/Next/FindReferences/Entries/DocumentSpanEntry.cs index 4cc90105669..cec1acbf32b 100644 --- a/src/VisualStudio/Core/Next/FindReferences/Entries/DocumentSpanEntry.cs +++ b/src/VisualStudio/Core/Next/FindReferences/Entries/DocumentSpanEntry.cs @@ -25,7 +25,7 @@ namespace Microsoft.VisualStudio.LanguageServices.FindUsages internal class DocumentSpanEntry : AbstractDocumentSpanEntry { private readonly bool _isDefinitionLocation; - private readonly ClassifiedSpansAndHighlightSpan _classifiedSpans; + private readonly ClassifiedSpansAndHighlightSpan _classifiedSpansAndHighlights; public DocumentSpanEntry( AbstractTableDataSourceFindUsagesContext context, @@ -38,52 +38,31 @@ internal class DocumentSpanEntry : AbstractDocumentSpanEntry : base(context, definitionBucket, documentSpan, projectGuid, sourceText) { _isDefinitionLocation = isDefinitionLocation; - _classifiedSpans = classifiedSpans; + _classifiedSpansAndHighlights = classifiedSpans; } - public override bool TryCreateColumnContent(string columnName, out FrameworkElement content) - { - if (columnName == StandardTableColumnDefinitions2.LineText) - { - var inlines = GetHighlightedInlines(Presenter, _sourceText, _classifiedSpans, _isDefinitionLocation); - var textBlock = inlines.ToTextBlock(Presenter.TypeMap, wrap: false); - - LazyToolTip.AttachTo(textBlock, CreateDisposableToolTip); - - content = textBlock; - return true; - } - - content = null; - return false; - } - - private static IList GetHighlightedInlines( - StreamingFindUsagesPresenter presenter, - SourceText sourceText, - ClassifiedSpansAndHighlightSpan classifiedSpansAndHighlight, - bool isDefinition) - { - var propertyId = isDefinition + protected override IList CreateLineTextInlines() + { + var propertyId = _isDefinitionLocation ? DefinitionHighlightTag.TagId : ReferenceHighlightTag.TagId; - var properties = presenter.FormatMapService + var properties = Presenter.FormatMapService .GetEditorFormatMap("text") .GetProperties(propertyId); var highlightBrush = properties["Background"] as Brush; - var classifiedSpans = classifiedSpansAndHighlight.ClassifiedSpans; + var classifiedSpans = _classifiedSpansAndHighlights.ClassifiedSpans; var classifiedTexts = classifiedSpans.SelectAsArray( - cs => new ClassifiedText(cs.ClassificationType, sourceText.ToString(cs.TextSpan))); + cs => new ClassifiedText(cs.ClassificationType, _sourceText.ToString(cs.TextSpan))); var inlines = classifiedTexts.ToInlines( - presenter.TypeMap, + Presenter.TypeMap, runCallback: (run, classifiedText, position) => { if (highlightBrush != null) { - if (position == classifiedSpansAndHighlight.HighlightSpan.Start) + if (position == _classifiedSpansAndHighlights.HighlightSpan.Start) { run.SetValue( System.Windows.Documents.TextElement.BackgroundProperty, diff --git a/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.cs b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.cs index 9476affb3af..1313a6bfa2a 100644 --- a/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.cs +++ b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.cs @@ -28,16 +28,16 @@ internal partial class StreamingFindUsagesPresenter : private readonly IServiceProvider _serviceProvider; - private readonly IFindAllReferencesService _vsFindAllReferencesService; - private readonly VisualStudioWorkspace _workspace; - public readonly ITextBufferFactoryService TextBufferFactoryService; public readonly IProjectionBufferFactoryService ProjectionBufferFactoryService; public readonly IEditorOptionsFactoryService EditorOptionsFactoryService; public readonly ITextEditorFactoryService TextEditorFactoryService; public readonly IContentTypeRegistryService ContentTypeRegistryService; - public readonly IEditorFormatMapService FormatMapService; public readonly ClassificationTypeMap TypeMap; + public readonly IEditorFormatMapService FormatMapService; + + private readonly IFindAllReferencesService _vsFindAllReferencesService; + private readonly VisualStudioWorkspace _workspace; [ImportingConstructor] public StreamingFindUsagesPresenter( -- GitLab