diff --git a/src/Tools/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreEmbeddedLanguageClassificationContext.cs b/src/Tools/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreEmbeddedLanguageClassificationContext.cs index 802a51bc8805c..2f8e3550b60a6 100644 --- a/src/Tools/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreEmbeddedLanguageClassificationContext.cs +++ b/src/Tools/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreEmbeddedLanguageClassificationContext.cs @@ -12,9 +12,12 @@ internal readonly struct AspNetCoreEmbeddedLanguageClassificationContext { private readonly EmbeddedLanguageClassificationContext _context; - public AspNetCoreEmbeddedLanguageClassificationContext(EmbeddedLanguageClassificationContext context) + internal AspNetCoreEmbeddedLanguageClassificationContext( + EmbeddedLanguageClassificationContext context, + AspNetCoreVirtualCharSequence virtualCharSequence) { _context = context; + VirtualCharSequence = virtualCharSequence; } /// @@ -23,6 +26,9 @@ public AspNetCoreEmbeddedLanguageClassificationContext(EmbeddedLanguageClassific /// public SemanticModel SemanticModel => _context.SemanticModel; + /// + public AspNetCoreVirtualCharSequence VirtualCharSequence { get; } + /// public CancellationToken CancellationToken => _context.CancellationToken; diff --git a/src/Tools/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualChar.cs b/src/Tools/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualChar.cs new file mode 100644 index 0000000000000..e54d787afd63a --- /dev/null +++ b/src/Tools/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualChar.cs @@ -0,0 +1,32 @@ +// 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.Text; +using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.ExternalAccess.AspNetCore.EmbeddedLanguages +{ + internal readonly struct AspNetCoreVirtualChar + { + private readonly VirtualChar _virtualChar; + + internal AspNetCoreVirtualChar(VirtualChar virtualChar) + { + _virtualChar = virtualChar; + } + + /// + public Rune Rune => _virtualChar.Rune; + + /// + public char SurrogateChar => _virtualChar.SurrogateChar; + + /// + public TextSpan Span => _virtualChar.Span; + + /// + public int Value => _virtualChar.Value; + } +} diff --git a/src/Tools/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs b/src/Tools/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs new file mode 100644 index 0000000000000..12fc5525412d9 --- /dev/null +++ b/src/Tools/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs @@ -0,0 +1,25 @@ +// 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 Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; + +namespace Microsoft.CodeAnalysis.ExternalAccess.AspNetCore.EmbeddedLanguages +{ + /// + internal readonly struct AspNetCoreVirtualCharSequence + { + private readonly VirtualCharSequence _virtualCharSequence; + + internal AspNetCoreVirtualCharSequence(VirtualCharSequence virtualCharSequence) + { + _virtualCharSequence = virtualCharSequence; + } + + /// + public int Length => _virtualCharSequence.Length; + + /// + public AspNetCoreVirtualChar this[int index] => new(_virtualCharSequence[index]); + } +} diff --git a/src/Tools/ExternalAccess/AspNetCore/Internal/EmbeddedLanguages/AspNetCoreEmbeddedLanguageClassifier.cs b/src/Tools/ExternalAccess/AspNetCore/Internal/EmbeddedLanguages/AspNetCoreEmbeddedLanguageClassifier.cs index 32f254ede03df..51df99756cf30 100644 --- a/src/Tools/ExternalAccess/AspNetCore/Internal/EmbeddedLanguages/AspNetCoreEmbeddedLanguageClassifier.cs +++ b/src/Tools/ExternalAccess/AspNetCore/Internal/EmbeddedLanguages/AspNetCoreEmbeddedLanguageClassifier.cs @@ -28,7 +28,15 @@ public void RegisterClassifications(EmbeddedLanguageClassificationContext contex return; var classifiers = AspNetCoreClassifierExtensionProvider.GetExtensions(context.Project); - var aspContext = new AspNetCoreEmbeddedLanguageClassificationContext(context); + if (classifiers.Length == 0) + return; + + var virtualChars = context.VirtualCharService.TryConvertToVirtualChars(context.SyntaxToken); + if (virtualChars.IsDefaultOrEmpty) + return; + + var aspContext = new AspNetCoreEmbeddedLanguageClassificationContext( + context, new AspNetCoreVirtualCharSequence(virtualChars)); foreach (var classifier in classifiers) classifier.RegisterClassifications(aspContext); } diff --git a/src/Workspaces/Core/Portable/Classification/EmbeddedLanguages/AbstractEmbeddedLanguageClassificationService.cs b/src/Workspaces/Core/Portable/Classification/EmbeddedLanguages/AbstractEmbeddedLanguageClassificationService.cs index 24f4902bc16a9..91ea5b504cf6f 100644 --- a/src/Workspaces/Core/Portable/Classification/EmbeddedLanguages/AbstractEmbeddedLanguageClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/EmbeddedLanguages/AbstractEmbeddedLanguageClassificationService.cs @@ -42,6 +42,11 @@ internal abstract class AbstractEmbeddedLanguageClassificationService : IEmbedde /// private readonly Dictionary>> _identifierToClassifiers = new(StringComparer.OrdinalIgnoreCase); + /// + /// Information about the embedded language. + /// + private readonly EmbeddedLanguageInfo _info; + /// /// Helper to look at string literals and determine what language they are annotated to take. /// @@ -71,6 +76,7 @@ protected AbstractEmbeddedLanguageClassificationService( foreach (var (_, classifiers) in _identifierToClassifiers) classifiers.RemoveDuplicates(); + _info = info; _detector = new EmbeddedLanguageDetector(info, _identifierToClassifiers.Keys.ToImmutableArray()); _syntaxTokenKinds.Add(syntaxKinds.CharacterLiteralToken); @@ -165,7 +171,7 @@ private void ClassifyToken(SyntaxToken token) _classifierBuffer.Clear(); var context = new EmbeddedLanguageClassificationContext( - _project, _semanticModel, token, _options, _result, _cancellationToken); + _project, _semanticModel, token, _options, _service._info.VirtualCharService, _result, _cancellationToken); // First, see if this is a string annotated with either a comment or [StringSyntax] attribute. If // so, delegate to the first classifier we have registered for whatever language ID we find. diff --git a/src/Workspaces/Core/Portable/Classification/EmbeddedLanguages/EmbeddedLanguageClassifierContext.cs b/src/Workspaces/Core/Portable/Classification/EmbeddedLanguages/EmbeddedLanguageClassifierContext.cs index 866d202fc068b..978224cee5bc0 100644 --- a/src/Workspaces/Core/Portable/Classification/EmbeddedLanguages/EmbeddedLanguageClassifierContext.cs +++ b/src/Workspaces/Core/Portable/Classification/EmbeddedLanguages/EmbeddedLanguageClassifierContext.cs @@ -4,6 +4,7 @@ using System.Threading; using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -11,7 +12,6 @@ namespace Microsoft.CodeAnalysis.Classification { internal struct EmbeddedLanguageClassificationContext { - internal readonly ClassificationOptions Options; private readonly ArrayBuilder _result; public Project? Project { get; } @@ -28,11 +28,15 @@ internal struct EmbeddedLanguageClassificationContext public CancellationToken CancellationToken { get; } + internal readonly ClassificationOptions Options; + internal readonly IVirtualCharService VirtualCharService; + internal EmbeddedLanguageClassificationContext( Project? project, SemanticModel semanticModel, SyntaxToken syntaxToken, ClassificationOptions options, + IVirtualCharService virtualCharService, ArrayBuilder result, CancellationToken cancellationToken) { @@ -40,6 +44,7 @@ internal EmbeddedLanguageClassificationContext( SemanticModel = semanticModel; SyntaxToken = syntaxToken; Options = options; + VirtualCharService = virtualCharService; _result = result; CancellationToken = cancellationToken; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.cs index 7d2a33b62242e..6a5ed7c255606 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.cs @@ -63,7 +63,14 @@ private VirtualCharSequence(Chunk sequence, TextSpan span) _span = span; } + /// + /// Gets the number of elements contained in the . + /// public int Length => _span.Length; + + /// + /// Gets the at the specified index. + /// public VirtualChar this[int index] => _leafCharacters[_span.Start + index]; public bool IsDefault => _leafCharacters == null;