提交 eb54955d 编写于 作者: C CyrusNajmabadi

Handle hte case where we get no results back at all.

上级 72e28bf1
......@@ -39,12 +39,15 @@ public ProgressAdapter(Solution solution, FindReferencesContext context)
_definitionFactory = s => s.ToDefinitionItem(solution);
}
// Do nothing functions. The streaming far service doesn't care about
// any of these.
public void OnStarted() { }
public void OnCompleted() { }
public void OnFindInDocumentStarted(Document document) { }
public void OnFindInDocumentCompleted(Document document) { }
// Simple context forwarding functions.
public void OnStarted() => _context.OnStarted();
public void OnCompleted() => _context.OnCompleted();
public void ReportProgress(int current, int maximum) => _context.ReportProgress(current, maximum);
public void OnFindInDocumentStarted(Document document) => _context.OnFindInDocumentStarted(document);
public void OnFindInDocumentCompleted(Document document) => _context.OnFindInDocumentCompleted(document);
// More complicated forwarding functions. These need to map from the symbols
// used by the FAR engine to the INavigableItems used by the streaming FAR
......
......@@ -8,6 +8,7 @@
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.FindReferences;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Shared.TestHooks;
......@@ -103,12 +104,25 @@ private bool TryExecuteCommand(int caretPosition, Document document)
Document document, IStreamingFindReferencesService service,
IStreamingFindReferencesPresenter presenter, int caretPosition)
{
using (var token = _asyncListener.BeginAsyncOperation(nameof(StreamingFindReferences)))
try
{
using (var token = _asyncListener.BeginAsyncOperation(nameof(StreamingFindReferences)))
{
// 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();
await service.FindReferencesAsync(document, caretPosition, context).ConfigureAwait(false);
// Note: we don't need to put this in a finally. The only time we might not hit
// this is if cancellation or another error gets thrown. In the former case,
// that means that a new search has started. We don't care about telling the
// context it has completed. In the latter case somethign wrong has happened
// and we don't want to run any more code code in this particular context.
context.OnCompleted();
}
}
catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
{
// 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();
await service.FindReferencesAsync(document, caretPosition, context).ConfigureAwait(false);
}
}
......
......@@ -18,22 +18,10 @@ public virtual void SetSearchLabel(string displayName)
{
}
public virtual void OnStarted()
{
}
public virtual void OnCompleted()
{
}
public virtual void OnFindInDocumentStarted(Document document)
{
}
public virtual void OnFindInDocumentCompleted(Document document)
{
}
public virtual void OnDefinitionFound(DefinitionItem definition)
{
}
......
// 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.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Projection;
using Microsoft.VisualStudio.Utilities;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo
{
......
......@@ -8,6 +8,10 @@
namespace Microsoft.CodeAnalysis.Editor.Implementation.Preview
{
/// <summary>
/// Special tagger we use for previews that is told precisely which spans to
/// reference highlight.
/// </summary>
[Export(typeof(ITaggerProvider))]
[TagType(typeof(NavigableHighlightTag))]
[ContentType(ContentTypeNames.RoslynContentType)]
......
......@@ -383,6 +383,9 @@ public static ImageMoniker GetImageMoniker(this Glyph glyph)
case Glyph.CompletionWarning:
return KnownMonikers.IntellisenseWarning;
case Glyph.StatusInformation:
return KnownMonikers.StatusInformation;
case Glyph.NuGet:
return KnownMonikers.NuGet;
......@@ -629,6 +632,9 @@ public static Glyph GetGlyph(this ImmutableArray<string> tags)
case CompletionTags.Warning:
return Glyph.CompletionWarning;
case CompletionTags.StatusInformation:
return Glyph.StatusInformation;
}
}
......
......@@ -35,6 +35,7 @@ internal enum Glyph
EnumMember,
Error,
StatusInformation,
EventPublic,
EventProtected,
......
......@@ -50,6 +50,7 @@ public static class CompletionTags
public const string Snippet = nameof(Snippet);
public const string Error = nameof(Error);
public const string Warning = nameof(Warning);
internal const string StatusInformation = nameof(StatusInformation);
// Currently needed, but removed from Dev15. Internal so no one accidently takes a
// dependency on them.
......
......@@ -146,6 +146,8 @@ public static ImmutableArray<string> GetTags(Glyph glyph)
return Snippet;
case Glyph.CompletionWarning:
return Warning;
case Glyph.StatusInformation:
return StatusInformation;
default:
return ImmutableArray<string>.Empty;
}
......@@ -216,6 +218,7 @@ public static ImmutableArray<string> GetTags(Glyph glyph)
private static readonly ImmutableArray<string> Error = ImmutableArray.Create(CompletionTags.Error);
private static readonly ImmutableArray<string> Warning = ImmutableArray.Create(CompletionTags.Warning);
private static readonly ImmutableArray<string> StatusInformation = ImmutableArray.Create(CompletionTags.StatusInformation);
private static readonly ImmutableArray<string> CSharpFile = ImmutableArray.Create(CompletionTags.File, LanguageNames.CSharp);
private static readonly ImmutableArray<string> VisualBasicFile = ImmutableArray.Create(CompletionTags.File, LanguageNames.VisualBasic);
......
using Microsoft.CodeAnalysis;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.Shell.TableManager;
namespace Microsoft.VisualStudio.LanguageServices.FindReferences
{
internal partial class StreamingFindReferencesPresenter
{
private class NoneFoundReferenceEntry : ReferenceEntry
private class SimpleMessageReferenceEntry : ReferenceEntry
{
public NoneFoundReferenceEntry(RoslynDefinitionBucket definitionBucket)
private readonly string _message;
private SimpleMessageReferenceEntry(
RoslynDefinitionBucket definitionBucket,
string message)
: base(definitionBucket)
{
_message = message;
}
public static Task<ReferenceEntry> CreateAsync(
RoslynDefinitionBucket definitionBucket,
string message)
{
var referenceEntry = new SimpleMessageReferenceEntry(definitionBucket, message);
return Task.FromResult<ReferenceEntry>(referenceEntry);
}
protected override object GetValueWorker(string keyName)
......@@ -17,7 +31,7 @@ protected override object GetValueWorker(string keyName)
switch (keyName)
{
case StandardTableKeyNames.Text:
return $"No references found to '{DefinitionBucket.DefinitionItem.DisplayParts.JoinText()}'";
return _message;
}
return null;
......
// 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.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
......@@ -17,9 +16,9 @@
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.Shell.FindAllReferences;
using Microsoft.VisualStudio.Shell.TableManager;
using Microsoft.CodeAnalysis.Editor.Shared.Preview;
using Microsoft.VisualStudio.Text;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.Completion;
using System.Diagnostics;
namespace Microsoft.VisualStudio.LanguageServices.FindReferences
{
......@@ -30,7 +29,7 @@ private class TableDataSourceFindReferencesContext :
{
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private readonly ConcurrentBag<Subscription> _subscriptions = new ConcurrentBag<Subscription>();
private ITableDataSink _tableDataSink;
public readonly StreamingFindReferencesPresenter Presenter;
private readonly IFindAllReferencesWindow _findReferencesWindow;
......@@ -76,6 +75,10 @@ private class TableDataSourceFindReferencesContext :
// And add ourselves as the source of results for the window.
findReferencesWindow.Manager.AddSource(this);
// After adding us as the source, the manager should immediately call into us to
// tell us what the data sink is.
Debug.Assert(_tableDataSink != null);
}
private void CancelSearch()
......@@ -101,12 +104,15 @@ internal void OnSubscriptionDisposed()
public IDisposable Subscribe(ITableDataSink sink)
{
var subscription = new Subscription(this, sink);
_subscriptions.Add(subscription);
Presenter.AssertIsForeground();
sink.AddFactory(this, removeAllFactories: true);
Debug.Assert(_tableDataSink == null);
_tableDataSink = sink;
return subscription;
_tableDataSink.AddFactory(this, removeAllFactories: true);
_tableDataSink.IsStable = false;
return this;
}
#endregion
......@@ -122,25 +128,41 @@ public override void SetSearchLabel(string displayName)
}
}
public override void OnStarted()
public override void OnCompleted()
{
foreach (var subscription in _subscriptions)
{
subscription.TableDataSink.IsStable = false;
}
// Now that we know the search is over, create and display any error messages
// for definitions that were not found.
CreateMissingReferenceEntriesIfNecessary();
CreateNoResultsFoundEntryIfNecessary();
_tableDataSink.IsStable = true;
}
public override void OnCompleted()
private void CreateNoResultsFoundEntryIfNecessary()
{
CreateMissingReferenceEntries();
bool noDefinitions;
lock(_gate)
{
noDefinitions = this._definitions.Count == 0;
}
foreach (var subscription in _subscriptions)
if (noDefinitions)
{
subscription.TableDataSink.IsStable = true;
// Create a fake definition/reference called "search found no results"
this.OnReferenceFound(NoResultsDefinitionItem,
(db, c) => SimpleMessageReferenceEntry.CreateAsync(
db, ServicesVisualStudioNextResources.Search_found_no_results));
}
}
private void CreateMissingReferenceEntries()
private static DefinitionItem NoResultsDefinitionItem =
DefinitionItem.CreateNonNavigableItem(
GlyphTags.GetTags(Glyph.StatusInformation),
ImmutableArray.Create(new TaggedText(
TextTags.Text,
ServicesVisualStudioNextResources.Search_found_no_results)));
private void CreateMissingReferenceEntriesIfNecessary()
{
// Go through and add dummy entries for any definitions that
// that we didn't find any references for.
......@@ -148,8 +170,14 @@ private void CreateMissingReferenceEntries()
var definitions = GetDefinitionsToCreateMissingReferenceItemsFor();
foreach (var definition in definitions)
{
// Create a fake reference to this definition that says
// "no references found to <symbolname>".
var message = string.Format(
ServicesVisualStudioNextResources.No_references_found_to_0,
definition.DisplayParts.JoinText());
OnReferenceFound(definition,
(db, c) => Task.FromResult<ReferenceEntry>(new NoneFoundReferenceEntry(db)));
(db, c) => SimpleMessageReferenceEntry.CreateAsync(
db, message));
}
}
......@@ -234,7 +262,7 @@ public override void OnReferenceFound(SourceReferenceItem reference)
}
// Let all our subscriptions know that we've updated.
NotifySinksOfChangedVersion();
_tableDataSink.FactorySnapshotChanged(this);
}
private async Task<ReferenceEntry> CreateReferenceEntryAsync(
......@@ -323,14 +351,6 @@ private RoslynDefinitionBucket GetOrCreateDefinitionBucket(DefinitionItem defini
}
}
private void NotifySinksOfChangedVersion()
{
foreach (var subscription in _subscriptions)
{
subscription.TableDataSink.FactorySnapshotChanged(this);
}
}
public override void ReportProgress(int current, int maximum)
{
//var progress = maximum == 0 ? 0 : ((double)current / maximum);
......@@ -374,7 +394,7 @@ public ITableEntriesSnapshot GetSnapshot(int versionNumber)
// We didn't have this version. Notify the sinks that something must have changed
// so that they call back into us with the latest version.
NotifySinksOfChangedVersion();
_tableDataSink.FactorySnapshotChanged(this);
return null;
}
......
......@@ -30,7 +30,7 @@
<Compile Include="FindReferences\StreamingFindReferencesPresenter.LazyToolTip.cs" />
<Compile Include="FindReferences\StreamingFindReferencesPresenter.cs" />
<Compile Include="FindReferences\StreamingFindReferencesPresenter.DocumentLocationReferenceEntry.cs" />
<Compile Include="FindReferences\StreamingFindReferencesPresenter.NoneFoundReferenceEntry.cs" />
<Compile Include="FindReferences\StreamingFindReferencesPresenter.SimpleMessageReferenceEntry.cs" />
<Compile Include="FindReferences\StreamingFindReferencesPresenter.RoslynDefinitionBucket.cs" />
<Compile Include="FindReferences\StreamingFindReferencesPresenter.TableDataSourceFindReferencesContext.cs" />
<Compile Include="FindReferences\StreamingFindReferencesPresenter.Subscription.cs" />
......@@ -38,6 +38,11 @@
<Compile Include="FindReferences\StreamingFindReferencesPresenter.ReferenceEntry.cs" />
<Compile Include="FindReferences\FindReferencesTableControlEventProcessorProvider.cs" />
<Compile Include="FindReferences\TaggedTextAndHighlightSpan.cs" />
<Compile Include="ServicesVisualStudioNextResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>ServicesVisualStudioNextResources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\..\..\Compilers\Core\Portable\CodeAnalysis.csproj">
......@@ -138,6 +143,12 @@
<PublicAPI Include="PublicAPI.Shipped.txt" />
<PublicAPI Include="PublicAPI.Unshipped.txt" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ServicesVisualStudioNextResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>ServicesVisualStudioNextResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ImportGroup Label="Targets">
<Import Project="..\..\..\..\build\Targets\VSL.Imports.targets" />
</ImportGroup>
......
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Microsoft.VisualStudio.LanguageServices {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class ServicesVisualStudioNextResources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal ServicesVisualStudioNextResources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.VisualStudio.LanguageServices.ServicesVisualStudioNextResources", typeof(ServicesVisualStudioNextResources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to No references found to &apos;{0}&apos;.
/// </summary>
internal static string No_references_found_to_0 {
get {
return ResourceManager.GetString("No_references_found_to_0", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Search found no results.
/// </summary>
internal static string Search_found_no_results {
get {
return ResourceManager.GetString("Search_found_no_results", resourceCulture);
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="No_references_found_to_0" xml:space="preserve">
<value>No references found to '{0}'</value>
</data>
<data name="Search_found_no_results" xml:space="preserve">
<value>Search found no results</value>
</data>
</root>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册