diff --git a/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs b/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs index 545e53524b7d7..531132d451939 100644 --- a/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs +++ b/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs @@ -178,5 +178,10 @@ internal static class PredefinedCommandHandlerNames /// Command handler name for Edit and Continue file save handler. /// public const string EditAndContinueFileSave = "Edit and Continue Save File Handler"; + + /// + /// Command handler name for showing the Value Tracking tool window. + /// + public const string ShowValueTracking = "Show Value Tracking"; } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs index 0c8bd812e5b38..3fff8bac5048f 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs @@ -2,19 +2,86 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + namespace Microsoft.CodeAnalysis.ValueTracking { internal class ValueTrackedItem { - public Location Location { get; } public ISymbol Symbol { get; } + public ValueTrackedItem? Parent { get; } + + public Document Document { get; } + public TextSpan Span { get; } + public ImmutableArray ClassifiedSpans { get; } + public SourceText SourceText { get; } + public LineSpan LineSpan { get; } - public ValueTrackedItem( - Location location, - ISymbol symbol) + private ValueTrackedItem( + ISymbol symbol, + SourceText sourceText, + ImmutableArray classifiedSpans, + TextSpan textSpan, + Document document, + LineSpan lineSpan, + ValueTrackedItem? parent = null) { - Location = location; Symbol = symbol; + Parent = parent; + + Span = textSpan; + ClassifiedSpans = classifiedSpans; + SourceText = sourceText; + LineSpan = lineSpan; + Document = document; + } + + public static async Task TryCreateAsync(Solution solution, Location location, ISymbol symbol, ValueTrackedItem? parent = null, CancellationToken cancellationToken = default) + { + Contract.ThrowIfNull(location.SourceTree); + + var document = solution.GetRequiredDocument(location.SourceTree); + var excerptService = document.Services.GetService(); + SourceText? sourceText = null; + ImmutableArray classifiedSpans = default; + + if (excerptService != null) + { + var result = await excerptService.TryExcerptAsync(document, location.SourceSpan, ExcerptMode.SingleLine, cancellationToken).ConfigureAwait(false); + if (result.HasValue) + { + var value = result.Value; + sourceText = value.Content; + } + } + + if (sourceText is null) + { + var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(document, location.SourceSpan, cancellationToken).ConfigureAwait(false); + var classificationResult = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(documentSpan, cancellationToken).ConfigureAwait(false); + classifiedSpans = classificationResult.ClassifiedSpans; + sourceText = await location.SourceTree.GetTextAsync(cancellationToken).ConfigureAwait(false); + } + + sourceText.GetLineAndOffset(location.SourceSpan.Start, out var lineStart, out var _); + sourceText.GetLineAndOffset(location.SourceSpan.End, out var lineEnd, out var _); + var lineSpan = LineSpan.FromBounds(lineStart, lineEnd); + + return new ValueTrackedItem( + symbol, + sourceText, + classifiedSpans, + location.SourceSpan, + document, + lineSpan, + parent: parent); } } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 95c39d8d344bd..9effa93548c32 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -49,7 +49,11 @@ or ILocalSymbol foreach (var syntaxRef in symbol.DeclaringSyntaxReferences) { var location = Location.Create(syntaxRef.SyntaxTree, syntaxRef.Span); - progressCollector.Report(new ValueTrackedItem(location, symbol)); + var item = await ValueTrackedItem.TryCreateAsync(solution, location, symbol, parent: null, cancellationToken).ConfigureAwait(false); + if (item is not null) + { + progressCollector.Report(item); + } } var findReferenceProgressCollector = new FindReferencesProgress(progressCollector); @@ -75,7 +79,7 @@ public Task TrackValueSourceAsync( ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) { - throw new NotImplementedException(); + return Task.CompletedTask; } private class FindReferencesProgress : IStreamingFindReferencesProgress, IStreamingProgressTracker @@ -100,14 +104,17 @@ public FindReferencesProgress(ValueTrackingProgressCollector valueTrackingProgre public ValueTask OnFindInDocumentStartedAsync(Document document) => new(); - public ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location) + public async ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location) { if (location.IsWrittenTo) { - _valueTrackingProgressCollector.Report(new ValueTrackedItem(location.Location, symbol)); + var solution = location.Document.Project.Solution; + var item = await ValueTrackedItem.TryCreateAsync(solution, location.Location, symbol, parent: null, CancellationToken.None).ConfigureAwait(false); + if (item is not null) + { + _valueTrackingProgressCollector.Report(item); + } } - - return new(); } public ValueTask OnStartedAsync() => new(); diff --git a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs index 40d4d8d82a33a..7e0719f1c0e3b 100644 --- a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs @@ -44,8 +44,7 @@ internal static async Task GetSelectedSymbolAsync(TextSpan textSpan, Do internal static void ValidateItem(ValueTrackedItem item, int line) { - var lineSpan = item.Location.GetLineSpan(); - Assert.Equal(line, lineSpan.StartLinePosition.Line); + Assert.Equal(line, item.LineSpan.Start); } } } diff --git a/src/VisualStudio/Core/Def/Commands.vsct b/src/VisualStudio/Core/Def/Commands.vsct index 422b93c7d0bfd..ae1d78ad48c01 100644 --- a/src/VisualStudio/Core/Def/Commands.vsct +++ b/src/VisualStudio/Core/Def/Commands.vsct @@ -411,6 +411,19 @@ RemoveUnusedReferences + + @@ -516,6 +529,10 @@ + + + + @@ -601,6 +618,8 @@ + + diff --git a/src/VisualStudio/Core/Def/Guids.cs b/src/VisualStudio/Core/Def/Guids.cs index 6f058e3c50002..994acb0fbbebc 100644 --- a/src/VisualStudio/Core/Def/Guids.cs +++ b/src/VisualStudio/Core/Def/Guids.cs @@ -125,6 +125,9 @@ internal static class Guids public static readonly Guid RoslynCommandSetId = new(RoslynCommandSetIdString); public static readonly Guid RoslynGroupId = new(RoslynGroupIdString); + public const string ValueTrackingToolWindowIdString = "60a19d42-2dd7-43f3-be90-c7a9cb7d28f4"; + public static readonly Guid ValueTrackingToolWindowId = new(ValueTrackingToolWindowIdString); + // TODO: Remove pending https://github.com/dotnet/roslyn/issues/8927 . // Interactive guids public const string InteractiveCommandSetIdString = "00B8868B-F9F5-4970-A048-410B05508506"; diff --git a/src/VisualStudio/Core/Def/ID.RoslynCommands.cs b/src/VisualStudio/Core/Def/ID.RoslynCommands.cs index d89285327286e..ad65fd8e87b1f 100644 --- a/src/VisualStudio/Core/Def/ID.RoslynCommands.cs +++ b/src/VisualStudio/Core/Def/ID.RoslynCommands.cs @@ -50,6 +50,7 @@ public static class RoslynCommands public const int RunCodeAnalysisForProject = 0x0201; public const int RemoveUnusedReferences = 0x0202; + public const int GoToValueTrackingWindow = 0x0203; } } } diff --git a/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs b/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs index 27a6efccbd0ea..b2992efa119ab 100644 --- a/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs +++ b/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.Editor.Commanding.Commands; using Microsoft.VisualStudio.Editor.Commanding; using Microsoft.VisualStudio.LanguageServices; +using Microsoft.VisualStudio.LanguageServices.ValueTracking; namespace Microsoft.VisualStudio.Editor.Implementation { @@ -28,5 +29,9 @@ internal sealed class CommandBindings [Export] [CommandBinding(Guids.CSharpGroupIdString, ID.CSharpCommands.ContextOrganizeRemoveAndSort, typeof(SortAndRemoveUnnecessaryImportsCommandArgs))] internal CommandBindingDefinition contextOrganizeRemoveAndSortCommandBinding; + + [Export] + [CommandBinding(Guids.RoslynGroupIdString, ID.RoslynCommands.GoToValueTrackingWindow, typeof(ValueTrackingEditorCommandArgs))] + internal CommandBindingDefinition gotoDataFlowToolCommandBinding; } } diff --git a/src/VisualStudio/Core/Def/PackageRegistration.pkgdef b/src/VisualStudio/Core/Def/PackageRegistration.pkgdef index 27e41b380e9ba..d1b8281f52747 100644 --- a/src/VisualStudio/Core/Def/PackageRegistration.pkgdef +++ b/src/VisualStudio/Core/Def/PackageRegistration.pkgdef @@ -24,3 +24,9 @@ "Value"=dword:00000000 "Title"="Enable experimental C#/VB LSP completion experience" "PreviewPaneChannels"="IntPreview,int.main" + +// The option page configuration is duplicated in RoslynPackage +// [ProvideToolWindow(typeof(ValueTracking.ValueTrackingToolWindow))] +[$RootKey$\ToolWindows\{60a19d42-2dd7-43f3-be90-c7a9cb7d28f4}] +"Name"="Microsoft.VisualStudio.LanguageServices.ValueTracking.ValueTrackingToolWindow" +@="{6cf2e545-6109-4730-8883-cf43d7aec3e1}" diff --git a/src/VisualStudio/Core/Def/RoslynPackage.cs b/src/VisualStudio/Core/Def/RoslynPackage.cs index 7261ce5c45d59..57e373566a571 100644 --- a/src/VisualStudio/Core/Def/RoslynPackage.cs +++ b/src/VisualStudio/Core/Def/RoslynPackage.cs @@ -19,28 +19,23 @@ using Microsoft.CodeAnalysis.Logging; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Versions; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.LanguageServices.ColorSchemes; using Microsoft.VisualStudio.LanguageServices.Experimentation; using Microsoft.VisualStudio.LanguageServices.Implementation; -using Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute; using Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics; using Microsoft.VisualStudio.LanguageServices.Implementation.Interactive; using Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.RuleSets; -using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectTelemetry; using Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource; -using Microsoft.VisualStudio.LanguageServices.Implementation.TodoComments; using Microsoft.VisualStudio.LanguageServices.Implementation.UnusedReferences; using Microsoft.VisualStudio.LanguageServices.Telemetry; using Microsoft.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.Shell.ServiceBroker; using Microsoft.VisualStudio.TaskStatusCenter; using Microsoft.VisualStudio.Telemetry; using Microsoft.VisualStudio.TextManager.Interop; @@ -51,6 +46,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Setup { [Guid(Guids.RoslynPackageIdString)] + + // The option page configuration is duplicated in PackageRegistration.pkgdef + [ProvideToolWindow(typeof(ValueTracking.ValueTrackingToolWindow))] internal sealed class RoslynPackage : AbstractPackage { // The randomly-generated key name is used for serializing the ILSpy decompiler EULA preference to the .SUO @@ -188,6 +186,19 @@ protected override async Task LoadComponentsAsync(CancellationToken cancellation LoadComponentsBackgroundAsync(cancellationToken).Forget(); } + // Overrides for VSSDK003 fix + // See https://github.com/Microsoft/VSSDK-Analyzers/blob/main/doc/VSSDK003.md + public override IVsAsyncToolWindowFactory GetAsyncToolWindowFactory(Guid toolWindowType) + => toolWindowType == typeof(ValueTracking.ValueTrackingToolWindow).GUID + ? this + : base.GetAsyncToolWindowFactory(toolWindowType); + + protected override string GetToolWindowTitle(Type toolWindowType, int id) + => base.GetToolWindowTitle(toolWindowType, id); + + protected override Task InitializeToolWindowAsync(Type toolWindowType, int id, CancellationToken cancellationToken) + => Task.FromResult((object?)null); + private async Task LoadComponentsBackgroundAsync(CancellationToken cancellationToken) { await TaskScheduler.Default; diff --git a/src/VisualStudio/Core/Def/ValueTracking/BindableTextBlock.cs b/src/VisualStudio/Core/Def/ValueTracking/BindableTextBlock.cs new file mode 100644 index 0000000000000..37f1696f34b32 --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/BindableTextBlock.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using Roslyn.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class BindableTextBlock : TextBlock + { + public IList InlineCollection + { + get { return (ObservableCollection)GetValue(InlineListProperty); } + set { SetValue(InlineListProperty, value); } + } + + public static readonly DependencyProperty InlineListProperty = + DependencyProperty.Register(nameof(InlineCollection), typeof(IList), typeof(BindableTextBlock), new UIPropertyMetadata(null, OnPropertyChanged)); + + private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + var textBlock = (BindableTextBlock)sender; + var newList = (IList)e.NewValue; + + textBlock.Inlines.Clear(); + foreach (var inline in newList) + { + textBlock.Inlines.Add(inline); + } + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/EmptyTreeViewItem.cs b/src/VisualStudio/Core/Def/ValueTracking/EmptyTreeViewItem.cs new file mode 100644 index 0000000000000..767ecab7af32f --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/EmptyTreeViewItem.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class EmptyTreeViewItem : TreeViewItemBase + { + public static EmptyTreeViewItem Instance { get; } = new(); + + private EmptyTreeViewItem() + { + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/TreeViewItemBase.cs b/src/VisualStudio/Core/Def/ValueTracking/TreeViewItemBase.cs new file mode 100644 index 0000000000000..d7cb529bc40e1 --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/TreeViewItemBase.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class TreeViewItemBase : INotifyPropertyChanged + { + private bool _isExpanded = false; + public bool IsNodeExpanded + { + get => _isExpanded; + set => SetProperty(ref _isExpanded, value); + } + + private bool _isSelected = false; + public bool IsNodeSelected + { + get => _isSelected; + set => SetProperty(ref _isSelected, value); + } + + public event PropertyChangedEventHandler? PropertyChanged; + + protected void SetProperty(ref T field, T value, [CallerMemberName] string name = "") + { + if (EqualityComparer.Default.Equals(field, value)) + { + return; + } + + field = value; + NotifyPropertyChanged(name); + } + + protected void NotifyPropertyChanged(string name) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs new file mode 100644 index 0000000000000..e2e1bdfcd5761 --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.ValueTracking; +using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Language.Intellisense; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class ValueTrackedTreeItemViewModel : ValueTrackingTreeItemViewModel + { + private bool _childrenCalculated; + private readonly Solution _solution; + private readonly IClassificationFormatMap _classificationFormatMap; + private readonly ClassificationTypeMap _classificationTypeMap; + private readonly IGlyphService _glyphService; + private readonly IValueTrackingService _valueTrackingService; + private readonly ValueTrackedItem _trackedItem; + + public ValueTrackedTreeItemViewModel( + ValueTrackedItem trackedItem, + Solution solution, + IClassificationFormatMap classificationFormatMap, + ClassificationTypeMap classificationTypeMap, + IGlyphService glyphService, + IValueTrackingService valueTrackingService, + ImmutableArray children = default) + : base( + trackedItem.Document, + trackedItem.LineSpan.Start, + trackedItem.SourceText, + trackedItem.Symbol, + trackedItem.ClassifiedSpans, + classificationFormatMap, + classificationTypeMap, + glyphService) + { + + _trackedItem = trackedItem; + _solution = solution; + _classificationFormatMap = classificationFormatMap; + _classificationTypeMap = classificationTypeMap; + _glyphService = glyphService; + _valueTrackingService = valueTrackingService; + + if (children.IsDefaultOrEmpty) + { + // Add an empty item so the treeview has an expansion showing to calculate + // the actual children of the node + ChildItems.Add(EmptyTreeViewItem.Instance); + + ChildItems.CollectionChanged += (s, a) => + { + NotifyPropertyChanged(nameof(ChildItems)); + }; + + PropertyChanged += (s, a) => + { + if (a.PropertyName == nameof(IsNodeExpanded)) + { + CalculateChildren(); + } + }; + } + } + + private void CalculateChildren() + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (_childrenCalculated) + { + return; + } + + _childrenCalculated = true; + + CalculateChildrenAsync(CancellationToken.None) + .ContinueWith(task => + { + if (task.Exception is not null) + { + _childrenCalculated = false; + return; + } + + ChildItems.Clear(); + + foreach (var item in task.Result) + { + ChildItems.Add(item); + } + }, + + // Use the UI thread synchronization context for calling back to the UI + // thread to add the tiems + TaskScheduler.FromCurrentSynchronizationContext()); + } + + private async Task> CalculateChildrenAsync(CancellationToken cancellationToken) + { + var valueTrackedItems = await _valueTrackingService.TrackValueSourceAsync( + _solution, + _trackedItem, + cancellationToken).ConfigureAwait(false); + + // TODO: Use pooled item here? + var builder = new List(); + foreach (var valueTrackedItem in valueTrackedItems) + { + builder.Add(new ValueTrackedTreeItemViewModel( + valueTrackedItem, + _solution, + _classificationFormatMap, + _classificationTypeMap, + _glyphService, + _valueTrackingService)); + } + + return builder.ToImmutableArray(); + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs new file mode 100644 index 0000000000000..6d1766a43543b --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -0,0 +1,242 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.ComponentModel.Composition; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.ValueTracking; +using Microsoft.VisualStudio.Commanding; +using Microsoft.VisualStudio.GraphModel.CodeSchema; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.LanguageServices.Setup; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Utilities; +using Roslyn.Utilities; +using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; +using Task = System.Threading.Tasks.Task; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + [Export(typeof(ICommandHandler))] + [ContentType(ContentTypeNames.RoslynContentType)] + [Name(PredefinedCommandHandlerNames.ShowValueTracking)] + internal class ValueTrackingCommandHandler : ICommandHandler + { + private readonly IAsyncServiceProvider _serviceProvider; + private readonly SVsServiceProvider _serviceProvider1; + private readonly IThreadingContext _threadingContext; + private readonly ClassificationTypeMap _typeMap; + private readonly IClassificationFormatMapService _classificationFormatMapService; + private readonly IGlyphService _glyphService; + private RoslynPackage? _roslynPackage; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public ValueTrackingCommandHandler( + SVsServiceProvider serviceProvider, + IThreadingContext threadingContext, + ClassificationTypeMap typeMap, + IClassificationFormatMapService classificationFormatMapService, + IGlyphService glyphService) + { + _serviceProvider = (IAsyncServiceProvider)serviceProvider; + _serviceProvider1 = serviceProvider; + _threadingContext = threadingContext; + _typeMap = typeMap; + _classificationFormatMapService = classificationFormatMapService; + _glyphService = glyphService; + } + + public string DisplayName => "Go to value tracking"; + + public CommandState GetCommandState(ValueTrackingEditorCommandArgs args) + => CommandState.Available; + + public bool ExecuteCommand(ValueTrackingEditorCommandArgs args, CommandExecutionContext executionContext) + { + var cancellationToken = executionContext.OperationContext.UserCancellationToken; + var caretPosition = args.TextView.GetCaretPoint(args.SubjectBuffer); + if (!caretPosition.HasValue) + { + return false; + } + + var textSpan = new TextSpan(caretPosition.Value.Position, 0); + var sourceTextContainer = args.SubjectBuffer.AsTextContainer(); + var document = sourceTextContainer.GetOpenDocumentInCurrentContext(); + if (document is null) + { + return false; + } + + _threadingContext.JoinableTaskFactory.RunAsync(async () => + { + var selectedSymbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken).ConfigureAwait(false); + if (selectedSymbol is null) + { + // TODO: Show error dialog + return; + } + + var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken); + var location = Location.Create(syntaxTree, textSpan); + + await ShowToolWindowAsync(args.TextView, selectedSymbol, location, document.Project.Solution, cancellationToken).ConfigureAwait(false); + }); + + return true; + } + + private static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var selectedNode = root.FindNode(textSpan); + if (selectedNode is null) + { + return null; + } + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var selectedSymbol = + semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol + ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); + + if (selectedSymbol is null) + { + return null; + } + + return selectedSymbol switch + { + ILocalSymbol + or IPropertySymbol { SetMethod: not null } + or IFieldSymbol { IsReadOnly: false } + or IEventSymbol + or IParameterSymbol + => selectedSymbol, + + _ => null + }; + } + + private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbol, Location location, Solution solution, CancellationToken cancellationToken) + { + var item = await ValueTrackedItem.TryCreateAsync(solution, location, selectedSymbol, cancellationToken: cancellationToken).ConfigureAwait(false); + if (item is null) + { + return; + } + + var valueTrackingService = solution.Workspace.Services.GetRequiredService(); + var classificationFormatMap = _classificationFormatMapService.GetClassificationFormatMap(textView); + + var childItems = await valueTrackingService.TrackValueSourceAsync(solution, item, cancellationToken).ConfigureAwait(false); + var childViewModels = childItems.SelectAsArray(child => CreateViewModel(child)); + + RoslynDebug.AssertNotNull(location.SourceTree); + var document = solution.GetRequiredDocument(location.SourceTree); + + var sourceText = await location.SourceTree.GetTextAsync(cancellationToken).ConfigureAwait(false); + sourceText.GetLineAndOffset(location.SourceSpan.Start, out var lineStart, out var _); + sourceText.GetLineAndOffset(location.SourceSpan.End, out var lineEnd, out var _); + var lineSpan = LineSpan.FromBounds(lineStart, lineEnd); + + var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(document, location.SourceSpan, cancellationToken).ConfigureAwait(false); + var classificationResult = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(documentSpan, cancellationToken).ConfigureAwait(false); + + var root = new ValueTrackingTreeItemViewModel( + document, + lineSpan.Start, + sourceText, + selectedSymbol, + classificationResult.ClassifiedSpans, + classificationFormatMap, + _typeMap, + _glyphService, + childViewModels); + + await ShowToolWindowAsync(root, cancellationToken).ConfigureAwait(false); + + ValueTrackingTreeItemViewModel CreateViewModel(ValueTrackedItem valueTrackedItem, ImmutableArray children = default) + => new ValueTrackedTreeItemViewModel( + valueTrackedItem, + solution, + classificationFormatMap, + _typeMap, + _glyphService, + valueTrackingService, + children); + } + + private async Task ShowToolWindowAsync(ValueTrackingTreeItemViewModel viewModel, CancellationToken cancellationToken) + { + var roslynPackage = await TryGetRoslynPackageAsync(cancellationToken).ConfigureAwait(false); + if (roslynPackage is null) + { + return; + } + + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + if (ValueTrackingToolWindow.Instance is null) + { + var factory = roslynPackage.GetAsyncToolWindowFactory(Guids.ValueTrackingToolWindowId); + + factory.CreateToolWindow(Guids.ValueTrackingToolWindowId, 0, viewModel); + await factory.InitializeToolWindowAsync(Guids.ValueTrackingToolWindowId, 0); + + ValueTrackingToolWindow.Instance = (ValueTrackingToolWindow)await roslynPackage.ShowToolWindowAsync( + typeof(ValueTrackingToolWindow), + 0, + true, + roslynPackage.DisposalToken).ConfigureAwait(false); + } + else + { + ValueTrackingToolWindow.Instance.Root = viewModel; + + await roslynPackage.ShowToolWindowAsync( + typeof(ValueTrackingToolWindow), + 0, + false, + roslynPackage.DisposalToken).ConfigureAwait(false); + } + } + + private async ValueTask TryGetRoslynPackageAsync(CancellationToken cancellationToken) + { + if (_roslynPackage is null) + { + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var shell = (IVsShell7?)await _serviceProvider.GetServiceAsync(typeof(SVsShell)).ConfigureAwait(true); + Assumes.Present(shell); + await shell.LoadPackageAsync(typeof(RoslynPackage).GUID); + + if (ErrorHandler.Succeeded(((IVsShell)shell).IsPackageLoaded(typeof(RoslynPackage).GUID, out var package))) + { + _roslynPackage = (RoslynPackage)package; + } + } + + return _roslynPackage; + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs new file mode 100644 index 0000000000000..75e7930090a3a --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.Commanding; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class ValueTrackingEditorCommandArgs : EditorCommandArgs + { + public ValueTrackingEditorCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer) + { + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs new file mode 100644 index 0000000000000..bfbad0894d99a --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Shell; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + [Guid(Guids.ValueTrackingToolWindowIdString)] + internal class ValueTrackingToolWindow : ToolWindowPane + { + public static ValueTrackingToolWindow? Instance { get; set; } + private readonly ValueTrackingTreeViewModel _viewModel; + + public ValueTrackingToolWindow(ValueTrackingTreeItemViewModel root) + : base(null) + { + if (Instance is not null) + { + throw new Exception("Cannot initialize the window more than once"); + } + + this.Caption = "Value Tracking"; + + _viewModel = new ValueTrackingTreeViewModel(root); + Content = new ValueTrackingTree(_viewModel); + } + + public ValueTrackingTreeItemViewModel Root + { + get => _viewModel.Roots.Single(); + set + { + _viewModel.Roots.Clear(); + _viewModel.Roots.Add(value); + } + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml new file mode 100644 index 0000000000000..c84201404c116 --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs new file mode 100644 index 0000000000000..87ee81bb63c8b --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + /// + /// Interaction logic for ValueTrackingTree.xaml + /// + internal partial class ValueTrackingTree : UserControl + { + private readonly ValueTrackingTreeViewModel _viewModel; + + public ValueTrackingTree(ValueTrackingTreeViewModel viewModel) + { + DataContext = _viewModel = viewModel; + InitializeComponent(); + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs new file mode 100644 index 0000000000000..c212d264d9d15 --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Microsoft.CodeAnalysis; +using System.Windows.Documents; +using System.Windows.Media; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.VisualStudio.Language.Intellisense; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class ValueTrackingTreeItemViewModel : TreeViewItemBase + { + private readonly SourceText _sourceText; + private readonly ISymbol _symbol; + private readonly IClassificationFormatMap _classificationFormatMap; + private readonly ClassificationTypeMap _classificationTypeMap; + private readonly IGlyphService _glyphService; + + protected Document Document { get; } + protected int LineNumber { get; } + + public ObservableCollection ChildItems { get; } = new(); + + public string FileDisplay => $"[{Document.Name}:{LineNumber}]"; + + public ImageSource GlyphImage => _symbol.GetGlyph().GetImageSource(_glyphService); + + public ImmutableArray ClassifiedSpans { get; } + + public IList Inlines + { + get + { + var classifiedTexts = ClassifiedSpans.SelectAsArray( + cs => new ClassifiedText(cs.ClassificationType, _sourceText.ToString(cs.TextSpan))); + + return classifiedTexts.ToInlines( + _classificationFormatMap, + _classificationTypeMap); + } + } + + public ValueTrackingTreeItemViewModel( + Document document, + int lineNumber, + SourceText sourceText, + ISymbol symbol, + ImmutableArray classifiedSpans, + IClassificationFormatMap classificationFormatMap, + ClassificationTypeMap classificationTypeMap, + IGlyphService glyphService, + ImmutableArray children = default) + { + Document = document; + LineNumber = lineNumber; + ClassifiedSpans = classifiedSpans; + + _sourceText = sourceText; + _symbol = symbol; + _classificationFormatMap = classificationFormatMap; + _classificationTypeMap = classificationTypeMap; + _glyphService = glyphService; + + if (!children.IsDefaultOrEmpty) + { + foreach (var child in children) + { + ChildItems.Add(child); + } + } + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeRootViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeRootViewModel.cs new file mode 100644 index 0000000000000..7617daec33d9d --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeRootViewModel.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class ValueTrackingTreeRootViewModel : TreeViewItemBase + { + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs new file mode 100644 index 0000000000000..aad24f697ff9a --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class ValueTrackingTreeViewModel : INotifyPropertyChanged + { + public ValueTrackingTreeViewModel(params ValueTrackingTreeItemViewModel[] roots) + { + foreach (var root in roots) + { + Roots.Add(root); + } + } + + public ValueTrackingTreeViewModel() + : this(GetSample()) + { } + + public ObservableCollection Roots { get; } = new(); + + public event PropertyChangedEventHandler? PropertyChanged; + + private void SetProperty(ref T field, T value, [CallerMemberName] string name = "") + { + if (EqualityComparer.Default.Equals(field, value)) + { + return; + } + + field = value; + NotifyPropertyChanged(name); + } + + private void NotifyPropertyChanged(string name) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + + public static ValueTrackingTreeItemViewModel[] GetSample() + { + return new ValueTrackingTreeItemViewModel[0]; + } + } +} diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf index 6ab46a153fc53..24399de7b3f2d 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf @@ -422,6 +422,21 @@ Přejít na implementaci + + Track Value Source + Track Value Source + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inicializovat Interactive přes projekt diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf index 39691247a87d9..cfa0d67550f38 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf @@ -422,6 +422,21 @@ Zur Implementierung wechseln + + Track Value Source + Track Value Source + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Interaktiv mit dem Projekt initialisieren diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf index 63941e8dbc88d..27ce157a48fe2 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf @@ -422,6 +422,21 @@ Ir a la implementación + + Track Value Source + Track Value Source + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inicializar el elemento interactivo con el proyecto diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf index 5eaa9a71bec58..5ebbababcaa00 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf @@ -422,6 +422,21 @@ Accéder à l'implémentation + + Track Value Source + Track Value Source + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Initialiser Interactive avec le projet diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf index bccc689169acb..3a18f2f4296e8 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf @@ -422,6 +422,21 @@ Vai all'implementazione + + Track Value Source + Track Value Source + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inizializza Interactive con il progetto diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf index 2ce24c0fc615d..40f558657176d 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf @@ -422,6 +422,21 @@ 実装に移動 + + Track Value Source + Track Value Source + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project プロジェクトでインタラクティブを初期化 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf index fed9ec757ba77..391f41638f42c 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf @@ -422,6 +422,21 @@ 구현으로 이동 + + Track Value Source + Track Value Source + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project 프로젝트에서 Interactive 초기화 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf index bb907800c0146..8dcb1fd6e47aa 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf @@ -422,6 +422,21 @@ Przejdź do implementacji + + Track Value Source + Track Value Source + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inicjuj środowisko interaktywne z projektem diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf index 5b6cd2c43c66c..6183276ec3987 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf @@ -422,6 +422,21 @@ Ir para Implementação + + Track Value Source + Track Value Source + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inicializar Interativo com o Projeto diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf index d68b6204e131e..aa6728418b0a7 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf @@ -422,6 +422,21 @@ Перейти к реализации + + Track Value Source + Track Value Source + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Инициализировать интерактивное окно с проектом diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf index 1d0204a6df606..6357ff5443c7e 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf @@ -422,6 +422,21 @@ Uygulamaya Git + + Track Value Source + Track Value Source + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Projeyi Etkileşimli Pencerede Başlat diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf index 316d4be560b9c..4b191897c05c1 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf @@ -422,6 +422,21 @@ 转到实现 + + Track Value Source + Track Value Source + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project 对交互窗口进行项目初始化 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf index c01b09934ee99..a4a5dfb4ea579 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf @@ -422,6 +422,21 @@ 前往實作 + + Track Value Source + Track Value Source + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project 使用專案將 Interactive 初始化