提交 b99ebd93 编写于 作者: C CyrusNajmabadi

Merge remote-tracking branch 'upstream/master' into findRefsNavigation

......@@ -4066,7 +4066,7 @@ public class Test
// TODO...
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/18800")]
[Fact]
public void CS0306ERR_BadTypeArgument01()
{
var source =
......
......@@ -1552,5 +1552,38 @@ public override void M2<T>(T? i = null)
}
}");
}
[WorkItem(13932, "https://github.com/dotnet/roslyn/issues/13932")]
[WorkItem(5898, "https://github.com/dotnet/roslyn/issues/5898")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public async Task TestAutoProperties()
{
await TestInRegularAndScript1Async(
@"abstract class AbstractClass
{
public abstract int ReadOnlyProp { get; }
public abstract int ReadWriteProp { get; set; }
public abstract int WriteOnlyProp { set; }
}
class [|C|] : AbstractClass
{
}",
@"abstract class AbstractClass
{
public abstract int ReadOnlyProp { get; }
public abstract int ReadWriteProp { get; set; }
public abstract int WriteOnlyProp { set; }
}
class C : AbstractClass
{
public override int ReadOnlyProp { get; }
public override int ReadWriteProp { get; set; }
public override int WriteOnlyProp { set => throw new System.NotImplementedException(); }
}", parameters: new TestParameters(options: Option(
ImplementTypeOptions.PropertyGenerationBehavior,
ImplementTypePropertyGenerationBehavior.PreferAutoProperties)));
}
}
}
\ No newline at end of file
......@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.ImplementInterface;
......@@ -6705,5 +6704,40 @@ class C : I
}
}");
}
[WorkItem(13932, "https://github.com/dotnet/roslyn/issues/13932")]
[WorkItem(5898, "https://github.com/dotnet/roslyn/issues/5898")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public async Task TestAutoProperties()
{
await TestInRegularAndScript1Async(
@"interface IInterface
{
int ReadOnlyProp { get; }
int ReadWriteProp { get; set; }
int WriteOnlyProp { set; }
}
class Class : [|IInterface|]
{
}",
@"interface IInterface
{
int ReadOnlyProp { get; }
int ReadWriteProp { get; set; }
int WriteOnlyProp { set; }
}
class Class : IInterface
{
public int ReadOnlyProp { get; }
public int ReadWriteProp { get; set; }
public int WriteOnlyProp { set => throw new System.NotImplementedException(); }
}", parameters: new TestParameters(options: Option(
ImplementTypeOptions.PropertyGenerationBehavior,
ImplementTypePropertyGenerationBehavior.PreferAutoProperties)));
}
}
}
\ No newline at end of file
......@@ -77,7 +77,7 @@ internal static class GoToDefinitionHelpers
FindUsagesHelpers.GetDisplayName(symbol));
return presenter.TryNavigateToOrPresentItemsAsync(
title, definitions.ToImmutableAndFree()).WaitAndGetResult(cancellationToken);
project.Solution.Workspace, title, definitions.ToImmutableAndFree()).WaitAndGetResult(cancellationToken);
}
private static IStreamingFindUsagesPresenter GetFindUsagesPresenter(
......
......@@ -136,7 +136,7 @@ public void ExecuteCommand(GoToImplementationCommandArgs args, Action nextHandle
var definitionItems = goToImplContext.GetDefinitions();
streamingPresenter.TryNavigateToOrPresentItemsAsync(
goToImplContext.SearchTitle, definitionItems).Wait(cancellationToken);
document.Project.Solution.Workspace, goToImplContext.SearchTitle, definitionItems).Wait(cancellationToken);
}
private IStreamingFindUsagesPresenter GetStreamingPresenter()
......
......@@ -43,17 +43,17 @@ internal static class IStreamingFindUsagesPresenterExtensions
/// </summary>
public static async Task<bool> TryNavigateToOrPresentItemsAsync(
this IStreamingFindUsagesPresenter presenter,
string title, ImmutableArray<DefinitionItem> items)
Workspace workspace, string title, ImmutableArray<DefinitionItem> items)
{
// Ignore any definitions that we can't navigate to.
var definitions = items.WhereAsArray(d => d.CanNavigateTo());
var definitions = items.WhereAsArray(d => d.CanNavigateTo(workspace));
// See if there's a third party external item we can navigate to. If so, defer
// to that item and finish.
var externalItems = definitions.WhereAsArray(d => d.IsExternal);
foreach (var item in externalItems)
{
if (item.TryNavigateTo(isPreview: true))
if (item.TryNavigateTo(workspace, isPreview: true))
{
return true;
}
......@@ -69,7 +69,7 @@ internal static class IStreamingFindUsagesPresenterExtensions
nonExternalItems[0].SourceSpans.Length <= 1)
{
// There was only one location to navigate to. Just directly go to that location.
return nonExternalItems[0].TryNavigateTo(isPreview: true);
return nonExternalItems[0].TryNavigateTo(workspace, isPreview: true);
}
if (presenter != null)
......
......@@ -182,6 +182,8 @@ private void VerifyBreakIntoCharacterParts(string original, params string[] part
[InlineData("[|Fog|]Bar", "fog", PatternMatchKind.Prefix, CaseInsensitive)]
[InlineData("[|fog|]BarFoo", "Fog", PatternMatchKind.Prefix, CaseInsensitive)]
[InlineData("[|system.ref|]lection", "system.ref", PatternMatchKind.Prefix, CaseSensitive)]
[InlineData("Fog[|B|]ar", "b", PatternMatchKind.Substring, CaseInsensitive)]
[InlineData("_[|my|]Button", "my", PatternMatchKind.Substring, CaseSensitive)]
......@@ -234,10 +236,10 @@ private void VerifyBreakIntoCharacterParts(string original, params string[] part
[InlineData("my[|_b|]utton", "_B", PatternMatchKind.CamelCase, CaseInsensitive, PatternMatcher.CamelCaseContiguousBonus)]
[InlineData("[|_|]my_[|b|]utton", "_B", PatternMatchKind.CamelCase, CaseInsensitive, PatternMatcher.CamelCaseMatchesFromStartBonus)]
public void TryMatchSingleWordPattern(
public void TestNonFuzzyMatch(
string candidate, string pattern, int matchKindInt, bool isCaseSensitive, int? camelCaseWeight = null)
{
var match = TryMatchSingleWordPattern(candidate, pattern);
var match = TestNonFuzzyMatch(candidate, pattern);
Assert.NotNull(match);
var matchKind = (PatternMatchKind)matchKindInt;
......@@ -269,9 +271,10 @@ private void VerifyBreakIntoCharacterParts(string original, params string[] part
[InlineData("FogBarBaz", "FZ")]
[InlineData("_mybutton", "myB")]
[InlineData("FogBarChangedEventArgs", "changedeventarrrgh")]
public void TryMatchSingleWordPattern_NoMatch(string candidate, string pattern)
[InlineData("runtime.native.system", "system.reflection")]
public void TestNonFuzzyMatch_NoMatch(string candidate, string pattern)
{
var match = TryMatchSingleWordPattern(candidate, pattern);
var match = TestNonFuzzyMatch(candidate, pattern);
Assert.Null(match);
}
......@@ -470,7 +473,7 @@ public void TryMatchSingleWordPattern_CultureAwareSingleWordPreferCaseSensitiveE
try
{
var match = TryMatchSingleWordPattern("[|ioo|]", "\u0130oo"); // u0130 = Capital I with dot
var match = TestNonFuzzyMatch("[|ioo|]", "\u0130oo"); // u0130 = Capital I with dot
Assert.Equal(PatternMatchKind.Exact, match.Value.Kind);
Assert.False(match.Value.IsCaseSensitive);
......@@ -499,11 +502,15 @@ private static IList<string> BreakIntoCharacterParts(string identifier)
private static IList<string> BreakIntoWordParts(string identifier)
=> PartListToSubstrings(identifier, StringBreaker.BreakIntoWordParts(identifier));
private static PatternMatch? TryMatchSingleWordPattern(string candidate, string pattern)
private static PatternMatch? TestNonFuzzyMatch(string candidate, string pattern)
{
MarkupTestFile.GetSpans(candidate, out candidate, out ImmutableArray<TextSpan> spans);
var match = new PatternMatcher(pattern).MatchSingleWordPattern_ForTestingOnly(candidate);
var match = new PatternMatcher(pattern).GetFirstMatch(candidate, includeMatchSpans: true);
if (match?.Kind == PatternMatchKind.Fuzzy)
{
match = null;
}
if (match == null)
{
......@@ -536,4 +543,4 @@ private static IEnumerable<PatternMatch> TryMatchMultiWordPattern(string candida
}
}
}
}
}
\ No newline at end of file
......@@ -3,6 +3,7 @@
Imports System.Globalization
Imports Microsoft.CodeAnalysis.Completion
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Text
Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
' These tests adapted from David Kean's table at
......@@ -10,7 +11,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
Public Class CompletionRulesTests
<Fact>
Public Sub TestMatchLowerCaseEnglishI()
Dim wordsToMatch = {"index", "Index", "işte", şte"}
Dim wordsToMatch = {"[|i|]ndex", "[|I|]ndex", "[|i|]şte", "[|İ|]şte"}
Dim wordsToNotMatch = {"ırak"}
TestMatches("i", wordsToMatch)
......@@ -19,7 +20,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
<Fact>
Public Sub TestMatchDottedUpperTurkishI()
Dim wordsToMatch = {"index", "işte", şte"}
Dim wordsToMatch = {"[|i|]ndex", "[|i|]şte", "[|İ|]şte"}
Dim wordsToNotMatch = {"ırak", "Irak", "Index"}
TestMatches("İ", wordsToMatch)
......@@ -28,7 +29,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
<Fact>
Public Sub TestMatchNonDottedLowerTurkishI()
Dim wordsToMatch = {"ırak", "Irak"}
Dim wordsToMatch = {"[|ı|]rak", "[|I|]rak"}
Dim wordsToNotMatch = {"index", "işte", "İşte"}
TestMatches("ı", wordsToMatch)
......@@ -37,31 +38,48 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
<Fact>
Public Sub TestMatchEnglishUpperI()
Dim wordsToMatch = {"Index", "index", "ırak", "Irak"}
' In turkish-culture "I" will not match "index". However, we want to verify that
' the underlying completion helper will fallback to doing an en-us check if the
' tr-tr check fails, and that it properly also returns the matched spans in this case.
Dim wordsToMatch = {"[|I|]ndex", "[|i|]ndex", "[|ı|]rak", "[|I|]rak"}
Dim wordsToNotMatch = {"İşte"}
TestMatches("I", wordsToMatch)
TestNotMatches("I", wordsToNotMatch)
End Sub
Private Sub TestMatches(v As String, wordsToMatch() As String)
Private Sub TestMatches(pattern As String, wordsToMatch() As String)
Dim culture = New CultureInfo("tr-TR", useUserOverride:=False)
Dim workspace = New TestWorkspace
Dim helper = CompletionHelper.GetHelper(workspace, LanguageNames.CSharp)
For Each word In wordsToMatch
For Each wordMarkup In wordsToMatch
Dim word As String = Nothing
Dim wordMatchSpan As TextSpan = Nothing
MarkupTestFile.GetSpan(wordMarkup, word, wordMatchSpan)
Dim item = CompletionItem.Create(word)
Assert.True(helper.MatchesPattern(item.FilterText, v, culture), $"Expected item {word} does not match {v}")
Assert.True(helper.MatchesPattern(item.FilterText, pattern, culture), $"Expected item {word} does not match {pattern}")
Dim highlightedSpans = helper.GetHighlightedSpans(item.FilterText, pattern, culture)
Assert.NotEmpty(highlightedSpans)
Assert.Equal(1, highlightedSpans.Length)
Assert.Equal(wordMatchSpan, highlightedSpans(0))
Next
End Sub
Private Sub TestNotMatches(v As String, wordsToNotMatch() As String)
Private Sub TestNotMatches(pattern As String, wordsToNotMatch() As String)
Dim culture = New CultureInfo("tr-TR", useUserOverride:=False)
Dim workspace = New TestWorkspace
Dim helper = CompletionHelper.GetHelper(workspace, LanguageNames.CSharp)
For Each word In wordsToNotMatch
Dim item = CompletionItem.Create(word)
Assert.False(helper.MatchesPattern(item.FilterText, v, culture), $"Unexpected item {word} matches {v}")
Assert.False(helper.MatchesPattern(item.FilterText, pattern, culture), $"Unexpected item {word} matches {pattern}")
Dim highlightedSpans = helper.GetHighlightedSpans(item.FilterText, pattern, culture)
Assert.Empty(highlightedSpans)
Next
End Sub
End Class
......
......@@ -3,6 +3,7 @@
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics
Imports Microsoft.CodeAnalysis.ImplementType
Imports Microsoft.CodeAnalysis.VisualBasic.ImplementAbstractClass
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ImplementAbstractClass
......@@ -574,5 +575,42 @@ Class x
End Function
End Class")
End Function
<WorkItem(13932, "https://github.com/dotnet/roslyn/issues/13932")>
<WorkItem(5898, "https://github.com/dotnet/roslyn/issues/5898")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)>
Public Async Function TestAutoProperties() As Task
Await TestInRegularAndScript1Async(
"MustInherit Class AbstractClass
MustOverride ReadOnly Property ReadOnlyProp As Integer
MustOverride Property ReadWriteProp As Integer
MustOverride WriteOnly Property WriteOnlyProp As Integer
End Class
Class [|C|]
Inherits AbstractClass
End Class",
"MustInherit Class AbstractClass
MustOverride ReadOnly Property ReadOnlyProp As Integer
MustOverride Property ReadWriteProp As Integer
MustOverride WriteOnly Property WriteOnlyProp As Integer
End Class
Class C
Inherits AbstractClass
Public Overrides ReadOnly Property ReadOnlyProp As Integer
Public Overrides Property ReadWriteProp As Integer
Public Overrides WriteOnly Property WriteOnlyProp As Integer
Set(value As Integer)
Throw New System.NotImplementedException()
End Set
End Property
End Class", parameters:=New TestParameters(options:=[Option](
ImplementTypeOptions.PropertyGenerationBehavior,
ImplementTypePropertyGenerationBehavior.PreferAutoProperties)))
End Function
End Class
End Namespace
\ No newline at end of file
......@@ -3,6 +3,7 @@
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics
Imports Microsoft.CodeAnalysis.ImplementType
Imports Microsoft.CodeAnalysis.VisualBasic.ImplementInterface
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ImplementInterface
......@@ -4518,5 +4519,41 @@ Namespace System
End Structure
End Namespace")
End Function
<WorkItem(13932, "https://github.com/dotnet/roslyn/issues/13932")>
<WorkItem(5898, "https://github.com/dotnet/roslyn/issues/5898")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)>
Public Async Function TestAutoProperties() As Task
Await TestInRegularAndScript1Async(
"interface IInterface
readonly property ReadOnlyProp as integer
property ReadWriteProp as integer
writeonly property WriteOnlyProp as integer
end interface
class Class
implements [|IInterface|]
end class",
"interface IInterface
readonly property ReadOnlyProp as integer
property ReadWriteProp as integer
writeonly property WriteOnlyProp as integer
end interface
class Class
implements IInterface
Public ReadOnly Property ReadOnlyProp As Integer Implements IInterface.ReadOnlyProp
Public Property ReadWriteProp As Integer Implements IInterface.ReadWriteProp
Public WriteOnly Property WriteOnlyProp As Integer Implements IInterface.WriteOnlyProp
Set(value As Integer)
Throw New System.NotImplementedException()
End Set
End Property
end class", parameters:=New TestParameters(options:=[Option](
ImplementTypeOptions.PropertyGenerationBehavior,
ImplementTypePropertyGenerationBehavior.PreferAutoProperties)))
End Function
End Class
End Namespace
\ No newline at end of file
......@@ -112,7 +112,7 @@ public bool MatchesPattern(string text, string pattern, CultureInfo culture)
if (!culture.Equals(EnUSCultureInfo))
{
patternMatcher = this.GetPatternMatcher(pattern, EnUSCultureInfo);
match = patternMatcher.GetFirstMatch(completionItemText);
match = patternMatcher.GetFirstMatch(completionItemText, includeMatchSpans);
if (match != null)
{
return match;
......
......@@ -396,8 +396,6 @@
<Compile Include="DocumentSpan.cs" />
<Compile Include="FindUsages\DefinitionItem.cs" />
<Compile Include="FindUsages\DefinitionItem.DocumentLocationDefinitionItem.cs" />
<Compile Include="FindUsages\DefinitionItem.NonNavigatingDefinitionItem.cs" />
<Compile Include="FindUsages\DefinitionItem.SymbolDefinitionItem.cs" />
<Compile Include="FindUsages\DefinitionsAndReferences.cs" />
<Compile Include="FindUsages\SourceReferenceItem.cs" />
<Compile Include="Diagnostics\EngineV2\DiagnosticAnalyzerExecutor.cs" />
......
// 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.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Navigation;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindUsages
{
......@@ -11,25 +17,111 @@ internal partial class DefinitionItem
/// <see cref="DocumentSpan"/>.
/// </summary>
// internal for testing purposes.
internal sealed class DocumentLocationDefinitionItem : DefinitionItem
internal sealed class DefaultDefinitionItem : DefinitionItem
{
internal override bool IsExternal => false;
public DocumentLocationDefinitionItem(
public DefaultDefinitionItem(
ImmutableArray<string> tags,
ImmutableArray<TaggedText> displayParts,
ImmutableArray<TaggedText> nameDisplayParts,
ImmutableArray<TaggedText> originationParts,
ImmutableArray<DocumentSpan> sourceSpans,
ImmutableDictionary<string, string> properties,
bool displayIfNoReferences)
: base(tags, displayParts, nameDisplayParts,
ImmutableArray.Create(new TaggedText(TextTags.Text, sourceSpans[0].Document.Project.Name)),
: base(tags, displayParts, nameDisplayParts, originationParts,
sourceSpans, properties, displayIfNoReferences)
{
}
public override bool CanNavigateTo() => SourceSpans[0].CanNavigateTo();
public override bool TryNavigateTo(bool isPreview) => SourceSpans[0].TryNavigateTo(isPreview);
public override bool CanNavigateTo(Workspace workspace)
{
if (this.Properties.ContainsKey(NonNavigable))
{
return false;
}
if (this.Properties.TryGetValue(MetadataSymbolKey, out var symbolKey))
{
return CanNavigateToMetadataSymbol(workspace, symbolKey);
}
return SourceSpans[0].CanNavigateTo();
}
public override bool TryNavigateTo(Workspace workspace, bool isPreview)
{
if (this.Properties.ContainsKey(NonNavigable))
{
return false;
}
if (this.Properties.TryGetValue(MetadataSymbolKey, out var symbolKey))
{
return TryNavigateToMetadataSymbol(workspace, symbolKey);
}
return SourceSpans[0].TryNavigateTo(isPreview);
}
private bool CanNavigateToMetadataSymbol(Workspace workspace, string symbolKey)
=> TryNavigateToMetadataSymbol(workspace, symbolKey, action: (symbol, project, service) => true);
private bool TryNavigateToMetadataSymbol(Workspace workspace, string symbolKey)
{
return TryNavigateToMetadataSymbol(workspace, symbolKey,
action: (symbol, project, service) =>
{
return service.TryNavigateToSymbol(
symbol, project, project.Solution.Options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true));
});
}
private bool TryNavigateToMetadataSymbol(
Workspace workspace, string symbolKey, Func<ISymbol, Project, ISymbolNavigationService, bool> action)
{
var projectAndSymbol = TryResolveSymbolInCurrentSolution(workspace, symbolKey);
var project = projectAndSymbol.project;
var symbol = projectAndSymbol.symbol;
if (symbol == null || project == null)
{
return false;
}
if (symbol.Kind == SymbolKind.Namespace)
{
return false;
}
var navigationService = workspace.Services.GetService<ISymbolNavigationService>();
return action(symbol, project, navigationService);
}
private (Project project, ISymbol symbol) TryResolveSymbolInCurrentSolution(
Workspace workspace, string symbolKey)
{
if (!this.Properties.TryGetValue(MetadataAssemblyIdentityDisplayName, out var identityDisplayName) ||
!AssemblyIdentity.TryParseDisplayName(identityDisplayName, out var identity))
{
return (null, null);
}
var project = workspace.CurrentSolution
.ProjectsWithReferenceToAssembly(identity)
.FirstOrDefault();
if (project == null)
{
return (null, null);
}
var compilation = project.GetCompilationAsync(CancellationToken.None)
.WaitAndGetResult(CancellationToken.None);
var symbol = SymbolKey.Resolve(symbolKey, compilation).Symbol;
return (project, symbol);
}
}
}
}
\ No newline at end of file
// 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.Collections.Immutable;
namespace Microsoft.CodeAnalysis.FindUsages
{
internal partial class DefinitionItem
{
/// <summary>
/// Implementation of a <see cref="DefinitionItem"/> used for definitions
/// that cannot be navigated to. For example, C# and VB namespaces cannot be
/// navigated to.
/// </summary>
private sealed class NonNavigatingDefinitionItem : DefinitionItem
{
internal override bool IsExternal => false;
public NonNavigatingDefinitionItem(
ImmutableArray<string> tags,
ImmutableArray<TaggedText> displayParts,
ImmutableArray<TaggedText> originationParts,
ImmutableDictionary<string, string> properties,
bool displayIfNoReferences)
: base(tags, displayParts, ImmutableArray<TaggedText>.Empty,
originationParts, ImmutableArray<DocumentSpan>.Empty,
properties, displayIfNoReferences)
{
}
public override bool CanNavigateTo() => false;
public override bool TryNavigateTo(bool isPreview) => false;
}
}
}
\ No newline at end of file
// 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.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Navigation;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindUsages
{
internal partial class DefinitionItem
{
/// <summary>
/// Implementation of a <see cref="DefinitionItem"/> that sits on top of an
/// <see cref="ISymbol"/>. In order to not keep anything alive too long, we only
/// hold onto IDs and Keys. When the user tries to navigate to an item we will
/// attempt to find the symbol again in the current solution snapshot and
/// navigate to it there.
/// </summary>
private sealed class MetadataDefinitionItem : DefinitionItem
{
private readonly Workspace _workspace;
private readonly SymbolKey _symbolKey;
private readonly AssemblyIdentity _symbolAssemblyIdentity;
internal override bool IsExternal => false;
public MetadataDefinitionItem(
ImmutableArray<string> tags,
ImmutableArray<TaggedText> displayParts,
ImmutableArray<TaggedText> nameDisplayParts,
ImmutableDictionary<string, string> properties,
bool displayIfNoReferences,
Solution solution, ISymbol definition)
: base(tags, displayParts, nameDisplayParts,
GetOriginationParts(definition),
ImmutableArray<DocumentSpan>.Empty,
properties,
displayIfNoReferences)
{
_workspace = solution.Workspace;
_symbolKey = definition.GetSymbolKey();
_symbolAssemblyIdentity = definition.ContainingAssembly?.Identity;
}
public override bool CanNavigateTo()
=> TryNavigateTo((symbol, project, service) => true);
public override bool TryNavigateTo(bool isPreview)
{
return TryNavigateTo((symbol, project, service) =>
service.TryNavigateToSymbol(
symbol, project, project.Solution.Options.WithChangedOption(NavigationOptions.PreferProvisionalTab, isPreview)));
}
private bool TryNavigateTo(Func<ISymbol, Project, ISymbolNavigationService, bool> action)
{
var projectAndSymbol = ResolveSymbolInCurrentSolution();
if (projectAndSymbol == null)
{
return false;
}
var project = projectAndSymbol?.project;
var symbol = projectAndSymbol?.symbol;
if (symbol == null || project == null)
{
return false;
}
if (symbol.Kind == SymbolKind.Namespace)
{
return false;
}
var navigationService = _workspace.Services.GetService<ISymbolNavigationService>();
return action(symbol, project, navigationService);
}
private (Project project, ISymbol symbol)? ResolveSymbolInCurrentSolution()
{
var project = _workspace.CurrentSolution
.ProjectsWithReferenceToAssembly(_symbolAssemblyIdentity)
.FirstOrDefault();
if (project == null)
{
return null;
}
var compilation = project.GetCompilationAsync(CancellationToken.None)
.WaitAndGetResult(CancellationToken.None);
return (project, _symbolKey.Resolve(compilation).Symbol);
}
}
}
}
\ No newline at end of file
......@@ -3,6 +3,7 @@
using System;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Completion;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindUsages
{
......@@ -18,6 +19,22 @@ namespace Microsoft.CodeAnalysis.FindUsages
/// </summary>
internal abstract partial class DefinitionItem
{
/// <summary>
/// For metadata symbols we encode information in the <see cref="Properties"/> so we can
/// retrieve the symbol later on when navigating. This is needed so that we can go to
/// metadata-as-source for metadata symbols. We need to store the <see cref="SymbolKey"/>
/// for the symbol and the name we get back from <see cref="AssemblyIdentity.GetDisplayName"/>
/// for. With these we can effetively recover the symbol.
/// </summary>
private const string MetadataSymbolKey = nameof(MetadataSymbolKey);
private const string MetadataAssemblyIdentityDisplayName = nameof(MetadataAssemblyIdentityDisplayName);
/// <summary>
/// If this item is something that cannot be navigated to. We store this in our
/// <see cref="Properties"/> to act as an explicit marker that navigation is not possible.
/// </summary>
private const string NonNavigable = nameof(NonNavigable);
/// <summary>
/// Descriptive tags from <see cref="CompletionTags"/>. These tags may influence how the
/// item is displayed.
......@@ -82,12 +99,17 @@ internal abstract partial class DefinitionItem
NameDisplayParts = nameDisplayParts.IsDefaultOrEmpty ? displayParts : nameDisplayParts;
OriginationParts = originationParts.NullToEmpty();
SourceSpans = sourceSpans.NullToEmpty();
Properties = properties;
Properties = properties ?? ImmutableDictionary<string, string>.Empty;
DisplayIfNoReferences = displayIfNoReferences;
if (Properties.ContainsKey(MetadataSymbolKey))
{
Contract.ThrowIfFalse(Properties.ContainsKey(MetadataAssemblyIdentityDisplayName));
}
}
public abstract bool CanNavigateTo();
public abstract bool TryNavigateTo(bool isPreview);
public abstract bool CanNavigateTo(Workspace workspace);
public abstract bool TryNavigateTo(Workspace workspace, bool isPreview);
public static DefinitionItem Create(
ImmutableArray<string> tags,
......@@ -127,8 +149,13 @@ internal abstract partial class DefinitionItem
throw new ArgumentException($"{nameof(sourceSpans)} cannot be empty.");
}
return new DocumentLocationDefinitionItem(
tags, displayParts, nameDisplayParts, sourceSpans, properties, displayIfNoReferences);
var firstDocument = sourceSpans[0].Document;
var originationParts = ImmutableArray.Create(
new TaggedText(TextTags.Text, firstDocument.Project.Name));
return new DefaultDefinitionItem(
tags, displayParts, nameDisplayParts, originationParts,
sourceSpans, properties, displayIfNoReferences);
}
internal static DefinitionItem CreateMetadataDefinition(
......@@ -139,9 +166,20 @@ internal abstract partial class DefinitionItem
ImmutableDictionary<string, string> properties = null,
bool displayIfNoReferences = true)
{
return new MetadataDefinitionItem(
tags, displayParts, nameDisplayParts, properties,
displayIfNoReferences, solution, symbol);
properties = properties ?? ImmutableDictionary<string, string>.Empty;
var symbolKey = symbol.GetSymbolKey().ToString();
var assemblyIdentityDisplayName = symbol.ContainingAssembly?.Identity.GetDisplayName();
properties = properties.Add(MetadataSymbolKey, symbolKey)
.Add(MetadataAssemblyIdentityDisplayName, assemblyIdentityDisplayName);
var originationParts = GetOriginationParts(symbol);
return new DefaultDefinitionItem(
tags, displayParts, nameDisplayParts, originationParts,
sourceSpans: ImmutableArray<DocumentSpan>.Empty,
properties: properties,
displayIfNoReferences: displayIfNoReferences);
}
// Kept around for binary compat with F#/TypeScript.
......@@ -163,8 +201,17 @@ internal abstract partial class DefinitionItem
ImmutableDictionary<string, string> properties = null,
bool displayIfNoReferences = true)
{
return new NonNavigatingDefinitionItem(
tags, displayParts, originationParts, properties, displayIfNoReferences);
properties = properties ?? ImmutableDictionary<string, string>.Empty;
properties = properties.Add(NonNavigable, "");
return new DefaultDefinitionItem(
tags: tags,
displayParts: displayParts,
nameDisplayParts: ImmutableArray<TaggedText>.Empty,
originationParts: originationParts,
sourceSpans: ImmutableArray<DocumentSpan>.Empty,
properties: properties,
displayIfNoReferences: displayIfNoReferences);
}
internal static ImmutableArray<TaggedText> GetOriginationParts(ISymbol symbol)
......
// 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.Collections.Immutable;
namespace Microsoft.CodeAnalysis.FindReferences
{
internal partial class DefinitionLocation
{
/// <summary>
/// Implementation of a <see cref="DefinitionLocation"/> that sits on top of a
/// <see cref="DocumentLocation"/>.
/// </summary>
// Internal for testing purposes only.
internal sealed class DocumentDefinitionLocation : DefinitionLocation
{
public readonly DocumentLocation Location;
public DocumentDefinitionLocation(DocumentLocation location)
{
Location = location;
}
/// <summary>
/// Show the project that this <see cref="DocumentLocation"/> is contained in as the
/// Origination of this <see cref="DefinitionLocation"/>.
/// </summary>
public override ImmutableArray<TaggedText> OriginationParts =>
ImmutableArray.Create(new TaggedText(TextTags.Text, Location.Document.Project.Name));
public override bool CanNavigateTo() => true;
public override bool TryNavigateTo() => Location.TryNavigateTo();
}
}
}
\ No newline at end of file
// 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.Collections.Immutable;
using System.Linq;
using System.Threading;
......@@ -37,11 +35,12 @@ public async Task<Document> GetEditAsync(CancellationToken cancellationToken)
{
var unimplementedMembers = _state.UnimplementedMembers;
var options = await _document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
var propertyGenerationBehavior = options.GetOption(ImplementTypeOptions.PropertyGenerationBehavior);
var memberDefinitions = GenerateMembers(
unimplementedMembers,
cancellationToken);
unimplementedMembers, propertyGenerationBehavior, cancellationToken);
var options = await _document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
var insertionBehavior = options.GetOption(ImplementTypeOptions.InsertionBehavior);
var groupMembers = insertionBehavior == ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind;
......@@ -51,23 +50,25 @@ public async Task<Document> GetEditAsync(CancellationToken cancellationToken)
memberDefinitions,
new CodeGenerationOptions(
_state.Location.GetLocation(),
autoInsertionLocation: groupMembers,
autoInsertionLocation: groupMembers,
sortMembers: groupMembers),
cancellationToken).ConfigureAwait(false);
}
private ImmutableArray<ISymbol> GenerateMembers(
ImmutableArray<(INamedTypeSymbol type, ImmutableArray<ISymbol> members)> unimplementedMembers,
ImplementTypePropertyGenerationBehavior propertyGenerationBehavior,
CancellationToken cancellationToken)
{
return unimplementedMembers.SelectMany(t => t.members)
.Select(m => GenerateMember(m, cancellationToken))
.Select(m => GenerateMember(m, propertyGenerationBehavior, cancellationToken))
.WhereNotNull()
.ToImmutableArray();
}
private ISymbol GenerateMember(
ISymbol member,
ImplementTypePropertyGenerationBehavior propertyGenerationBehavior,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
......@@ -76,32 +77,29 @@ public async Task<Document> GetEditAsync(CancellationToken cancellationToken)
var syntaxFacts = _document.Project.LanguageServices.GetService<ISyntaxFactsService>();
var addUnsafe = member.IsUnsafe() && !syntaxFacts.IsUnsafeContext(_state.Location);
return GenerateMember(member, addUnsafe, cancellationToken);
return GenerateMember(member, addUnsafe, propertyGenerationBehavior, cancellationToken);
}
private ISymbol GenerateMember(
ISymbol member,
bool addUnsafe,
ImplementTypePropertyGenerationBehavior propertyGenerationBehavior,
CancellationToken cancellationToken)
{
var modifiers = new DeclarationModifiers(isOverride: true, isUnsafe: addUnsafe);
var accessibility = member.ComputeResultantAccessibility(_state.ClassType);
if (member.Kind == SymbolKind.Method)
{
return GenerateMethod((IMethodSymbol)member, modifiers, accessibility, cancellationToken);
}
else if (member.Kind == SymbolKind.Property)
{
return GenerateProperty((IPropertySymbol)member, modifiers, accessibility, cancellationToken);
}
else if (member.Kind == SymbolKind.Event)
switch (member)
{
var @event = (IEventSymbol)member;
return CodeGenerationSymbolFactory.CreateEventSymbol(
@event,
accessibility: accessibility,
modifiers: modifiers);
case IMethodSymbol method:
return GenerateMethod(method, modifiers, accessibility, cancellationToken);
case IPropertySymbol property:
return GenerateProperty(property, modifiers, accessibility, propertyGenerationBehavior, cancellationToken);
case IEventSymbol @event:
return CodeGenerationSymbolFactory.CreateEventSymbol(
@event, accessibility: accessibility, modifiers: modifiers);
}
return null;
......@@ -128,18 +126,27 @@ public async Task<Document> GetEditAsync(CancellationToken cancellationToken)
IPropertySymbol property,
DeclarationModifiers modifiers,
Accessibility accessibility,
ImplementTypePropertyGenerationBehavior propertyGenerationBehavior,
CancellationToken cancellationToken)
{
if (property.GetMethod == null)
{
// Can't generate an auto-prop for a setter-only property.
propertyGenerationBehavior = ImplementTypePropertyGenerationBehavior.PreferThrowingProperties;
}
var syntaxFactory = _document.Project.LanguageServices.GetService<SyntaxGenerator>();
var throwingBody = syntaxFactory.CreateThrowNotImplementedStatementBlock(
_model.Compilation);
var accessorBody = propertyGenerationBehavior == ImplementTypePropertyGenerationBehavior.PreferAutoProperties
? default(ImmutableArray<SyntaxNode>)
: syntaxFactory.CreateThrowNotImplementedStatementBlock(_model.Compilation);
var getMethod = ShouldGenerateAccessor(property.GetMethod)
? CodeGenerationSymbolFactory.CreateAccessorSymbol(
property.GetMethod,
attributes: default(ImmutableArray<AttributeData>),
accessibility: property.GetMethod.ComputeResultantAccessibility(_state.ClassType),
statements: throwingBody)
statements: accessorBody)
: null;
var setMethod = ShouldGenerateAccessor(property.SetMethod)
......@@ -147,7 +154,7 @@ public async Task<Document> GetEditAsync(CancellationToken cancellationToken)
property.SetMethod,
attributes: default(ImmutableArray<AttributeData>),
accessibility: property.SetMethod.ComputeResultantAccessibility(_state.ClassType),
statements: throwingBody)
statements: accessorBody)
: null;
return CodeGenerationSymbolFactory.CreatePropertySymbol(
......@@ -158,7 +165,8 @@ public async Task<Document> GetEditAsync(CancellationToken cancellationToken)
setMethod: setMethod);
}
private bool ShouldGenerateAccessor(IMethodSymbol method) => method != null && _state.ClassType.FindImplementationForAbstractMember(method) == null;
private bool ShouldGenerateAccessor(IMethodSymbol method)
=> method != null && _state.ClassType.FindImplementationForAbstractMember(method) == null;
}
}
}
}
\ No newline at end of file
......@@ -176,15 +176,15 @@ public Task<Document> GetUpdatedDocumentAsync(CancellationToken cancellationToke
var compilation = await result.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var isComImport = unimplementedMembers.Any(t => t.Item1.IsComImport);
var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
var propertyGenerationBehavior = options.GetOption(ImplementTypeOptions.PropertyGenerationBehavior);
var memberDefinitions = GenerateMembers(
compilation,
unimplementedMembers,
cancellationToken);
compilation, unimplementedMembers, propertyGenerationBehavior, cancellationToken);
// Only group the members in the destination if the user wants that *and*
// it's not a ComImport interface. Member ordering in ComImport interfaces
// matters, so we don't want to much with them.
var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
var insertionBehavior = options.GetOption(ImplementTypeOptions.InsertionBehavior);
var groupMembers = !isComImport &&
insertionBehavior == ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind;
......@@ -203,6 +203,7 @@ public Task<Document> GetUpdatedDocumentAsync(CancellationToken cancellationToke
private ImmutableArray<ISymbol> GenerateMembers(
Compilation compilation,
ImmutableArray<(INamedTypeSymbol type, ImmutableArray<ISymbol> members)> unimplementedMembers,
ImplementTypePropertyGenerationBehavior propertyGenerationBehavior,
CancellationToken cancellationToken)
{
// As we go along generating members we may end up with conflicts. For example, say
......@@ -229,7 +230,9 @@ public Task<Document> GetUpdatedDocumentAsync(CancellationToken cancellationToke
foreach (var unimplementedInterfaceMember in unimplementedInterfaceMembers)
{
var member = GenerateMember(compilation, unimplementedInterfaceMember, implementedVisibleMembers, cancellationToken);
var member = GenerateMember(
compilation, unimplementedInterfaceMember, implementedVisibleMembers,
propertyGenerationBehavior, cancellationToken);
if (member != null)
{
implementedMembers.Add(member);
......@@ -272,6 +275,7 @@ private string DetermineMemberName(ISymbol member, List<ISymbol> implementedVisi
Compilation compilation,
ISymbol member,
List<ISymbol> implementedVisibleMembers,
ImplementTypePropertyGenerationBehavior propertyGenerationBehavior,
CancellationToken cancellationToken)
{
// First check if we already generate a member that matches the member we want to
......@@ -308,7 +312,9 @@ private string DetermineMemberName(ISymbol member, List<ISymbol> implementedVisi
var syntaxFacts = Document.GetLanguageService<ISyntaxFactsService>();
var addUnsafe = member.IsUnsafe() && !syntaxFacts.IsUnsafeContext(State.Location);
return GenerateMember(compilation, member, memberName, generateInvisibleMember, generateAbstractly, addNew, addUnsafe, cancellationToken);
return GenerateMember(
compilation, member, memberName, generateInvisibleMember, generateAbstractly,
addNew, addUnsafe, propertyGenerationBehavior, cancellationToken);
}
private bool GenerateInvisibleMember(ISymbol member, string memberName)
......@@ -376,6 +382,7 @@ private static bool IsUnexpressibleTypeParameter(ITypeParameterSymbol typeParame
bool generateAbstractly,
bool addNew,
bool addUnsafe,
ImplementTypePropertyGenerationBehavior propertyGenerationBehavior,
CancellationToken cancellationToken)
{
var factory = this.Document.GetLanguageService<SyntaxGenerator>();
......@@ -386,35 +393,28 @@ private static bool IsUnexpressibleTypeParameter(ITypeParameterSymbol typeParame
? Accessibility.Public
: Accessibility.Private;
if (member.Kind == SymbolKind.Method)
{
var method = (IMethodSymbol)member;
return GenerateMethod(compilation, method, accessibility, modifiers, generateAbstractly, useExplicitInterfaceSymbol, memberName, cancellationToken);
}
else if (member.Kind == SymbolKind.Property)
{
var property = (IPropertySymbol)member;
return GenerateProperty(compilation, property, accessibility, modifiers, generateAbstractly, useExplicitInterfaceSymbol, memberName, cancellationToken);
}
else if (member.Kind == SymbolKind.Event)
switch (member)
{
var @event = (IEventSymbol)member;
var accessor = CodeGenerationSymbolFactory.CreateAccessorSymbol(
attributes: default(ImmutableArray<AttributeData>),
accessibility: Accessibility.NotApplicable,
statements: factory.CreateThrowNotImplementedStatementBlock(compilation));
return CodeGenerationSymbolFactory.CreateEventSymbol(
@event,
accessibility: accessibility,
modifiers: modifiers,
explicitInterfaceSymbol: useExplicitInterfaceSymbol ? @event : null,
name: memberName,
addMethod: GetAddOrRemoveMethod(generateInvisibly, accessor, memberName, factory.AddEventHandler),
removeMethod: GetAddOrRemoveMethod(generateInvisibly, accessor, memberName, factory.RemoveEventHandler));
case IMethodSymbol method:
return GenerateMethod(compilation, method, accessibility, modifiers, generateAbstractly, useExplicitInterfaceSymbol, memberName, cancellationToken);
case IPropertySymbol property:
return GenerateProperty(compilation, property, accessibility, modifiers, generateAbstractly, useExplicitInterfaceSymbol, memberName, propertyGenerationBehavior, cancellationToken);
case IEventSymbol @event:
var accessor = CodeGenerationSymbolFactory.CreateAccessorSymbol(
attributes: default(ImmutableArray<AttributeData>),
accessibility: Accessibility.NotApplicable,
statements: factory.CreateThrowNotImplementedStatementBlock(compilation));
return CodeGenerationSymbolFactory.CreateEventSymbol(
@event,
accessibility: accessibility,
modifiers: modifiers,
explicitInterfaceSymbol: useExplicitInterfaceSymbol ? @event : null,
name: memberName,
addMethod: GetAddOrRemoveMethod(generateInvisibly, accessor, memberName, factory.AddEventHandler),
removeMethod: GetAddOrRemoveMethod(generateInvisibly, accessor, memberName, factory.RemoveEventHandler));
}
return null;
......
......@@ -7,6 +7,7 @@
using System.Threading;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.ImplementType;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
......@@ -25,16 +26,19 @@ internal partial class ImplementInterfaceCodeAction
bool generateAbstractly,
bool useExplicitInterfaceSymbol,
string memberName,
ImplementTypePropertyGenerationBehavior propertyGenerationBehavior,
CancellationToken cancellationToken)
{
var factory = this.Document.GetLanguageService<SyntaxGenerator>();
var attributesToRemove = AttributesToRemove(compilation);
var getAccessor = GenerateGetAccessor(compilation, property, accessibility, generateAbstractly,
useExplicitInterfaceSymbol, attributesToRemove, cancellationToken);
var getAccessor = GenerateGetAccessor(
compilation, property, accessibility, generateAbstractly, useExplicitInterfaceSymbol,
propertyGenerationBehavior, attributesToRemove, cancellationToken);
var setAccessor = GenerateSetAccessor(compilation, property, accessibility,
generateAbstractly, useExplicitInterfaceSymbol, attributesToRemove, cancellationToken);
var setAccessor = GenerateSetAccessor(
compilation, property, accessibility, generateAbstractly, useExplicitInterfaceSymbol,
propertyGenerationBehavior, attributesToRemove, cancellationToken);
var syntaxFacts = Document.GetLanguageService<ISyntaxFactsService>();
var parameterNames = NameGenerator.EnsureUniqueness(
......@@ -73,6 +77,7 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation)
Accessibility accessibility,
bool generateAbstractly,
bool useExplicitInterfaceSymbol,
ImplementTypePropertyGenerationBehavior propertyGenerationBehavior,
INamedTypeSymbol[] attributesToRemove,
CancellationToken cancellationToken)
{
......@@ -81,6 +86,12 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation)
return null;
}
if (property.GetMethod == null)
{
// Can't have an auto-prop with just a setter.
propertyGenerationBehavior = ImplementTypePropertyGenerationBehavior.PreferThrowingProperties;
}
var setMethod = property.SetMethod.RemoveInaccessibleAttributesAndAttributesOfTypes(
this.State.ClassOrStructType,
attributesToRemove);
......@@ -90,7 +101,8 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation)
attributes: default(ImmutableArray<AttributeData>),
accessibility: accessibility,
explicitInterfaceSymbol: useExplicitInterfaceSymbol ? property.SetMethod : null,
statements: GetSetAccessorStatements(compilation, property, generateAbstractly, cancellationToken));
statements: GetSetAccessorStatements(
compilation, property, generateAbstractly, propertyGenerationBehavior, cancellationToken));
}
private IMethodSymbol GenerateGetAccessor(
......@@ -99,6 +111,7 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation)
Accessibility accessibility,
bool generateAbstractly,
bool useExplicitInterfaceSymbol,
ImplementTypePropertyGenerationBehavior propertyGenerationBehavior,
INamedTypeSymbol[] attributesToRemove,
CancellationToken cancellationToken)
{
......@@ -116,13 +129,15 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation)
attributes: default(ImmutableArray<AttributeData>),
accessibility: accessibility,
explicitInterfaceSymbol: useExplicitInterfaceSymbol ? property.GetMethod : null,
statements: GetGetAccessorStatements(compilation, property, generateAbstractly, cancellationToken));
statements: GetGetAccessorStatements(
compilation, property, generateAbstractly, propertyGenerationBehavior, cancellationToken));
}
private ImmutableArray<SyntaxNode> GetSetAccessorStatements(
Compilation compilation,
IPropertySymbol property,
bool generateAbstractly,
ImplementTypePropertyGenerationBehavior propertyGenerationBehavior,
CancellationToken cancellationToken)
{
if (generateAbstractly)
......@@ -157,13 +172,16 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation)
return ImmutableArray.Create(factory.ExpressionStatement(expression));
}
return factory.CreateThrowNotImplementedStatementBlock(compilation);
return propertyGenerationBehavior == ImplementTypePropertyGenerationBehavior.PreferAutoProperties
? default(ImmutableArray<SyntaxNode>)
: factory.CreateThrowNotImplementedStatementBlock(compilation);
}
private ImmutableArray<SyntaxNode> GetGetAccessorStatements(
Compilation compilation,
IPropertySymbol property,
bool generateAbstractly,
ImplementTypePropertyGenerationBehavior propertyGenerationBehavior,
CancellationToken cancellationToken)
{
if (generateAbstractly)
......@@ -196,8 +214,10 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation)
return ImmutableArray.Create(factory.ReturnStatement(expression));
}
return factory.CreateThrowNotImplementedStatementBlock(compilation);
return propertyGenerationBehavior == ImplementTypePropertyGenerationBehavior.PreferAutoProperties
? default(ImmutableArray<SyntaxNode>)
: factory.CreateThrowNotImplementedStatementBlock(compilation);
}
}
}
}
}
\ No newline at end of file
// 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.Linq;
using System.Threading;
......
......@@ -10,6 +10,12 @@ internal enum ImplementTypeInsertionBehavior
AtTheEnd = 1,
}
internal enum ImplementTypePropertyGenerationBehavior
{
PreferThrowingProperties = 0,
PreferAutoProperties = 1,
}
internal static class ImplementTypeOptions
{
public static readonly PerLanguageOption<ImplementTypeInsertionBehavior> InsertionBehavior =
......@@ -19,5 +25,14 @@ internal static class ImplementTypeOptions
defaultValue: ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind,
storageLocations: new RoamingProfileStorageLocation(
$"TextEditor.%LANGUAGE%.{nameof(ImplementTypeOptions)}.{nameof(InsertionBehavior)}"));
public static readonly PerLanguageOption<ImplementTypePropertyGenerationBehavior> PropertyGenerationBehavior =
new PerLanguageOption<ImplementTypePropertyGenerationBehavior>(
nameof(ImplementTypeOptions),
nameof(PropertyGenerationBehavior),
defaultValue: ImplementTypePropertyGenerationBehavior.PreferThrowingProperties,
storageLocations: new RoamingProfileStorageLocation(
$"TextEditor.%LANGUAGE%.{nameof(ImplementTypeOptions)}.{nameof(PropertyGenerationBehavior)}"));
}
}
\ No newline at end of file
......@@ -100,6 +100,16 @@
x:Name="at_the_end"
Content="{x:Static local:AdvancedOptionPageStrings.Option_at_the_end}"/>
</StackPanel>
<Label Content="{x:Static local:AdvancedOptionPageStrings.Option_When_generating_properties}"/>
<StackPanel Margin="15, 0, 0, 0">
<RadioButton GroupName="Property_generation_behavior"
x:Name="prefer_throwing_properties"
Content="{x:Static local:AdvancedOptionPageStrings.Option_prefer_throwing_properties}"/>
<RadioButton GroupName="Property_generation_behavior"
x:Name="prefer_auto_properties"
Content="{x:Static local:AdvancedOptionPageStrings.Option_prefer_auto_properties}"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
......
......@@ -48,6 +48,9 @@ public AdvancedOptionPageControl(IServiceProvider serviceProvider) : base(servic
BindToOption(with_other_members_of_the_same_kind, ImplementTypeOptions.InsertionBehavior, ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, LanguageNames.CSharp);
BindToOption(at_the_end, ImplementTypeOptions.InsertionBehavior, ImplementTypeInsertionBehavior.AtTheEnd, LanguageNames.CSharp);
BindToOption(prefer_throwing_properties, ImplementTypeOptions.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferThrowingProperties, LanguageNames.CSharp);
BindToOption(prefer_auto_properties, ImplementTypeOptions.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferAutoProperties, LanguageNames.CSharp);
}
}
}
\ No newline at end of file
......@@ -62,6 +62,15 @@ public static string Option_with_other_members_of_the_same_kind
public static string Option_at_the_end
=> ServicesVSResources.at_the_end;
public static string Option_When_generating_properties
=> ServicesVSResources.When_generating_properties;
public static string Option_prefer_auto_properties
=> ServicesVSResources.prefer_auto_properties;
public static string Option_prefer_throwing_properties
=> ServicesVSResources.prefer_throwing_properties;
public static string Option_GenerateXmlDocCommentsForTripleSlash
{
get { return CSharpVSResources.Generate_XML_documentation_comments_for; }
......
......@@ -106,9 +106,9 @@ private class ExternalDefinitionItem : DefinitionItem
_charOffset = charOffset;
}
public override bool CanNavigateTo() => true;
public override bool CanNavigateTo(Workspace workspace) => true;
public override bool TryNavigateTo(bool isPreview)
public override bool TryNavigateTo(Workspace workspace, bool isPreview)
{
return TryOpenFile() && TryNavigateToPosition();
}
......
......@@ -12,9 +12,12 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Library.FindRes
[Guid(Guids.RoslynLibraryIdString)]
internal partial class LibraryManager : AbstractLibraryManager
{
public LibraryManager(IServiceProvider serviceProvider)
private readonly Workspace _workspace;
public LibraryManager(Workspace workspace, IServiceProvider serviceProvider)
: base(Guids.RoslynLibraryId, serviceProvider)
{
_workspace = workspace;
}
public override uint GetLibraryFlags()
......
......@@ -40,7 +40,7 @@ public void PresentDefinitionsAndReferences(DefinitionsAndReferences definitions
var query =
from d in definitionsAndReferences.Definitions
let referenceItems = CreateReferenceItems(d, definitionsAndReferences, commonPathElements)
select new DefinitionTreeItem(d, referenceItems);
select new DefinitionTreeItem(_workspace, d, referenceItems);
return query.ToList<AbstractTreeItem>();
}
......
......@@ -12,13 +12,16 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Library.FindRes
{
internal class DefinitionTreeItem : AbstractTreeItem
{
private readonly Workspace _workspace;
private readonly DefinitionItem _definitionItem;
public DefinitionTreeItem(
Workspace workspace,
DefinitionItem definitionItem,
ImmutableArray<SourceReferenceTreeItem> referenceItems)
: base(definitionItem.Tags.GetGlyph().GetGlyphIndex())
{
_workspace = workspace;
_definitionItem = definitionItem;
this.Children.AddRange(referenceItems);
......@@ -49,14 +52,14 @@ private string CreateDisplayText()
public override int GoToSource()
{
return _definitionItem.TryNavigateTo(isPreview: true)
return _definitionItem.TryNavigateTo(_workspace, isPreview: true)
? VSConstants.S_OK
: VSConstants.E_FAIL;
}
public override bool CanGoToDefinition()
{
return _definitionItem.CanNavigateTo();
return _definitionItem.CanNavigateTo(_workspace);
}
}
}
\ No newline at end of file
......@@ -62,11 +62,11 @@ protected override void Initialize()
var method = compilerFailFast.GetMethod(nameof(FailFast.OnFatalException), BindingFlags.Static | BindingFlags.NonPublic);
property.SetValue(null, Delegate.CreateDelegate(property.PropertyType, method));
RegisterFindResultsLibraryManager();
var componentModel = (IComponentModel)this.GetService(typeof(SComponentModel));
_workspace = componentModel.GetService<VisualStudioWorkspace>();
RegisterFindResultsLibraryManager();
// Ensure the options persisters are loaded since we have to fetch options from the shell
componentModel.GetExtensions<IOptionPersister>();
......@@ -186,7 +186,7 @@ private void RegisterFindResultsLibraryManager()
var objectManager = this.GetService(typeof(SVsObjectManager)) as IVsObjectManager2;
if (objectManager != null)
{
_libraryManager = new LibraryManager(this);
_libraryManager = new LibraryManager(_workspace, this);
if (ErrorHandler.Failed(objectManager.RegisterSimpleLibrary(_libraryManager, out _libraryManagerCookie)))
{
......
......@@ -1457,6 +1457,15 @@ internal class ServicesVSResources {
}
}
/// <summary>
/// Looks up a localized string similar to prefer auto properties.
/// </summary>
internal static string prefer_auto_properties {
get {
return ResourceManager.GetString("prefer_auto_properties", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Prefer braces.
/// </summary>
......@@ -1547,6 +1556,15 @@ internal class ServicesVSResources {
}
}
/// <summary>
/// Looks up a localized string similar to prefer throwing properties.
/// </summary>
internal static string prefer_throwing_properties {
get {
return ResourceManager.GetString("prefer_throwing_properties", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Preference.
/// </summary>
......@@ -2164,7 +2182,7 @@ internal class ServicesVSResources {
internal static string Unfortunately_a_process_used_by_Visual_Studio_has_encountered_an_unrecoverable_error_We_recommend_saving_your_work_and_then_closing_and_restarting_Visual_Studio {
get {
return ResourceManager.GetString("Unfortunately_a_process_used_by_Visual_Studio_has_encountered_an_unrecoverable_er" +
"ror_We_recommend_saving_your_work_and_then_closing_and_restarting_Visual Studio", resourceCulture);
"ror_We_recommend_saving_your_work_and_then_closing_and_restarting_Visual_Studio", resourceCulture);
}
}
......@@ -2332,6 +2350,15 @@ internal class ServicesVSResources {
}
}
/// <summary>
/// Looks up a localized string similar to When generating properties:.
/// </summary>
internal static string When_generating_properties {
get {
return ResourceManager.GetString("When_generating_properties", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to When inserting properties, events and methods, place them:.
/// </summary>
......
......@@ -873,7 +873,7 @@ Additional information: {1}</value>
<data name="Pick_members" xml:space="preserve">
<value>Pick members</value>
</data>
<data name="Unfortunately_a_process_used_by_Visual_Studio_has_encountered_an_unrecoverable_error_We_recommend_saving_your_work_and_then_closing_and_restarting_Visual Studio" xml:space="preserve">
<data name="Unfortunately_a_process_used_by_Visual_Studio_has_encountered_an_unrecoverable_error_We_recommend_saving_your_work_and_then_closing_and_restarting_Visual_Studio" xml:space="preserve">
<value>Unfortunately, a process used by Visual Studio has encountered an unrecoverable error. We recommend saving your work, and then closing and restarting Visual Studio.</value>
</data>
<data name="Add_a_symbol_specification" xml:space="preserve">
......@@ -900,6 +900,15 @@ Additional information: {1}</value>
<data name="VisualStudioWorkspace_TryApplyChanges_cannot_be_called_from_a_background_thread" xml:space="preserve">
<value>VisualStudioWorkspace.TryApplyChanges cannot be called from a background thread.</value>
</data>
<data name="prefer_auto_properties" xml:space="preserve">
<value>prefer auto properties</value>
</data>
<data name="prefer_throwing_properties" xml:space="preserve">
<value>prefer throwing properties</value>
</data>
<data name="When_generating_properties" xml:space="preserve">
<value>When generating properties:</value>
</data>
<data name="Options" xml:space="preserve">
<value>Options</value>
</data>
......
......@@ -35,9 +35,7 @@ private class RoslynDefinitionBucket : DefinitionBucket, ISupportsNavigation
}
public bool TryNavigateTo(bool isPreview)
{
return DefinitionItem.TryNavigateTo(isPreview);
}
=> DefinitionItem.TryNavigateTo(_presenter._workspace, isPreview);
public override bool TryGetValue(string key, out object content)
{
......
......@@ -110,6 +110,16 @@
x:Name="at_the_end"
Content="{x:Static local:AdvancedOptionPageStrings.Option_at_the_end}"/>
</StackPanel>
<Label Content="{x:Static local:AdvancedOptionPageStrings.Option_When_generating_properties}"/>
<StackPanel Margin="15, 0, 0, 0">
<RadioButton GroupName="Property_generation_behavior"
x:Name="prefer_throwing_properties"
Content="{x:Static local:AdvancedOptionPageStrings.Option_prefer_throwing_properties}"/>
<RadioButton GroupName="Property_generation_behavior"
x:Name="prefer_auto_properties"
Content="{x:Static local:AdvancedOptionPageStrings.Option_prefer_auto_properties}"/>
</StackPanel>
</StackPanel>
</GroupBox>
......
......@@ -45,6 +45,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options
BindToOption(with_other_members_of_the_same_kind, ImplementTypeOptions.InsertionBehavior, ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, LanguageNames.VisualBasic)
BindToOption(at_the_end, ImplementTypeOptions.InsertionBehavior, ImplementTypeInsertionBehavior.AtTheEnd, LanguageNames.VisualBasic)
BindToOption(prefer_throwing_properties, ImplementTypeOptions.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferThrowingProperties, LanguageNames.VisualBasic)
BindToOption(prefer_auto_properties, ImplementTypeOptions.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferAutoProperties, LanguageNames.VisualBasic)
End Sub
End Class
End Namespace
\ No newline at end of file
......@@ -84,6 +84,15 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options
Public ReadOnly Property Option_with_other_members_of_the_same_kind As String =
ServicesVSResources.with_other_members_of_the_same_kind
Public ReadOnly Property Option_When_generating_properties As String =
ServicesVSResources.When_generating_properties
Public ReadOnly Property Option_prefer_auto_properties As String =
ServicesVSResources.prefer_auto_properties
Public ReadOnly Property Option_prefer_throwing_properties As String =
ServicesVSResources.prefer_throwing_properties
Public ReadOnly Property Option_at_the_end As String =
ServicesVSResources.at_the_end
......
......@@ -246,18 +246,27 @@ public PatternMatches GetMatches(string candidate, string dottedContainer)
/// so, unless you need to know the full set of matches, use this version.
/// </remarks>
/// <param name="candidate">The word being tested.</param>
/// <param name="inludeMatchSpans">Whether or not the matched spans should be included with results</param>
/// <param name="includeMatchSpans">Whether or not the matched spans should be included with results</param>
/// <returns>If this was a match, the first element of the set of match types that occurred while matching the
/// patterns. If it was not a match, it returns null.</returns>
public PatternMatch? GetFirstMatch(string candidate, bool inludeMatchSpans = false)
public PatternMatch? GetFirstMatch(
string candidate, bool includeMatchSpans)
{
if (SkipMatch(candidate))
{
return null;
}
return MatchPatternSegment(candidate, inludeMatchSpans, _fullPatternSegment, wantAllMatches: false, allMatches: out _, fuzzyMatch: false) ??
MatchPatternSegment(candidate, inludeMatchSpans, _fullPatternSegment, wantAllMatches: false, allMatches: out _, fuzzyMatch: true);
return GetFirstMatchWorker(candidate, includeMatchSpans, fuzzyMatch: false) ??
GetFirstMatchWorker(candidate, includeMatchSpans, fuzzyMatch: true);
}
private PatternMatch? GetFirstMatchWorker(
string candidate, bool includeMatchSpans, bool fuzzyMatch)
{
return MatchPatternSegment(
candidate, includeMatchSpans, _fullPatternSegment,
wantAllMatches: false, allMatches: out _, fuzzyMatch: fuzzyMatch);
}
private StringBreaks GetWordSpans(string word)
......@@ -268,13 +277,6 @@ private StringBreaks GetWordSpans(string word)
}
}
internal PatternMatch? MatchSingleWordPattern_ForTestingOnly(string candidate)
{
return MatchPatternChunk(candidate, includeMatchSpans: true,
patternChunk: _fullPatternSegment.TotalTextChunk, punctuationStripped: false,
fuzzyMatch: false);
}
private static bool ContainsUpperCaseLetter(string pattern)
{
// Expansion of "foreach(char ch in pattern)" to avoid a CharEnumerator allocation
......@@ -443,12 +445,10 @@ private static bool ContainsSpaceOrAsterisk(string text)
var singleMatch = MatchPatternSegment(candidate, includeMatchSpans, patternSegment,
wantAllMatches: true, fuzzyMatch: fuzzyMatch, allMatches: out var matches);
if (singleMatch.HasValue)
{
return ImmutableArray.Create(singleMatch.Value);
}
return matches;
return singleMatch.HasValue
? ImmutableArray.Create(singleMatch.Value)
: matches;
}
/// <summary>
......@@ -554,17 +554,18 @@ private static bool ContainsSpaceOrAsterisk(string text)
return null;
}
if (!wantAllMatches || subWordTextChunks.Length == 1)
{
// Stop at the first word
return result;
}
matches.Add(result.Value);
}
allMatches = matches.ToImmutable();
return null;
if (wantAllMatches && matches.Count >= 2)
{
allMatches = matches.ToImmutable();
return null;
}
else
{
return matches.FirstOrNullable();
}
}
finally
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册