File: Classification\ClassificationTests.vb
Web Access
Project: src\src\EditorFeatures\Test2\Microsoft.CodeAnalysis.EditorFeatures2.UnitTests.vbproj (Microsoft.CodeAnalysis.EditorFeatures2.UnitTests)
' 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.
 
Imports System.Collections.Immutable
Imports System.Composition
Imports System.Threading
Imports Microsoft.CodeAnalysis.Classification
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.Editor.Shared.Extensions
Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities
Imports Microsoft.CodeAnalysis.Editor.Tagging
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Host
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.Shared.TestHooks
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.UnitTests
Imports Microsoft.VisualStudio.Text
Imports Microsoft.VisualStudio.Text.Classification
Imports Roslyn.Utilities
 
Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Classification
    <UseExportProvider>
    Public Class ClassificationTests
        <Fact, WorkItem("https://github.com/dotnet/roslyn/pull/66245")>
        Public Async Function TestClassificationAndHighlight1() As Task
            Using workspace = EditorTestWorkspace.Create(
                <Workspace>
                    <Project Language="C#" AssemblyName="TestAssembly" CommonReferences="true">
                        <Document>
                        using System.Text.RegularExpressions;
 
                        class C
                        {
                           [| Regex |]re = new Regex("()");
                        }
                        </Document>
                    </Project>
                </Workspace>)
 
                Dim document = workspace.CurrentSolution.Projects.Single().Documents.Single()
                Dim text = Await document.GetTextAsync()
                Dim referenceSpan = workspace.Documents.Single().SelectedSpans.Single()
 
                Dim spansAndHighlightSpan = Await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(
                    New DocumentSpan(document, referenceSpan),
                    classifiedSpans:=Nothing,
                    ClassificationOptions.Default, CancellationToken.None)
 
                ' This is the classification of the line, starting at the beginning of the highlight, and going to the end of that line.
                Assert.Equal(
"(text, '<spaces>', [154..155))
(class name, 'Regex', [155..160))
(text, '<spaces>', [160..161))
(field name, 're', [161..163))
(text, '<spaces>', [163..164))
(operator, '=', [164..165))
(text, '<spaces>', [165..166))
(keyword, 'new', [166..169))
(text, '<spaces>', [169..170))
(class name, 'Regex', [170..175))
(punctuation, '(', [175..176))
(string, '""', [176..177))
(regex - grouping, '(', [177..178))
(regex - grouping, ')', [178..179))
(string, '""', [179..180))
(punctuation, ')', [180..181))
(punctuation, ';', [181..182))", String.Join(vbCrLf, spansAndHighlightSpan.ClassifiedSpans.Select(Function(s) ToTestString(text, s))))
 
                ' The portion of the classified spans to highlight goes from the start of the classified spans to the
                ' length of the original reference span.
                Assert.Equal(New TextSpan(0, referenceSpan.Length), spansAndHighlightSpan.HighlightSpan)
            End Using
        End Function
 
        <Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65926")>
        Public Async Function TestEmbeddedClassifications1() As Task
            Using workspace = EditorTestWorkspace.Create(
                <Workspace>
                    <Project Language="C#" AssemblyName="TestAssembly" CommonReferences="true">
                        <Document>
                        using System.Text.RegularExpressions;
 
                        class C
                        {
                            private Regex re = new Regex("()");
                        }
                        </Document>
                    </Project>
                </Workspace>)
 
                Dim document = workspace.CurrentSolution.Projects.Single().Documents.Single()
                Dim text = Await document.GetTextAsync()
 
                Dim spans = Await ClassifierHelper.GetClassifiedSpansAsync(
                    document, New TextSpan(0, text.Length), ClassificationOptions.Default, includeAdditiveSpans:=False, CancellationToken.None)
 
                Assert.Equal(
"(text, '<spaces>', [0..26))
(keyword, 'using', [26..31))
(text, '<spaces>', [31..32))
(namespace name, 'System', [32..38))
(operator, '.', [38..39))
(namespace name, 'Text', [39..43))
(operator, '.', [43..44))
(namespace name, 'RegularExpressions', [44..62))
(punctuation, ';', [62..63))
(text, '<spaces>', [63..91))
(keyword, 'class', [91..96))
(text, '<spaces>', [96..97))
(class name, 'C', [97..98))
(text, '<spaces>', [98..124))
(punctuation, '{', [124..125))
(text, '<spaces>', [125..155))
(keyword, 'private', [155..162))
(text, '<spaces>', [162..163))
(class name, 'Regex', [163..168))
(text, '<spaces>', [168..169))
(field name, 're', [169..171))
(text, '<spaces>', [171..172))
(operator, '=', [172..173))
(text, '<spaces>', [173..174))
(keyword, 'new', [174..177))
(text, '<spaces>', [177..178))
(class name, 'Regex', [178..183))
(punctuation, '(', [183..184))
(string, '""', [184..185))
(regex - grouping, '(', [185..186))
(regex - grouping, ')', [186..187))
(string, '""', [187..188))
(punctuation, ')', [188..189))
(punctuation, ';', [189..190))
(text, '<spaces>', [190..216))
(punctuation, '}', [216..217))", String.Join(vbCrLf, spans.Select(Function(s) ToTestString(text, s))))
            End Using
        End Function
 
        <Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63702")>
        Public Async Function TestEmbeddedClassifications2() As Task
            Using workspace = EditorTestWorkspace.Create(
                <Workspace>
                    <Project Language="C#" AssemblyName="TestAssembly" CommonReferences="true">
                        <Document>
                        class C
                        {
                            void B() => M("W\"X\tY\'Z");
                            void M(string s) { }
                        }
                        </Document>
                    </Project>
                </Workspace>)
 
                Dim document = workspace.CurrentSolution.Projects.Single().Documents.Single()
                Dim text = Await document.GetTextAsync()
 
                Dim spans = Await ClassifierHelper.GetClassifiedSpansAsync(
                    document, New TextSpan(0, text.Length), ClassificationOptions.Default, includeAdditiveSpans:=False, CancellationToken.None)
 
                Assert.Equal(
"(text, '<spaces>', [0..26))
(keyword, 'class', [26..31))
(text, '<spaces>', [31..32))
(class name, 'C', [32..33))
(text, '<spaces>', [33..59))
(punctuation, '{', [59..60))
(text, '<spaces>', [60..90))
(keyword, 'void', [90..94))
(text, '<spaces>', [94..95))
(method name, 'B', [95..96))
(punctuation, '(', [96..97))
(punctuation, ')', [97..98))
(text, '<spaces>', [98..99))
(operator, '=>', [99..101))
(text, '<spaces>', [101..102))
(method name, 'M', [102..103))
(punctuation, '(', [103..104))
(string, '""W', [104..106))
(string - escape character, '\""', [106..108))
(string, 'X', [108..109))
(string - escape character, '\t', [109..111))
(string, 'Y', [111..112))
(string - escape character, '\'', [112..114))
(string, 'Z""', [114..116))
(punctuation, ')', [116..117))
(punctuation, ';', [117..118))
(text, '<spaces>', [118..148))
(keyword, 'void', [148..152))
(text, '<spaces>', [152..153))
(method name, 'M', [153..154))
(punctuation, '(', [154..155))
(keyword, 'string', [155..161))
(text, '<spaces>', [161..162))
(parameter name, 's', [162..163))
(punctuation, ')', [163..164))
(text, '<spaces>', [164..165))
(punctuation, '{', [165..166))
(text, '<spaces>', [166..167))
(punctuation, '}', [167..168))
(text, '<spaces>', [168..194))
(punctuation, '}', [194..195))", String.Join(vbCrLf, spans.Select(Function(s) ToTestString(text, s))))
            End Using
        End Function
 
        <Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66507")>
        Public Async Function TestUtf8StringSuffix() As Task
            Using workspace = EditorTestWorkspace.Create(
                <Workspace>
                    <Project Language="C#" AssemblyName="TestAssembly" CommonReferences="true">
                        <Document>
                        [|var v = "goo"u8;|]
                        </Document>
                    </Project>
                </Workspace>)
 
                Dim document = workspace.CurrentSolution.Projects.Single().Documents.Single()
                Dim text = Await document.GetTextAsync()
                Dim referenceSpan = workspace.Documents.Single().SelectedSpans.Single()
 
                Dim spansAndHighlightSpan = Await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(
                    New DocumentSpan(document, referenceSpan),
                    classifiedSpans:=Nothing,
                    ClassificationOptions.Default, CancellationToken.None)
 
                ' string classification should not overlap u8 classification.
                AssertEx.Equal(
"(keyword, 'var', [26..29))
(text, '<spaces>', [29..30))
(local name, 'v', [30..31))
(text, '<spaces>', [31..32))
(operator, '=', [32..33))
(text, '<spaces>', [33..34))
(string, '""goo""', [34..39))
(keyword, 'u8', [39..41))
(punctuation, ';', [41..42))", String.Join(vbCrLf, spansAndHighlightSpan.ClassifiedSpans.Select(Function(s) ToTestString(text, s))))
            End Using
        End Function
 
        Private Shared Function ToTestString(text As SourceText, span As ClassifiedSpan) As String
            Dim subText = text.ToString(span.TextSpan)
            Return $"({span.ClassificationType}, '{If(subText.Trim() = "", "<spaces>",
                subText)}', {span.TextSpan})"
        End Function
 
        <WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/13753")>
        Public Async Function TestSemanticClassificationWithoutSyntaxTree() As Task
            Dim workspaceDefinition =
            <Workspace>
                <Project Language="NoCompilation" AssemblyName="TestAssembly" CommonReferencesPortable="true">
                    <Document>
                        var x = {}; // e.g., TypeScript code or anything else that doesn't support compilations
                    </Document>
                </Project>
            </Workspace>
 
            Dim composition = EditorTestCompositions.EditorFeatures.AddParts(
                GetType(NoCompilationContentTypeDefinitions),
                GetType(NoCompilationContentTypeLanguageService),
                GetType(NoCompilationEditorClassificationService))
 
            Using workspace = EditorTestWorkspace.Create(workspaceDefinition, composition:=composition)
                Dim listenerProvider = workspace.ExportProvider.GetExportedValue(Of IAsynchronousOperationListenerProvider)
 
                Dim provider = New SemanticClassificationViewTaggerProvider(
                    workspace.GetService(Of TaggerHost),
                    workspace.GetService(Of ClassificationTypeMap))
 
                Dim buffer = workspace.Documents.First().GetTextBuffer()
                Using tagger = provider.CreateTagger(
                    workspace.Documents.First().GetTextView(),
                    buffer)
 
                    Using edit = buffer.CreateEdit()
                        edit.Insert(0, " ")
                        edit.Apply()
                    End Using
 
                    Await listenerProvider.GetWaiter(FeatureAttribute.Classification).ExpeditedWaitAsync()
 
                    ' Note: we don't actually care what results we get back.  We're just
                    ' verifying that we don't crash because the SemanticViewTagger ends up
                    ' calling SyntaxTree/SemanticModel code.
                    tagger.GetTags(New NormalizedSnapshotSpanCollection(
                            New SnapshotSpan(buffer.CurrentSnapshot, New Span(0, 1))))
                End Using
            End Using
        End Function
 
        <WpfFact>
        Public Sub TestFailOverOfMissingClassificationType()
            Dim exportProvider = EditorTestCompositions.EditorFeatures.ExportProviderFactory.CreateExportProvider()
 
            Dim typeMap = exportProvider.GetExportedValue(Of ClassificationTypeMap)
            Dim formatMap = exportProvider.GetExportedValue(Of IClassificationFormatMapService).GetClassificationFormatMap("tooltip")
 
            Dim classifiedText = New ClassifiedText("UnknownClassificationType", "dummy")
            Dim run = classifiedText.ToRun(formatMap, typeMap)
 
            Assert.NotNull(run)
        End Sub
 
        <WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/13753")>
        Public Async Function TestWrongDocument() As Task
            Dim workspaceDefinition =
            <Workspace>
                <Project Language="NoCompilation" AssemblyName="NoCompilationAssembly" CommonReferencesPortable="true">
                    <Document>
                        var x = {}; // e.g., TypeScript code or anything else that doesn't support compilations
                    </Document>
                </Project>
                <Project Language="C#" AssemblyName="CSharpAssembly" CommonReferencesPortable="true">
                </Project>
            </Workspace>
 
            Dim composition = EditorTestCompositions.EditorFeatures.AddParts(
                GetType(NoCompilationContentTypeLanguageService),
                GetType(NoCompilationContentTypeDefinitions))
 
            Using workspace = EditorTestWorkspace.Create(workspaceDefinition, composition:=composition)
                Dim project = workspace.CurrentSolution.Projects.First(Function(p) p.Language = LanguageNames.CSharp)
                Dim classificationService = project.Services.GetService(Of IClassificationService)()
 
                Dim wrongDocument = workspace.CurrentSolution.Projects.First(Function(p) p.Language = "NoCompilation").Documents.First()
                Dim text = Await wrongDocument.GetTextAsync(CancellationToken.None)
 
                ' make sure we don't crash with wrong document
                Dim result = New SegmentedList(Of ClassifiedSpan)()
                Await classificationService.AddSyntacticClassificationsAsync(wrongDocument, New TextSpan(0, text.Length), result, CancellationToken.None)
                Await classificationService.AddSemanticClassificationsAsync(wrongDocument, New TextSpan(0, text.Length), options:=Nothing, result, CancellationToken.None)
            End Using
        End Function
 
        <ExportLanguageService(GetType(IClassificationService), NoCompilationConstants.LanguageName, ServiceLayer.Test), [Shared], PartNotDiscoverable>
        Private Class NoCompilationEditorClassificationService
            Implements IClassificationService
 
            <ImportingConstructor>
            <Obsolete(MefConstruction.ImportingConstructorMessage, True)>
            Public Sub New()
            End Sub
 
            Public Sub AddLexicalClassifications(text As SourceText, textSpan As TextSpan, result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) Implements IClassificationService.AddLexicalClassifications
            End Sub
 
            Public Sub AddSyntacticClassifications(services As SolutionServices, root As SyntaxNode, textSpans As ImmutableArray(Of TextSpan), result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) Implements IClassificationService.AddSyntacticClassifications
            End Sub
 
            Public Function AddSemanticClassificationsAsync(document As Document, textSpans As ImmutableArray(Of TextSpan), options As ClassificationOptions, result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) As Task Implements IClassificationService.AddSemanticClassificationsAsync
                Return Task.CompletedTask
            End Function
 
            Public Function AddSyntacticClassificationsAsync(document As Document, textSpans As ImmutableArray(Of TextSpan), result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) As Task Implements IClassificationService.AddSyntacticClassificationsAsync
                Return Task.CompletedTask
            End Function
 
            Public Function AdjustStaleClassification(text As SourceText, classifiedSpan As ClassifiedSpan) As ClassifiedSpan Implements IClassificationService.AdjustStaleClassification
            End Function
 
            Public Function ComputeSyntacticChangeRangeAsync(oldDocument As Document, newDocument As Document, timeout As TimeSpan, cancellationToken As CancellationToken) As ValueTask(Of TextChangeRange?) Implements IClassificationService.ComputeSyntacticChangeRangeAsync
                Return New ValueTask(Of TextChangeRange?)
            End Function
 
            Public Function ComputeSyntacticChangeRange(services As SolutionServices, oldRoot As SyntaxNode, newRoot As SyntaxNode, timeout As TimeSpan, cancellationToken As CancellationToken) As TextChangeRange? Implements IClassificationService.ComputeSyntacticChangeRange
                Return Nothing
            End Function
 
            Public Function AddEmbeddedLanguageClassificationsAsync(document As Document, textSpans As ImmutableArray(Of TextSpan), options As ClassificationOptions, result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) As Task Implements IClassificationService.AddEmbeddedLanguageClassificationsAsync
                Return Task.CompletedTask
            End Function
        End Class
    End Class
End Namespace