From ca7a08ba46940203b4eb1c0eb7708998f5b1da22 Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Mon, 25 Jan 2021 18:03:15 -0800 Subject: [PATCH] annotate syntax visualizer --- .../ColorPickerViewModel.cs | 2 +- .../ControlExtensions.cs | 7 +- .../SymbolDisplay/BasePropertyGridAdapter.cs | 4 +- .../SyntaxKindHelper.cs | 4 +- .../SyntaxVisualizerControl.xaml.cs | 160 +++++++++++------- .../TabStopPanel.cs | 4 +- .../HelperExtensionMethods.cs | 4 +- .../SyntaxVisualizerContainer.xaml.cs | 100 ++++++----- .../InProcess/InProcComponent.cs | 4 + 9 files changed, 172 insertions(+), 117 deletions(-) diff --git a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/ColorPickerViewModel.cs b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/ColorPickerViewModel.cs index 96c9f4687e..269184d4e5 100644 --- a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/ColorPickerViewModel.cs +++ b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/ColorPickerViewModel.cs @@ -184,7 +184,7 @@ private void UpdateColorComponents() } #region NotifyPropertyChanged - public event PropertyChangedEventHandler PropertyChanged; + public event PropertyChangedEventHandler? PropertyChanged; private bool SetProperty(ref T backingField, T value, [CallerMemberName] string propertyName = "") { if (EqualityComparer.Default.Equals(backingField, value)) diff --git a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/ControlExtensions.cs b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/ControlExtensions.cs index 81cefbb70b..e09144d915 100644 --- a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/ControlExtensions.cs +++ b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/ControlExtensions.cs @@ -29,7 +29,7 @@ internal static Color GetColorAtOffset(this GradientStopCollection collection, d } GradientStop left = stops[0]; - GradientStop right = null; + GradientStop? right = null; foreach (GradientStop stop in stops) { @@ -42,6 +42,11 @@ internal static Color GetColorAtOffset(this GradientStopCollection collection, d left = stop; } + if (right is null) + { + return left.Color; + } + double percent = Math.Round((offset - left.Offset) / (right.Offset - left.Offset), 3); byte a = (byte)((right.Color.A - left.Color.A) * percent + left.Color.A); byte r = (byte)((right.Color.R - left.Color.R) * percent + left.Color.R); diff --git a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/SymbolDisplay/BasePropertyGridAdapter.cs b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/SymbolDisplay/BasePropertyGridAdapter.cs index f2299d455c..d9470090ab 100644 --- a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/SymbolDisplay/BasePropertyGridAdapter.cs +++ b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/SymbolDisplay/BasePropertyGridAdapter.cs @@ -12,12 +12,12 @@ internal abstract class BasePropertyGridAdapter : ICustomTypeDescriptor public virtual string GetComponentName() => TypeDescriptor.GetClassName(this, true); public virtual TypeConverter GetConverter() => TypeDescriptor.GetConverter(this, true); public virtual EventDescriptor GetDefaultEvent() => TypeDescriptor.GetDefaultEvent(this, true); - public virtual PropertyDescriptor GetDefaultProperty() => TypeDescriptor.GetDefaultProperty(this, true); + public virtual PropertyDescriptor? GetDefaultProperty() => TypeDescriptor.GetDefaultProperty(this, true); public virtual object GetEditor(Type editorBaseType) => TypeDescriptor.GetEditor(this, editorBaseType, true); public virtual EventDescriptorCollection GetEvents() => TypeDescriptor.GetEvents(this, true); public virtual EventDescriptorCollection GetEvents(Attribute[] attributes) => TypeDescriptor.GetEvents(this, attributes, true); public virtual PropertyDescriptorCollection GetProperties() => TypeDescriptor.GetProperties(this, true); public virtual PropertyDescriptorCollection GetProperties(Attribute[] attributes) => TypeDescriptor.GetProperties(this, attributes, true); - public virtual object GetPropertyOwner(PropertyDescriptor pd) => null; + public virtual object? GetPropertyOwner(PropertyDescriptor pd) => null; } } diff --git a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/SyntaxKindHelper.cs b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/SyntaxKindHelper.cs index 15ce4a2611..af62333089 100644 --- a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/SyntaxKindHelper.cs +++ b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/SyntaxKindHelper.cs @@ -15,9 +15,9 @@ public static string GetKind(this SyntaxNodeOrToken nodeOrToken) { var kind = string.Empty; - if (nodeOrToken.IsNode) + if (nodeOrToken.IsNode && nodeOrToken.AsNode() is SyntaxNode node) { - kind = nodeOrToken.AsNode().GetKind(); + kind = node.GetKind(); } else { diff --git a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/SyntaxVisualizerControl.xaml.cs b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/SyntaxVisualizerControl.xaml.cs index 507834971e..55397cb8c2 100644 --- a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/SyntaxVisualizerControl.xaml.cs +++ b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/SyntaxVisualizerControl.xaml.cs @@ -14,6 +14,7 @@ using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Text; @@ -23,7 +24,9 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Text.Classification; + using Roslyn.SyntaxVisualizer.Control.SymbolDisplay; + using SystemInformation = System.Windows.Forms.SystemInformation; namespace Roslyn.SyntaxVisualizer.Control @@ -52,16 +55,16 @@ private class SyntaxTag { internal TextSpan Span { get; set; } internal TextSpan FullSpan { get; set; } - internal TreeViewItem ParentItem { get; set; } - internal string Kind { get; set; } - internal SyntaxNode SyntaxNode { get; set; } + internal TreeViewItem? ParentItem { get; set; } + internal string? Kind { get; set; } + internal SyntaxNode? SyntaxNode { get; set; } internal SyntaxToken SyntaxToken { get; set; } internal SyntaxTrivia SyntaxTrivia { get; set; } internal SyntaxCategory Category { get; set; } } #region Private State - private TreeViewItem _currentSelection; + private TreeViewItem? _currentSelection; private bool _isNavigatingFromSourceToTree; private bool _isNavigatingFromTreeToSource; private readonly System.Windows.Forms.PropertyGrid _propertyGrid; @@ -94,26 +97,26 @@ private class SyntaxTag /// we temporarily clear the specified foreground color and restore it when the item is /// unselected. This field is used to save and restore that foreground color. /// - private Brush _currentSelectionUnselectedForeground; + private Brush? _currentSelectionUnselectedForeground; private ImmutableArray classifiedSpans; #endregion #region Public Properties, Events - public SyntaxTree SyntaxTree { get; private set; } - public SemanticModel SemanticModel { get; private set; } + public SyntaxTree? SyntaxTree { get; private set; } + public SemanticModel? SemanticModel { get; private set; } public bool IsLazy { get; private set; } - public delegate void SyntaxNodeDelegate(SyntaxNode node); - public event SyntaxNodeDelegate SyntaxNodeDirectedGraphRequested; - public event SyntaxNodeDelegate SyntaxNodeNavigationToSourceRequested; + public delegate void SyntaxNodeDelegate(SyntaxNode? node); + public event SyntaxNodeDelegate? SyntaxNodeDirectedGraphRequested; + public event SyntaxNodeDelegate? SyntaxNodeNavigationToSourceRequested; public delegate void SyntaxTokenDelegate(SyntaxToken token); - public event SyntaxTokenDelegate SyntaxTokenDirectedGraphRequested; - public event SyntaxTokenDelegate SyntaxTokenNavigationToSourceRequested; + public event SyntaxTokenDelegate? SyntaxTokenDirectedGraphRequested; + public event SyntaxTokenDelegate? SyntaxTokenNavigationToSourceRequested; public delegate void SyntaxTriviaDelegate(SyntaxTrivia trivia); - public event SyntaxTriviaDelegate SyntaxTriviaDirectedGraphRequested; - public event SyntaxTriviaDelegate SyntaxTriviaNavigationToSourceRequested; + public event SyntaxTriviaDelegate? SyntaxTriviaDirectedGraphRequested; + public event SyntaxTriviaDelegate? SyntaxTriviaNavigationToSourceRequested; private ClassifiedSpan? _classifiedSpan; public ClassifiedSpan? ClassifiedSpan @@ -142,7 +145,10 @@ public ClassifiedSpan? ClassifiedSpan { colorLabel.Visibility = Visibility.Visible; colorPickerGrid.Visibility = Visibility.Visible; - colorPickerButton.Background = new SolidColorBrush(color.Value); + if (color is not null) + { + colorPickerButton.Background = new SolidColorBrush(color.Value); + } var textValue = _classifiedSpan?.ClassificationType; if (string.IsNullOrEmpty(textValue)) @@ -214,12 +220,16 @@ private void ColorPickerButton_Click(object sender, RoutedEventArgs e) { ThreadHelper.ThrowIfNotOnUIThread(); - var color = ((SolidColorBrush)colorPickerButton.Background).Color; + var color = ( (SolidColorBrush)colorPickerButton.Background ).Color; var popup = new ColorPickerWindow(color); if (popup.ShowDialog() == true) { - FontsAndColorsHelper.UpdateClassificationColor(_classifiedSpan.Value, popup.Color); + if (_classifiedSpan is not null) + { + FontsAndColorsHelper.UpdateClassificationColor(_classifiedSpan.Value, popup.Color); + } + colorPickerButton.Background = new SolidColorBrush(popup.Color); } @@ -323,7 +333,7 @@ public void Clear() // the children for any given item are only populated when the item is selected. If lazy is // false then the entire tree is populated at once (and this can result in bad performance when // displaying large trees). - public void DisplaySyntaxTree(SyntaxTree tree, SemanticModel model = null, bool lazy = true, Workspace workspace = null) + public void DisplaySyntaxTree(SyntaxTree tree, SemanticModel? model = null, bool lazy = true, Workspace? workspace = null) { if (tree != null) { @@ -347,7 +357,7 @@ public void DisplaySyntaxTree(SyntaxTree tree, SemanticModel model = null, bool // the children for any given item are only populated when the item is selected. If lazy is // false then the entire tree is populated at once (and this can result in bad performance when // displaying large trees). - public void DisplaySyntaxNode(SyntaxNode node, SemanticModel model = null, bool lazy = true) + public void DisplaySyntaxNode(SyntaxNode node, SemanticModel? model = null, bool lazy = true) { if (node != null) { @@ -359,11 +369,11 @@ public void DisplaySyntaxNode(SyntaxNode node, SemanticModel model = null, bool } // Select the SyntaxNode / SyntaxToken / SyntaxTrivia whose position best matches the supplied position. - public bool NavigateToBestMatch(int position, string kind = null, + public bool NavigateToBestMatch(int position, string? kind = null, SyntaxCategory category = SyntaxCategory.None, bool highlightMatch = false) { - TreeViewItem match = null; + TreeViewItem? match = null; if (treeView.HasItems && !_isNavigatingFromTreeToSource) { @@ -372,20 +382,18 @@ public bool NavigateToBestMatch(int position, string kind = null, _isNavigatingFromSourceToTree = false; } - var matchFound = match != null; - - if (highlightMatch && matchFound) + if (highlightMatch && match is not null) { match.Background = Brushes.Yellow; match.BorderBrush = Brushes.Black; match.BorderThickness = s_defaultBorderThickness; } - return matchFound; + return match is not null; } // Select the SyntaxNode / SyntaxToken / SyntaxTrivia whose span best matches the supplied span. - public bool NavigateToBestMatch(int start, int length, string kind = null, + public bool NavigateToBestMatch(int start, int length, string? kind = null, SyntaxCategory category = SyntaxCategory.None, bool highlightMatch = false) { @@ -393,11 +401,11 @@ public bool NavigateToBestMatch(int start, int length, string kind = null, } // Select the SyntaxNode / SyntaxToken / SyntaxTrivia whose span best matches the supplied span. - public bool NavigateToBestMatch(TextSpan span, string kind = null, + public bool NavigateToBestMatch(TextSpan span, string? kind = null, SyntaxCategory category = SyntaxCategory.None, bool highlightMatch = false) { - TreeViewItem match = null; + TreeViewItem? match = null; if (treeView.HasItems && !_isNavigatingFromTreeToSource) { @@ -406,16 +414,14 @@ public bool NavigateToBestMatch(TextSpan span, string kind = null, _isNavigatingFromSourceToTree = false; } - var matchFound = match != null; - - if (highlightMatch && matchFound) + if (highlightMatch && match is not null) { match.Background = Brushes.Yellow; match.BorderBrush = Brushes.Black; match.BorderThickness = s_defaultBorderThickness; } - return matchFound; + return match is not null; } #endregion @@ -447,7 +453,7 @@ private void DeepCollapse(TreeViewItem item) } // Ensure that the supplied treeview item and all its ancestors are expanded. - private void ExpandPathTo(TreeViewItem item) + private void ExpandPathTo(TreeViewItem? item) { if (item != null) { @@ -457,10 +463,10 @@ private void ExpandPathTo(TreeViewItem item) } // Select the SyntaxNode / SyntaxToken / SyntaxTrivia whose position best matches the supplied position. - private TreeViewItem NavigateToBestMatch(TreeViewItem current, int position, string kind = null, + private TreeViewItem? NavigateToBestMatch(TreeViewItem current, int position, string? kind = null, SyntaxCategory category = SyntaxCategory.None) { - TreeViewItem match = null; + TreeViewItem? match = null; if (current != null) { @@ -478,8 +484,8 @@ private TreeViewItem NavigateToBestMatch(TreeViewItem current, int position, str } } - if (match == null && (kind == null || currentTag.Kind == kind) && - (category == SyntaxCategory.None || category == currentTag.Category)) + if (match == null && ( kind == null || currentTag.Kind == kind ) && + ( category == SyntaxCategory.None || category == currentTag.Category )) { match = current; } @@ -490,17 +496,17 @@ private TreeViewItem NavigateToBestMatch(TreeViewItem current, int position, str } // Select the SyntaxNode / SyntaxToken / SyntaxTrivia whose span best matches the supplied span. - private TreeViewItem NavigateToBestMatch(TreeViewItem current, TextSpan span, string kind = null, + private TreeViewItem? NavigateToBestMatch(TreeViewItem current, TextSpan span, string? kind = null, SyntaxCategory category = SyntaxCategory.None) { - TreeViewItem match = null; + TreeViewItem? match = null; if (current != null) { var currentTag = (SyntaxTag)current.Tag; if (currentTag.FullSpan.Contains(span)) { - if ((currentTag.Span == span || currentTag.FullSpan == span) && (kind == null || currentTag.Kind == kind)) + if (( currentTag.Span == span || currentTag.FullSpan == span ) && ( kind == null || currentTag.Kind == kind )) { CollapseEverythingBut(current); match = current; @@ -511,7 +517,7 @@ private TreeViewItem NavigateToBestMatch(TreeViewItem current, TextSpan span, st foreach (TreeViewItem item in current.Items) { - if (category != SyntaxCategory.Operation && ((SyntaxTag)item.Tag).Category == SyntaxCategory.Operation) + if (category != SyntaxCategory.Operation && ( (SyntaxTag)item.Tag ).Category == SyntaxCategory.Operation) { // Do not prefer navigating to IOperation nodes when clicking in source code continue; @@ -524,8 +530,8 @@ private TreeViewItem NavigateToBestMatch(TreeViewItem current, TextSpan span, st } } - if (match == null && (kind == null || currentTag.Kind == kind) && - (category == SyntaxCategory.None || category == currentTag.Category)) + if (match == null && ( kind == null || currentTag.Kind == kind ) && + ( category == SyntaxCategory.None || category == currentTag.Category )) { match = current; } @@ -669,8 +675,13 @@ static IEnumerable GetOperationInterfaces(IOperation operation) } } - private void AddNode(TreeViewItem parentItem, SyntaxNode node) + private void AddNode(TreeViewItem? parentItem, SyntaxNode? node) { + if (node is null) + { + return; + } + var kind = node.GetKind(); var tag = new SyntaxTag() { @@ -724,10 +735,13 @@ private void AddNode(TreeViewItem parentItem, SyntaxNode node) // Remove placeholder child and populate real children. item.Items.RemoveAt(0); - var operation = SemanticModel.GetOperation(node); - if (operation is { Parent: null }) + if (SemanticModel is not null) { - AddOperation(item, operation); + var operation = SemanticModel.GetOperation(node); + if (operation is { Parent: null }) + { + AddOperation(item, operation); + } } foreach (var child in node.ChildNodesAndTokens()) @@ -877,7 +891,7 @@ private void AddTrivia(TreeViewItem parentItem, SyntaxTrivia trivia, bool isLead ParentItem = parentItem }; - var item = CreateTreeViewItem(tag, (isLeadingTrivia ? "Lead: " : "Trail: ") + tag.Kind + " " + trivia.Span.ToString(), trivia.ContainsDiagnostics); + var item = CreateTreeViewItem(tag, ( isLeadingTrivia ? "Lead: " : "Trail: " ) + tag.Kind + " " + trivia.Span.ToString(), trivia.ContainsDiagnostics); item.SetResourceReference(ForegroundProperty, SyntaxTriviaTextBrushKey); if (SyntaxTree != null && trivia.ContainsDiagnostics) @@ -988,7 +1002,7 @@ private TreeViewItem CreateTreeViewItem(SyntaxTag tag, string text, bool contain #endregion #region Private Helpers - Other - private void DisplaySymbolInPropertyGrid(ISymbol symbol) + private void DisplaySymbolInPropertyGrid(ISymbol? symbol) { if (symbol == null) { @@ -1010,9 +1024,9 @@ private void DisplaySymbolInPropertyGrid(ISymbol symbol) } } - private static TreeViewItem FindTreeViewItem(DependencyObject source) + private static TreeViewItem? FindTreeViewItem(DependencyObject source) { - while (source != null && !(source is TreeViewItem)) + while (source != null && !( source is TreeViewItem )) { if (source is ContentElement contentElement) { @@ -1024,7 +1038,7 @@ private static TreeViewItem FindTreeViewItem(DependencyObject source) } } - return (TreeViewItem)source; + return (TreeViewItem?)source; } #endregion @@ -1067,15 +1081,15 @@ private void TreeView_ContextMenuOpening(object sender, ContextMenuEventArgs e) } var directedSyntaxGraphEnabled = - (SyntaxNodeDirectedGraphRequested != null) && - (SyntaxTokenDirectedGraphRequested != null) && - (SyntaxTriviaDirectedGraphRequested != null); + ( SyntaxNodeDirectedGraphRequested != null ) && + ( SyntaxTokenDirectedGraphRequested != null ) && + ( SyntaxTriviaDirectedGraphRequested != null ); var symbolDetailsEnabled = - (SemanticModel != null) && - (((SyntaxTag)_currentSelection.Tag).Category == SyntaxCategory.SyntaxNode); + ( SemanticModel != null ) && + ( ( (SyntaxTag)_currentSelection.Tag ).Category == SyntaxCategory.SyntaxNode ); - if ((!directedSyntaxGraphEnabled) && (!symbolDetailsEnabled)) + if (( !directedSyntaxGraphEnabled ) && ( !symbolDetailsEnabled )) { e.Handled = true; } @@ -1133,7 +1147,7 @@ private void SymbolDetailsMenuItem_Click(object sender, RoutedEventArgs e) } var currentTag = (SyntaxTag)_currentSelection.Tag; - if ((SemanticModel != null) && (currentTag.Category == SyntaxCategory.SyntaxNode)) + if (( SemanticModel != null ) && ( currentTag.Category == SyntaxCategory.SyntaxNode ) && currentTag.SyntaxNode is not null) { var symbol = SemanticModel.GetSymbolInfo(currentTag.SyntaxNode).Symbol; if (symbol == null) @@ -1159,7 +1173,13 @@ private void TypeSymbolDetailsMenuItem_Click(object sender, RoutedEventArgs e) } var currentTag = (SyntaxTag)_currentSelection.Tag; - if ((SemanticModel != null) && (currentTag.Category == SyntaxCategory.SyntaxNode)) + if (currentTag.SyntaxNode is null) + { + e.Handled = true; + return; + } + + if (( SemanticModel != null ) && ( currentTag.Category == SyntaxCategory.SyntaxNode )) { var symbol = SemanticModel.GetTypeInfo(currentTag.SyntaxNode).Type; DisplaySymbolInPropertyGrid(symbol); @@ -1175,7 +1195,13 @@ private void ConvertedTypeSymbolDetailsMenuItem_Click(object sender, RoutedEvent } var currentTag = (SyntaxTag)_currentSelection.Tag; - if ((SemanticModel != null) && (currentTag.Category == SyntaxCategory.SyntaxNode)) + if (currentTag.SyntaxNode is null) + { + e.Handled = true; + return; + } + + if (( SemanticModel != null ) && ( currentTag.Category == SyntaxCategory.SyntaxNode )) { var symbol = SemanticModel.GetTypeInfo(currentTag.SyntaxNode).ConvertedType; DisplaySymbolInPropertyGrid(symbol); @@ -1191,7 +1217,13 @@ private void AliasSymbolDetailsMenuItem_Click(object sender, RoutedEventArgs e) } var currentTag = (SyntaxTag)_currentSelection.Tag; - if ((SemanticModel != null) && (currentTag.Category == SyntaxCategory.SyntaxNode)) + if (currentTag.SyntaxNode is null) + { + e.Handled = true; + return; + } + + if (( SemanticModel != null ) && ( currentTag.Category == SyntaxCategory.SyntaxNode )) { var symbol = SemanticModel.GetAliasInfo(currentTag.SyntaxNode); DisplaySymbolInPropertyGrid(symbol); @@ -1207,7 +1239,9 @@ private void ConstantValueDetailsMenuItem_Click(object sender, RoutedEventArgs e } var currentTag = (SyntaxTag)_currentSelection.Tag; - if ((SemanticModel != null) && (currentTag.Category == SyntaxCategory.SyntaxNode)) + if (SemanticModel != null && + currentTag.Category == SyntaxCategory.SyntaxNode && + currentTag.SyntaxNode is not null) { var value = SemanticModel.GetConstantValue(currentTag.SyntaxNode); kindTextLabel.Visibility = Visibility.Hidden; diff --git a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/TabStopPanel.cs b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/TabStopPanel.cs index b23843966a..7add4f10da 100644 --- a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/TabStopPanel.cs +++ b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Control/TabStopPanel.cs @@ -14,13 +14,13 @@ namespace Roslyn.SyntaxVisualizer.Control internal class TabStopPanel : Panel { private readonly HwndHost _wpfHost; - private PropertyGrid _propertyGrid; + private PropertyGrid? _propertyGrid; public TabStopPanel(HwndHost wpfHost) { _wpfHost = wpfHost; } - public PropertyGrid PropertyGrid + public PropertyGrid? PropertyGrid { get => _propertyGrid; set diff --git a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Extension/HelperExtensionMethods.cs b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Extension/HelperExtensionMethods.cs index e580b801ea..91be1f6cbb 100644 --- a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Extension/HelperExtensionMethods.cs +++ b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Extension/HelperExtensionMethods.cs @@ -12,9 +12,9 @@ namespace Roslyn.SyntaxVisualizer.Extension { internal static class HelperExtensionMethods { - internal static IWpfTextView ToWpfTextView(this IVsWindowFrame vsWindowFrame) + internal static IWpfTextView? ToWpfTextView(this IVsWindowFrame vsWindowFrame) { - IWpfTextView wpfTextView = null; + IWpfTextView? wpfTextView = null; var vsTextView = VsShellUtilities.GetTextView(vsWindowFrame); if (vsTextView != null) diff --git a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Extension/SyntaxVisualizerContainer.xaml.cs b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Extension/SyntaxVisualizerContainer.xaml.cs index 72c5c3a930..2427b98147 100644 --- a/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Extension/SyntaxVisualizerContainer.xaml.cs +++ b/src/VisualStudio.Roslyn.SDK/SyntaxVisualizer/Roslyn.SyntaxVisualizer.Extension/SyntaxVisualizerContainer.xaml.cs @@ -8,6 +8,7 @@ using System.Windows.Controls; using System.Windows.Threading; using System.Xml.Linq; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Text; @@ -17,6 +18,7 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; + using Roslyn.SyntaxVisualizer.DgmlHelper; namespace Roslyn.SyntaxVisualizer.Extension @@ -26,11 +28,11 @@ namespace Roslyn.SyntaxVisualizer.Extension internal partial class SyntaxVisualizerContainer : UserControl, IVsRunningDocTableEvents, IVsSolutionEvents, IDisposable { private readonly SyntaxVisualizerToolWindow parent; - private IWpfTextView activeWpfTextView; - private IClassificationFormatMap activeClassificationFormatMap; - private IEditorFormatMap activeEditorFormatMap; - private SyntaxTree activeSyntaxTree; - private DispatcherTimer typingTimer; + private IWpfTextView? activeWpfTextView; + private IClassificationFormatMap? activeClassificationFormatMap; + private IEditorFormatMap? activeEditorFormatMap; + private SyntaxTree? activeSyntaxTree; + private DispatcherTimer? typingTimer; private const string CSharpContentType = "CSharp"; private const string VisualBasicContentType = "Basic"; @@ -62,7 +64,7 @@ internal SyntaxVisualizerContainer(SyntaxVisualizerToolWindow parent) UpdateThemedColors(); - syntaxVisualizer.SyntaxNodeNavigationToSourceRequested += node => NavigateToSource(node.Span); + syntaxVisualizer.SyntaxNodeNavigationToSourceRequested += node => NavigateToSource(node?.Span); syntaxVisualizer.SyntaxTokenNavigationToSourceRequested += token => NavigateToSource(token.Span); syntaxVisualizer.SyntaxTriviaNavigationToSourceRequested += trivia => NavigateToSource(trivia.Span); } @@ -78,7 +80,10 @@ internal void UpdateThemedColors() if (activeClassificationFormatMap != null && activeEditorFormatMap != null) { var classificationTypeRegistryService = GetMefService(); - syntaxVisualizer.SetTreeViewColors(classificationTypeRegistryService, activeClassificationFormatMap, activeEditorFormatMap); + if (classificationTypeRegistryService is not null) + { + syntaxVisualizer.SetTreeViewColors(classificationTypeRegistryService, activeClassificationFormatMap, activeEditorFormatMap); + } } } @@ -121,7 +126,7 @@ internal void Clear() } #region Helpers - GetService - private static Microsoft.VisualStudio.OLE.Interop.IServiceProvider globalServiceProvider; + private static Microsoft.VisualStudio.OLE.Interop.IServiceProvider? globalServiceProvider; private static Microsoft.VisualStudio.OLE.Interop.IServiceProvider GlobalServiceProvider { get @@ -138,11 +143,11 @@ private static Microsoft.VisualStudio.OLE.Interop.IServiceProvider GlobalService } } - private TServiceInterface GetService() + private TServiceInterface? GetService() where TServiceInterface : class where TService : class { - TServiceInterface service = null; + TServiceInterface? service = null; if (parent != null) { @@ -152,12 +157,12 @@ private TServiceInterface GetService() return service; } - private static object GetService( + private static object? GetService( Microsoft.VisualStudio.OLE.Interop.IServiceProvider serviceProvider, Guid guidService, bool unique) { var guidInterface = VSConstants.IID_IUnknown; var ptr = IntPtr.Zero; - object service = null; + object? service = null; #pragma warning disable VSTHRD010 // Invoke single-threaded types on Main thread if (serviceProvider.QueryService(ref guidService, ref guidInterface, out ptr) == 0 && @@ -184,17 +189,17 @@ private static object GetService( return service; } - private static TServiceInterface GetService( + private static TServiceInterface? GetService( Microsoft.VisualStudio.OLE.Interop.IServiceProvider serviceProvider) where TServiceInterface : class where TService : class { - return (TServiceInterface)GetService(serviceProvider, typeof(TService).GUID, false); + return (TServiceInterface?)GetService(serviceProvider, typeof(TService).GUID, false); } - private static TServiceInterface GetMefService() where TServiceInterface : class + private static TServiceInterface? GetMefService() where TServiceInterface : class { - TServiceInterface service = null; + TServiceInterface? service = null; var componentModel = GetService(GlobalServiceProvider); if (componentModel != null) @@ -209,8 +214,8 @@ private static TServiceInterface GetMefService() where TServi #region Helpers - Initialize and Dispose IVsRunningDocumentTable private uint runningDocumentTableCookie; - private IVsRunningDocumentTable runningDocumentTable; - private IVsRunningDocumentTable RunningDocumentTable + private IVsRunningDocumentTable? runningDocumentTable; + private IVsRunningDocumentTable? RunningDocumentTable { get { @@ -238,7 +243,7 @@ void IDisposable.Dispose() if (runningDocumentTableCookie != 0) { #pragma warning disable VSTHRD010 // Invoke single-threaded types on Main thread - runningDocumentTable.UnadviseRunningDocTableEvents(runningDocumentTableCookie); + runningDocumentTable?.UnadviseRunningDocTableEvents(runningDocumentTableCookie); #pragma warning restore VSTHRD010 // Invoke single-threaded types on Main thread runningDocumentTableCookie = 0; } @@ -268,7 +273,7 @@ private void RefreshSyntaxVisualizer() var activeSemanticModel = ThreadHelper.JoinableTaskFactory.Run(() => document.GetSemanticModelAsync()); // Display the SyntaxTree. - if (contentType.IsOfType(VisualBasicContentType) || contentType.IsOfType(CSharpContentType)) + if (( contentType.IsOfType(VisualBasicContentType) || contentType.IsOfType(CSharpContentType) ) && activeSyntaxTree is not null) { syntaxVisualizer.DisplaySyntaxTree(activeSyntaxTree, activeSemanticModel, workspace: document.Project.Solution.Workspace); } @@ -290,11 +295,11 @@ private void NavigateFromSource() } // When user clicks on a particular item in the treeview select the corresponding text in the editor. - private void NavigateToSource(TextSpan span) + private void NavigateToSource(TextSpan? span) { - if (IsVisible && activeWpfTextView != null) + if (IsVisible && activeWpfTextView != null && span is TextSpan nonNullableSpan) { - var snapShotSpan = span.ToSnapshotSpan(activeWpfTextView.TextBuffer.CurrentSnapshot); + var snapShotSpan = nonNullableSpan.ToSnapshotSpan(activeWpfTextView.TextBuffer.CurrentSnapshot); // See SyntaxVisualizerToolWindow_GotFocus and SyntaxVisualizerToolWindow_LostFocus // for some notes about selection opacity and why it needs to be manipulated. @@ -337,7 +342,7 @@ private void HandleTextViewLostFocus(object sender, EventArgs e) private void HandleTypingTimerTimeout(object sender, EventArgs e) { - typingTimer.Stop(); + typingTimer?.Stop(); RefreshSyntaxVisualizer(); } @@ -361,10 +366,16 @@ int IVsRunningDocTableEvents.OnBeforeDocumentWindowShow(uint docCookie, int isFi var classificationFormatMapService = GetMefService(); var editorFormatMapService = GetMefService(); - activeClassificationFormatMap = classificationFormatMapService.GetClassificationFormatMap(activeWpfTextView); - activeClassificationFormatMap.ClassificationFormatMappingChanged += HandleFormatMappingChanged; - activeEditorFormatMap = editorFormatMapService.GetEditorFormatMap(activeWpfTextView); - activeEditorFormatMap.FormatMappingChanged += HandleFormatMappingChanged; + activeClassificationFormatMap = classificationFormatMapService?.GetClassificationFormatMap(activeWpfTextView); + if (activeClassificationFormatMap is not null) + { + activeClassificationFormatMap.ClassificationFormatMappingChanged += HandleFormatMappingChanged; + } + activeEditorFormatMap = editorFormatMapService?.GetEditorFormatMap(activeWpfTextView); + if (activeEditorFormatMap is not null) + { + activeEditorFormatMap.FormatMappingChanged += HandleFormatMappingChanged; + } UpdateThemedColors(); RefreshSyntaxVisualizer(); @@ -414,9 +425,9 @@ int IVsRunningDocTableEvents.OnAfterSave(uint docCookie) #endregion #region Event Handlers - Directed Syntax Graph / IVsSolutionEvents Events - private string dgmlFilePath; - private IVsFileChangeEx fileChangeService; - private IVsFileChangeEx FileChangeService + private string? dgmlFilePath; + private IVsFileChangeEx? fileChangeService; + private IVsFileChangeEx? FileChangeService { get { @@ -429,8 +440,8 @@ private IVsFileChangeEx FileChangeService } } - private IVsSolution solutionService; - private IVsSolution SolutionService + private IVsSolution? solutionService; + private IVsSolution? SolutionService { get { @@ -443,7 +454,7 @@ private IVsSolution SolutionService } } - private void DisplayDgml(XElement dgml) + private void DisplayDgml(XElement? dgml) { uint cookie; const int TRUE = -1; @@ -466,8 +477,9 @@ private void DisplayDgml(XElement dgml) #pragma warning restore VSTHRD010 // Invoke single-threaded types on Main thread out var docUIHierarchy, out var docItemId, out var docWindowFrame) && docWindowFrame != null) { + if (RunningDocumentTable is not null && #pragma warning disable VSTHRD010 // Invoke single-threaded types on Main thread - if (RunningDocumentTable.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, dgmlFilePath, + RunningDocumentTable.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, dgmlFilePath, #pragma warning restore VSTHRD010 // Invoke single-threaded types on Main thread out var docHierarchy, out docItemId, out var docDataIUnknownPointer, @@ -490,16 +502,16 @@ private void DisplayDgml(XElement dgml) // The below call ensures that there are no pop-ups from Visual Studio // prompting the user to reload the file each time it is changed. #pragma warning disable VSTHRD010 // Invoke single-threaded types on Main thread - FileChangeService.IgnoreFile(0, dgmlFilePath, TRUE); + FileChangeService?.IgnoreFile(0, dgmlFilePath, TRUE); #pragma warning restore VSTHRD010 // Invoke single-threaded types on Main thread // Update the file on disk with the new directed syntax graph. - dgml.Save(dgmlFilePath); + dgml?.Save(dgmlFilePath); // The below calls ensure that the file is refreshed inside Visual Studio // so that the latest contents are displayed to the user. #pragma warning disable VSTHRD010 // Invoke single-threaded types on Main thread - FileChangeService.SyncFile(dgmlFilePath); + FileChangeService?.SyncFile(dgmlFilePath); persistDocDataService.ReloadDocData((uint)_VSRELOADDOCDATA.RDD_IgnoreNextFileChange); #pragma warning restore VSTHRD010 // Invoke single-threaded types on Main thread @@ -519,7 +531,7 @@ private void DisplayDgml(XElement dgml) else { // Update the file on disk with the new directed syntax graph. - dgml.Save(dgmlFilePath); + dgml?.Save(dgmlFilePath); // Open the new directed syntax graph in the 'design' view. VsShellUtilities.OpenDocument( @@ -532,18 +544,18 @@ private void DisplayDgml(XElement dgml) // This ensures that the file won't be persisted in the .suo file and that it therefore won't get re-opened // when the solution is re-opened. #pragma warning disable VSTHRD010 // Invoke single-threaded types on Main thread - SolutionService.AdviseSolutionEvents(this, out cookie); + SolutionService?.AdviseSolutionEvents(this, out cookie); #pragma warning restore VSTHRD010 // Invoke single-threaded types on Main thread } } - private void DisplaySyntaxNodeDgml(SyntaxNode node) + private void DisplaySyntaxNodeDgml(SyntaxNode? node) { if (activeWpfTextView != null) { var snapshot = activeWpfTextView.TextBuffer.CurrentSnapshot; var contentType = snapshot.ContentType; - XElement dgml = null; + XElement? dgml = null; if (contentType.IsOfType(CSharpContentType) || contentType.IsOfType(VisualBasicContentType)) { @@ -560,7 +572,7 @@ private void DisplaySyntaxTokenDgml(SyntaxToken token) { var snapshot = activeWpfTextView.TextBuffer.CurrentSnapshot; var contentType = snapshot.ContentType; - XElement dgml = null; + XElement? dgml = null; if (contentType.IsOfType(CSharpContentType) || contentType.IsOfType(VisualBasicContentType)) { @@ -577,7 +589,7 @@ private void DisplaySyntaxTriviaDgml(SyntaxTrivia trivia) { var snapshot = activeWpfTextView.TextBuffer.CurrentSnapshot; var contentType = snapshot.ContentType; - XElement dgml = null; + XElement? dgml = null; if (contentType.IsOfType(CSharpContentType) || contentType.IsOfType(VisualBasicContentType)) { diff --git a/tests/VisualStudio.Roslyn.SDK/Roslyn.SDK.IntegrationTests/InProcess/InProcComponent.cs b/tests/VisualStudio.Roslyn.SDK/Roslyn.SDK.IntegrationTests/InProcess/InProcComponent.cs index f489684626..307a699ca1 100644 --- a/tests/VisualStudio.Roslyn.SDK/Roslyn.SDK.IntegrationTests/InProcess/InProcComponent.cs +++ b/tests/VisualStudio.Roslyn.SDK/Roslyn.SDK.IntegrationTests/InProcess/InProcComponent.cs @@ -40,9 +40,13 @@ protected async Task GetGlobalServiceAsync() { await JoinableTaskFactory.SwitchToMainThreadAsync(); +#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. +#pragma warning disable CS8603 // Possible null reference return. var serviceProvider = (IAsyncServiceProvider2)await AsyncServiceProvider.GlobalProvider.GetServiceAsync(typeof(SAsyncServiceProvider)); Assumes.Present(serviceProvider); return (TInterface)await serviceProvider.GetServiceAsync(typeof(TService)); +#pragma warning restore CS8603 // Possible null reference return. +#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. } protected async Task GetComponentModelServiceAsync()