提交 b5a44ee9 编写于 作者: C Cyrus Najmabadi

Remove regex features code from the json branch.

上级 d8a5691a
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Editor.Implementation.BraceMatching;
namespace Microsoft.CodeAnalysis.Editor.CSharp.BraceMatching
{
[ExportBraceMatcher(LanguageNames.CSharp)]
internal class CSharpRegexBraceMatcher : IBraceMatcher
{
public Task<BraceMatchingResult?> FindBracesAsync(Document document, int position, CancellationToken cancellationToken)
=> CommonRegexBraceMatcher.FindBracesAsync(document, position, cancellationToken);
}
}
......@@ -2505,242 +2505,5 @@ interface unmanaged {}
TypeParameter("T"),
Keyword("unmanaged"));
}
[WpfFact, Trait(Traits.Feature, Traits.Features.Classification)]
public async Task TestRegex1()
{
await TestAsync(
@"
using System.Text.RegularExpressions;
class Program
{
void Goo()
{
var r = new Regex(@""$(\a\t\u0020)|[^\p{Lu}-a\w\sa-z-[m-p]]+?(?#comment)|(\b\G\z)|(?<name>sub){0,5}?^"");
}
}",
Keyword("var"),
Class("Regex"),
Regex.Anchor("$"),
Regex.Grouping("("),
Regex.OtherEscape("\\"),
Regex.OtherEscape("a"),
Regex.OtherEscape("\\"),
Regex.OtherEscape("t"),
Regex.OtherEscape("\\"),
Regex.OtherEscape("u"),
Regex.OtherEscape("0020"),
Regex.Grouping(")"),
Regex.Alternation("|"),
Regex.CharacterClass("["),
Regex.CharacterClass("^"),
Regex.CharacterClass("\\"),
Regex.CharacterClass("p"),
Regex.CharacterClass("{"),
Regex.CharacterClass("Lu"),
Regex.CharacterClass("}"),
Regex.Text("-a"),
Regex.CharacterClass("\\"),
Regex.CharacterClass("w"),
Regex.CharacterClass("\\"),
Regex.CharacterClass("s"),
Regex.Text("a"),
Regex.CharacterClass("-"),
Regex.Text("z"),
Regex.CharacterClass("-"),
Regex.CharacterClass("["),
Regex.Text("m"),
Regex.CharacterClass("-"),
Regex.Text("p"),
Regex.CharacterClass("]"),
Regex.CharacterClass("]"),
Regex.Quantifier("+"),
Regex.Quantifier("?"),
Regex.Comment("(?#comment)"),
Regex.Alternation("|"),
Regex.Grouping("("),
Regex.Anchor("\\"),
Regex.Anchor("b"),
Regex.Anchor("\\"),
Regex.Anchor("G"),
Regex.Anchor("\\"),
Regex.Anchor("z"),
Regex.Grouping(")"),
Regex.Alternation("|"),
Regex.Grouping("("),
Regex.Grouping("?"),
Regex.Grouping("<"),
Regex.Grouping("name"),
Regex.Grouping(">"),
Regex.Text("sub"),
Regex.Grouping(")"),
Regex.Quantifier("{"),
Regex.Quantifier("0"),
Regex.Quantifier(","),
Regex.Quantifier("5"),
Regex.Quantifier("}"),
Regex.Quantifier("?"),
Regex.Anchor("^"));
}
[WpfFact, Trait(Traits.Feature, Traits.Features.Classification)]
public async Task TestRegex2()
{
await TestAsync(
@"
using System.Text.RegularExpressions;
class Program
{
void Goo()
{
// language=regex
var r = @""$(\a\t\u0020)|[^\p{Lu}-a\w\sa-z-[m-p]]+?(?#comment)|(\b\G\z)|(?<name>sub){0,5}?^"";
}
}",
Keyword("var"),
Regex.Anchor("$"),
Regex.Grouping("("),
Regex.OtherEscape("\\"),
Regex.OtherEscape("a"),
Regex.OtherEscape("\\"),
Regex.OtherEscape("t"),
Regex.OtherEscape("\\"),
Regex.OtherEscape("u"),
Regex.OtherEscape("0020"),
Regex.Grouping(")"),
Regex.Alternation("|"),
Regex.CharacterClass("["),
Regex.CharacterClass("^"),
Regex.CharacterClass("\\"),
Regex.CharacterClass("p"),
Regex.CharacterClass("{"),
Regex.CharacterClass("Lu"),
Regex.CharacterClass("}"),
Regex.Text("-a"),
Regex.CharacterClass("\\"),
Regex.CharacterClass("w"),
Regex.CharacterClass("\\"),
Regex.CharacterClass("s"),
Regex.Text("a"),
Regex.CharacterClass("-"),
Regex.Text("z"),
Regex.CharacterClass("-"),
Regex.CharacterClass("["),
Regex.Text("m"),
Regex.CharacterClass("-"),
Regex.Text("p"),
Regex.CharacterClass("]"),
Regex.CharacterClass("]"),
Regex.Quantifier("+"),
Regex.Quantifier("?"),
Regex.Comment("(?#comment)"),
Regex.Alternation("|"),
Regex.Grouping("("),
Regex.Anchor("\\"),
Regex.Anchor("b"),
Regex.Anchor("\\"),
Regex.Anchor("G"),
Regex.Anchor("\\"),
Regex.Anchor("z"),
Regex.Grouping(")"),
Regex.Alternation("|"),
Regex.Grouping("("),
Regex.Grouping("?"),
Regex.Grouping("<"),
Regex.Grouping("name"),
Regex.Grouping(">"),
Regex.Text("sub"),
Regex.Grouping(")"),
Regex.Quantifier("{"),
Regex.Quantifier("0"),
Regex.Quantifier(","),
Regex.Quantifier("5"),
Regex.Quantifier("}"),
Regex.Quantifier("?"),
Regex.Anchor("^"));
}
[WpfFact, Trait(Traits.Feature, Traits.Features.Classification)]
public async Task TestRegex3()
{
await TestAsync(
@"
using System.Text.RegularExpressions;
class Program
{
void Goo()
{
var r = /* language=regex */@""$(\a\t\u0020\\)|[^\p{Lu}-a\w\sa-z-[m-p]]+?(?#comment)|(\b\G\z)|(?<name>sub){0,5}?^"";
}
}",
Keyword("var"),
Regex.Anchor("$"),
Regex.Grouping("("),
Regex.OtherEscape("\\"),
Regex.OtherEscape("a"),
Regex.OtherEscape("\\"),
Regex.OtherEscape("t"),
Regex.OtherEscape("\\"),
Regex.OtherEscape("u"),
Regex.OtherEscape("0020"),
Regex.SelfEscapedCharacter("\\"),
Regex.SelfEscapedCharacter("\\"),
Regex.Grouping(")"),
Regex.Alternation("|"),
Regex.CharacterClass("["),
Regex.CharacterClass("^"),
Regex.CharacterClass("\\"),
Regex.CharacterClass("p"),
Regex.CharacterClass("{"),
Regex.CharacterClass("Lu"),
Regex.CharacterClass("}"),
Regex.Text("-a"),
Regex.CharacterClass("\\"),
Regex.CharacterClass("w"),
Regex.CharacterClass("\\"),
Regex.CharacterClass("s"),
Regex.Text("a"),
Regex.CharacterClass("-"),
Regex.Text("z"),
Regex.CharacterClass("-"),
Regex.CharacterClass("["),
Regex.Text("m"),
Regex.CharacterClass("-"),
Regex.Text("p"),
Regex.CharacterClass("]"),
Regex.CharacterClass("]"),
Regex.Quantifier("+"),
Regex.Quantifier("?"),
Regex.Comment("(?#comment)"),
Regex.Alternation("|"),
Regex.Grouping("("),
Regex.Anchor("\\"),
Regex.Anchor("b"),
Regex.Anchor("\\"),
Regex.Anchor("G"),
Regex.Anchor("\\"),
Regex.Anchor("z"),
Regex.Grouping(")"),
Regex.Alternation("|"),
Regex.Grouping("("),
Regex.Grouping("?"),
Regex.Grouping("<"),
Regex.Grouping("name"),
Regex.Grouping(">"),
Regex.Text("sub"),
Regex.Grouping(")"),
Regex.Quantifier("{"),
Regex.Quantifier("0"),
Regex.Quantifier(","),
Regex.Quantifier("5"),
Regex.Quantifier("}"),
Regex.Quantifier("?"),
Regex.Anchor("^"));
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.ValidateRegexString;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ValidateRegexString
{
public class ValidateRegexStringTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (new CSharpValidateRegexStringDiagnosticAnalyzer(), null);
private IDictionary<OptionKey, object> OptionOn()
{
var optionsSet = new Dictionary<OptionKey, object>();
optionsSet.Add(new OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.CSharp), true);
optionsSet.Add(new OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.VisualBasic), true);
return optionsSet;
}
private IDictionary<OptionKey, object> OptionOff()
{
var optionsSet = new Dictionary<OptionKey, object>();
optionsSet.Add(new OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.CSharp), false);
optionsSet.Add(new OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.VisualBasic), false);
return optionsSet;
}
[Fact, Trait(Traits.Feature, Traits.Features.ValidateRegexString)]
public async Task TestWarning1()
{
await TestDiagnosticInfoAsync(@"
using System.Text.RegularExpressions;
class Program
{
void Main()
{
var r = new Regex(@""[|)|]"");
}
}",
options: OptionOn(),
diagnosticId: IDEDiagnosticIds.RegexPatternDiagnosticId,
diagnosticSeverity: DiagnosticSeverity.Warning,
diagnosticMessage: string.Format(FeaturesResources.Regex_issue_0, WorkspacesResources.Too_many_close_parens));
}
[Fact, Trait(Traits.Feature, Traits.Features.ValidateRegexString)]
public async Task TestWarning2()
{
await TestDiagnosticInfoAsync(@"
using System.Text.RegularExpressions;
class Program
{
void Main()
{
var r = new Regex(""[|\u0029|]"");
}
}",
options: OptionOn(),
diagnosticId: IDEDiagnosticIds.RegexPatternDiagnosticId,
diagnosticSeverity: DiagnosticSeverity.Warning,
diagnosticMessage: string.Format(FeaturesResources.Regex_issue_0, WorkspacesResources.Too_many_close_parens));
}
[Fact, Trait(Traits.Feature, Traits.Features.ValidateRegexString)]
public async Task TestWarningMissing1()
{
await TestDiagnosticMissingAsync(@"
using System.Text.RegularExpressions;
class Program
{
void Main()
{
var r = new Regex(@""[|\u0029|]"");
}
}");
}
}
}
......@@ -345,171 +345,6 @@ private XmlDocCommentTextFormatDefinition()
}
#endregion
#region Regex - Comment
#if dark_theme
private static readonly Color s_regexTextColor = Color.FromRgb(0xd6, 0x9d, 0x85);
private static readonly Color s_regexOtherEscapeColor = Color.FromRgb(0xff, 0xd6, 0x8f);
private static readonly Color s_regexGroupingAndAlternationColor = Color.FromRgb(0x05, 0xc3, 0xba);
private static readonly Color s_characterClassColor = Color.FromRgb(0x00, 0x8a, 0xff);
private static readonly Color s_regexAnchorAndQuantifierColor = Color.FromRgb(0xd7, 0x45, 0x8c);
private static readonly Color s_regexCommentColor = Color.FromRgb(87, 166, 74);
#else
private static readonly Color s_regexTextColor = Color.FromRgb(0x80, 0x00, 0x00);
private static readonly Color s_regexOtherEscapeColor = Color.FromRgb(0x9e, 0x5b, 0x71);
private static readonly Color s_regexGroupingAndAlternationColor = Color.FromRgb(0x05, 0xc3, 0xba);
private static readonly Color s_characterClassColor = Color.FromRgb(0x00, 0x73, 0xff);
private static readonly Color s_regexAnchorAndQuantifierColor = Color.FromRgb(0xff, 0x00, 0xc1);
private static readonly Color s_regexCommentColor = Color.FromRgb(0, 128, 0);
#endif
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = ClassificationTypeNames.RegexComment)]
[Name(ClassificationTypeNames.RegexComment)]
[Order(After = ClassificationTypeNames.StringLiteral)]
[Order(After = ClassificationTypeNames.VerbatimStringLiteral)]
[UserVisible(true)]
[ExcludeFromCodeCoverage]
private class RegexCommentFormatDefinition : ClassificationFormatDefinition
{
private RegexCommentFormatDefinition()
{
this.DisplayName = EditorFeaturesWpfResources.Regex_Comment;
this.ForegroundColor = s_regexCommentColor;
}
}
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = ClassificationTypeNames.RegexCharacterClass)]
[Name(ClassificationTypeNames.RegexCharacterClass)]
[Order(After = ClassificationTypeNames.StringLiteral)]
[Order(After = ClassificationTypeNames.VerbatimStringLiteral)]
[UserVisible(true)]
[ExcludeFromCodeCoverage]
private class RegexCharacterClassFormatDefinition : ClassificationFormatDefinition
{
private RegexCharacterClassFormatDefinition()
{
this.DisplayName = EditorFeaturesWpfResources.Regex_Character_class;
this.ForegroundColor = s_characterClassColor;
}
}
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = ClassificationTypeNames.RegexAnchor)]
[Name(ClassificationTypeNames.RegexAnchor)]
[Order(After = ClassificationTypeNames.StringLiteral)]
[Order(After = ClassificationTypeNames.VerbatimStringLiteral)]
[UserVisible(true)]
[ExcludeFromCodeCoverage]
private class RegexAnchorFormatDefinition : ClassificationFormatDefinition
{
private RegexAnchorFormatDefinition()
{
this.DisplayName = EditorFeaturesWpfResources.Regex_Anchor;
this.ForegroundColor = s_regexAnchorAndQuantifierColor;
}
}
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = ClassificationTypeNames.RegexQuantifier)]
[Name(ClassificationTypeNames.RegexQuantifier)]
[Order(After = ClassificationTypeNames.StringLiteral)]
[Order(After = ClassificationTypeNames.VerbatimStringLiteral)]
[UserVisible(true)]
[ExcludeFromCodeCoverage]
private class RegexQuantifierFormatDefinition : ClassificationFormatDefinition
{
private RegexQuantifierFormatDefinition()
{
this.DisplayName = EditorFeaturesWpfResources.Regex_Quantifier;
this.ForegroundColor = s_regexAnchorAndQuantifierColor;
}
}
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = ClassificationTypeNames.RegexGrouping)]
[Name(ClassificationTypeNames.RegexGrouping)]
[Order(After = ClassificationTypeNames.StringLiteral)]
[Order(After = ClassificationTypeNames.VerbatimStringLiteral)]
[UserVisible(true)]
[ExcludeFromCodeCoverage]
private class RegexGroupingFormatDefinition : ClassificationFormatDefinition
{
private RegexGroupingFormatDefinition()
{
this.DisplayName = EditorFeaturesWpfResources.Regex_Grouping;
this.ForegroundColor = s_regexGroupingAndAlternationColor;
}
}
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = ClassificationTypeNames.RegexAlternation)]
[Name(ClassificationTypeNames.RegexAlternation)]
[Order(After = ClassificationTypeNames.StringLiteral)]
[Order(After = ClassificationTypeNames.VerbatimStringLiteral)]
[UserVisible(true)]
[ExcludeFromCodeCoverage]
private class RegexAlternationFormatDefinition : ClassificationFormatDefinition
{
private RegexAlternationFormatDefinition()
{
this.DisplayName = EditorFeaturesWpfResources.Regex_Alternation;
this.ForegroundColor = s_regexGroupingAndAlternationColor;
}
}
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = ClassificationTypeNames.RegexText)]
[Name(ClassificationTypeNames.RegexText)]
[Order(After = ClassificationTypeNames.StringLiteral)]
[Order(After = ClassificationTypeNames.VerbatimStringLiteral)]
[UserVisible(true)]
[ExcludeFromCodeCoverage]
private class RegexTextFormatDefinition : ClassificationFormatDefinition
{
private RegexTextFormatDefinition()
{
this.DisplayName = EditorFeaturesWpfResources.Regex_Text;
this.ForegroundColor = s_regexTextColor;
}
}
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = ClassificationTypeNames.RegexSelfEscapedCharacter)]
[Name(ClassificationTypeNames.RegexSelfEscapedCharacter)]
[Order(After = ClassificationTypeNames.StringLiteral)]
[Order(After = ClassificationTypeNames.VerbatimStringLiteral)]
[UserVisible(true)]
[ExcludeFromCodeCoverage]
private class RegexSelfEscapedCharacterFormatDefinition : ClassificationFormatDefinition
{
private RegexSelfEscapedCharacterFormatDefinition()
{
this.DisplayName = EditorFeaturesWpfResources.Regex_SelfEscapedCharacter;
this.ForegroundColor = s_regexTextColor;
this.IsBold = true;
}
}
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = ClassificationTypeNames.RegexOtherEscape)]
[Name(ClassificationTypeNames.RegexOtherEscape)]
[Order(After = ClassificationTypeNames.StringLiteral)]
[Order(After = ClassificationTypeNames.VerbatimStringLiteral)]
[UserVisible(true)]
[ExcludeFromCodeCoverage]
private class RegexOtherEscapeFormatDefinition : ClassificationFormatDefinition
{
private RegexOtherEscapeFormatDefinition()
{
this.DisplayName = EditorFeaturesWpfResources.Regex_OtherEscape;
this.ForegroundColor = s_regexOtherEscapeColor;
}
}
#endregion
#region JSON
[Export(typeof(EditorFormatDefinition))]
......
......@@ -185,86 +185,5 @@ internal class EditorFeaturesWpfResources {
return ResourceManager.GetString("JSON_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Regex - Alternation.
/// </summary>
internal static string Regex_Alternation {
get {
return ResourceManager.GetString("Regex_Alternation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Regex - Anchor.
/// </summary>
internal static string Regex_Anchor {
get {
return ResourceManager.GetString("Regex_Anchor", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Regex - Character class.
/// </summary>
internal static string Regex_Character_class {
get {
return ResourceManager.GetString("Regex_Character_class", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Regex - Comment.
/// </summary>
internal static string Regex_Comment {
get {
return ResourceManager.GetString("Regex_Comment", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Regex - Grouping.
/// </summary>
internal static string Regex_Grouping {
get {
return ResourceManager.GetString("Regex_Grouping", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Regex - Other Escape.
/// </summary>
internal static string Regex_OtherEscape {
get {
return ResourceManager.GetString("Regex_OtherEscape", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Regex - Quantifier.
/// </summary>
internal static string Regex_Quantifier {
get {
return ResourceManager.GetString("Regex_Quantifier", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Regex - Self Escaped Character.
/// </summary>
internal static string Regex_SelfEscapedCharacter {
get {
return ResourceManager.GetString("Regex_SelfEscapedCharacter", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Regex - Text.
/// </summary>
internal static string Regex_Text {
get {
return ResourceManager.GetString("Regex_Text", resourceCulture);
}
}
}
}
......@@ -126,33 +126,6 @@
<data name="Downloading_IntelliSense_index_for_0" xml:space="preserve">
<value>Downloading IntelliSense index for {0}</value>
</data>
<data name="Regex_Comment" xml:space="preserve">
<value>Regex - Comment</value>
</data>
<data name="Regex_Character_class" xml:space="preserve">
<value>Regex - Character class</value>
</data>
<data name="Regex_Alternation" xml:space="preserve">
<value>Regex - Alternation</value>
</data>
<data name="Regex_Anchor" xml:space="preserve">
<value>Regex - Anchor</value>
</data>
<data name="Regex_Quantifier" xml:space="preserve">
<value>Regex - Quantifier</value>
</data>
<data name="Regex_SelfEscapedCharacter" xml:space="preserve">
<value>Regex - Self Escaped Character</value>
</data>
<data name="Regex_Grouping" xml:space="preserve">
<value>Regex - Grouping</value>
</data>
<data name="Regex_Text" xml:space="preserve">
<value>Regex - Text</value>
</data>
<data name="Regex_OtherEscape" xml:space="preserve">
<value>Regex - Other Escape</value>
</data>
<data name="JSON_Property_Name" xml:space="preserve">
<value>JSON Property Name</value>
</data>
......
......@@ -17,51 +17,6 @@
<target state="translated">Stahuje se index IntelliSense pro {0}.</target>
<note />
</trans-unit>
<trans-unit id="Regex_Comment">
<source>Regex - Comment</source>
<target state="new">Regex - Comment</target>
<note />
</trans-unit>
<trans-unit id="Regex_Character_class">
<source>Regex - Character class</source>
<target state="new">Regex - Character class</target>
<note />
</trans-unit>
<trans-unit id="Regex_Quantifier">
<source>Regex - Quantifier</source>
<target state="new">Regex - Quantifier</target>
<note />
</trans-unit>
<trans-unit id="Regex_Anchor">
<source>Regex - Anchor</source>
<target state="new">Regex - Anchor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Alternation">
<source>Regex - Alternation</source>
<target state="new">Regex - Alternation</target>
<note />
</trans-unit>
<trans-unit id="Regex_Text">
<source>Regex - Text</source>
<target state="new">Regex - Text</target>
<note />
</trans-unit>
<trans-unit id="Regex_Grouping">
<source>Regex - Grouping</source>
<target state="new">Regex - Grouping</target>
<note />
</trans-unit>
<trans-unit id="Regex_SelfEscapedCharacter">
<source>Regex - Self Escaped Character</source>
<target state="new">Regex - Self Escaped Character</target>
<note />
</trans-unit>
<trans-unit id="Regex_OtherEscape">
<source>Regex - Other Escape</source>
<target state="new">Regex - Other Escape</target>
<note />
</trans-unit>
<trans-unit id="JSON_Property_Name">
<source>JSON Property Name</source>
<target state="new">JSON Property Name</target>
......
......@@ -17,51 +17,6 @@
<target state="translated">Der IntelliSense-Index für "{0}" wird heruntergeladen.</target>
<note />
</trans-unit>
<trans-unit id="Regex_Comment">
<source>Regex - Comment</source>
<target state="new">Regex - Comment</target>
<note />
</trans-unit>
<trans-unit id="Regex_Character_class">
<source>Regex - Character class</source>
<target state="new">Regex - Character class</target>
<note />
</trans-unit>
<trans-unit id="Regex_Quantifier">
<source>Regex - Quantifier</source>
<target state="new">Regex - Quantifier</target>
<note />
</trans-unit>
<trans-unit id="Regex_Anchor">
<source>Regex - Anchor</source>
<target state="new">Regex - Anchor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Alternation">
<source>Regex - Alternation</source>
<target state="new">Regex - Alternation</target>
<note />
</trans-unit>
<trans-unit id="Regex_Text">
<source>Regex - Text</source>
<target state="new">Regex - Text</target>
<note />
</trans-unit>
<trans-unit id="Regex_Grouping">
<source>Regex - Grouping</source>
<target state="new">Regex - Grouping</target>
<note />
</trans-unit>
<trans-unit id="Regex_SelfEscapedCharacter">
<source>Regex - Self Escaped Character</source>
<target state="new">Regex - Self Escaped Character</target>
<note />
</trans-unit>
<trans-unit id="Regex_OtherEscape">
<source>Regex - Other Escape</source>
<target state="new">Regex - Other Escape</target>
<note />
</trans-unit>
<trans-unit id="JSON_Property_Name">
<source>JSON Property Name</source>
<target state="new">JSON Property Name</target>
......
......@@ -17,51 +17,6 @@
<target state="translated">Descargando el índice de IntelliSense para {0}</target>
<note />
</trans-unit>
<trans-unit id="Regex_Comment">
<source>Regex - Comment</source>
<target state="new">Regex - Comment</target>
<note />
</trans-unit>
<trans-unit id="Regex_Character_class">
<source>Regex - Character class</source>
<target state="new">Regex - Character class</target>
<note />
</trans-unit>
<trans-unit id="Regex_Quantifier">
<source>Regex - Quantifier</source>
<target state="new">Regex - Quantifier</target>
<note />
</trans-unit>
<trans-unit id="Regex_Anchor">
<source>Regex - Anchor</source>
<target state="new">Regex - Anchor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Alternation">
<source>Regex - Alternation</source>
<target state="new">Regex - Alternation</target>
<note />
</trans-unit>
<trans-unit id="Regex_Text">
<source>Regex - Text</source>
<target state="new">Regex - Text</target>
<note />
</trans-unit>
<trans-unit id="Regex_Grouping">
<source>Regex - Grouping</source>
<target state="new">Regex - Grouping</target>
<note />
</trans-unit>
<trans-unit id="Regex_SelfEscapedCharacter">
<source>Regex - Self Escaped Character</source>
<target state="new">Regex - Self Escaped Character</target>
<note />
</trans-unit>
<trans-unit id="Regex_OtherEscape">
<source>Regex - Other Escape</source>
<target state="new">Regex - Other Escape</target>
<note />
</trans-unit>
<trans-unit id="JSON_Property_Name">
<source>JSON Property Name</source>
<target state="new">JSON Property Name</target>
......
......@@ -17,51 +17,6 @@
<target state="translated">Téléchargement de l'index IntelliSense pour {0}</target>
<note />
</trans-unit>
<trans-unit id="Regex_Comment">
<source>Regex - Comment</source>
<target state="new">Regex - Comment</target>
<note />
</trans-unit>
<trans-unit id="Regex_Character_class">
<source>Regex - Character class</source>
<target state="new">Regex - Character class</target>
<note />
</trans-unit>
<trans-unit id="Regex_Quantifier">
<source>Regex - Quantifier</source>
<target state="new">Regex - Quantifier</target>
<note />
</trans-unit>
<trans-unit id="Regex_Anchor">
<source>Regex - Anchor</source>
<target state="new">Regex - Anchor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Alternation">
<source>Regex - Alternation</source>
<target state="new">Regex - Alternation</target>
<note />
</trans-unit>
<trans-unit id="Regex_Text">
<source>Regex - Text</source>
<target state="new">Regex - Text</target>
<note />
</trans-unit>
<trans-unit id="Regex_Grouping">
<source>Regex - Grouping</source>
<target state="new">Regex - Grouping</target>
<note />
</trans-unit>
<trans-unit id="Regex_SelfEscapedCharacter">
<source>Regex - Self Escaped Character</source>
<target state="new">Regex - Self Escaped Character</target>
<note />
</trans-unit>
<trans-unit id="Regex_OtherEscape">
<source>Regex - Other Escape</source>
<target state="new">Regex - Other Escape</target>
<note />
</trans-unit>
<trans-unit id="JSON_Property_Name">
<source>JSON Property Name</source>
<target state="new">JSON Property Name</target>
......
......@@ -17,51 +17,6 @@
<target state="translated">Download dell'indice IntelliSense per {0}</target>
<note />
</trans-unit>
<trans-unit id="Regex_Comment">
<source>Regex - Comment</source>
<target state="new">Regex - Comment</target>
<note />
</trans-unit>
<trans-unit id="Regex_Character_class">
<source>Regex - Character class</source>
<target state="new">Regex - Character class</target>
<note />
</trans-unit>
<trans-unit id="Regex_Quantifier">
<source>Regex - Quantifier</source>
<target state="new">Regex - Quantifier</target>
<note />
</trans-unit>
<trans-unit id="Regex_Anchor">
<source>Regex - Anchor</source>
<target state="new">Regex - Anchor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Alternation">
<source>Regex - Alternation</source>
<target state="new">Regex - Alternation</target>
<note />
</trans-unit>
<trans-unit id="Regex_Text">
<source>Regex - Text</source>
<target state="new">Regex - Text</target>
<note />
</trans-unit>
<trans-unit id="Regex_Grouping">
<source>Regex - Grouping</source>
<target state="new">Regex - Grouping</target>
<note />
</trans-unit>
<trans-unit id="Regex_SelfEscapedCharacter">
<source>Regex - Self Escaped Character</source>
<target state="new">Regex - Self Escaped Character</target>
<note />
</trans-unit>
<trans-unit id="Regex_OtherEscape">
<source>Regex - Other Escape</source>
<target state="new">Regex - Other Escape</target>
<note />
</trans-unit>
<trans-unit id="JSON_Property_Name">
<source>JSON Property Name</source>
<target state="new">JSON Property Name</target>
......
......@@ -17,51 +17,6 @@
<target state="translated">{0} の IntelliSense インデックスをダウンロードしています</target>
<note />
</trans-unit>
<trans-unit id="Regex_Comment">
<source>Regex - Comment</source>
<target state="new">Regex - Comment</target>
<note />
</trans-unit>
<trans-unit id="Regex_Character_class">
<source>Regex - Character class</source>
<target state="new">Regex - Character class</target>
<note />
</trans-unit>
<trans-unit id="Regex_Quantifier">
<source>Regex - Quantifier</source>
<target state="new">Regex - Quantifier</target>
<note />
</trans-unit>
<trans-unit id="Regex_Anchor">
<source>Regex - Anchor</source>
<target state="new">Regex - Anchor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Alternation">
<source>Regex - Alternation</source>
<target state="new">Regex - Alternation</target>
<note />
</trans-unit>
<trans-unit id="Regex_Text">
<source>Regex - Text</source>
<target state="new">Regex - Text</target>
<note />
</trans-unit>
<trans-unit id="Regex_Grouping">
<source>Regex - Grouping</source>
<target state="new">Regex - Grouping</target>
<note />
</trans-unit>
<trans-unit id="Regex_SelfEscapedCharacter">
<source>Regex - Self Escaped Character</source>
<target state="new">Regex - Self Escaped Character</target>
<note />
</trans-unit>
<trans-unit id="Regex_OtherEscape">
<source>Regex - Other Escape</source>
<target state="new">Regex - Other Escape</target>
<note />
</trans-unit>
<trans-unit id="JSON_Property_Name">
<source>JSON Property Name</source>
<target state="new">JSON Property Name</target>
......
......@@ -17,51 +17,6 @@
<target state="translated">{0}의 IntelliSense 인덱스 다운로드 중</target>
<note />
</trans-unit>
<trans-unit id="Regex_Comment">
<source>Regex - Comment</source>
<target state="new">Regex - Comment</target>
<note />
</trans-unit>
<trans-unit id="Regex_Character_class">
<source>Regex - Character class</source>
<target state="new">Regex - Character class</target>
<note />
</trans-unit>
<trans-unit id="Regex_Quantifier">
<source>Regex - Quantifier</source>
<target state="new">Regex - Quantifier</target>
<note />
</trans-unit>
<trans-unit id="Regex_Anchor">
<source>Regex - Anchor</source>
<target state="new">Regex - Anchor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Alternation">
<source>Regex - Alternation</source>
<target state="new">Regex - Alternation</target>
<note />
</trans-unit>
<trans-unit id="Regex_Text">
<source>Regex - Text</source>
<target state="new">Regex - Text</target>
<note />
</trans-unit>
<trans-unit id="Regex_Grouping">
<source>Regex - Grouping</source>
<target state="new">Regex - Grouping</target>
<note />
</trans-unit>
<trans-unit id="Regex_SelfEscapedCharacter">
<source>Regex - Self Escaped Character</source>
<target state="new">Regex - Self Escaped Character</target>
<note />
</trans-unit>
<trans-unit id="Regex_OtherEscape">
<source>Regex - Other Escape</source>
<target state="new">Regex - Other Escape</target>
<note />
</trans-unit>
<trans-unit id="JSON_Property_Name">
<source>JSON Property Name</source>
<target state="new">JSON Property Name</target>
......
......@@ -17,51 +17,6 @@
<target state="translated">Pobieranie indeksu funkcji IntelliSense dla {0}</target>
<note />
</trans-unit>
<trans-unit id="Regex_Comment">
<source>Regex - Comment</source>
<target state="new">Regex - Comment</target>
<note />
</trans-unit>
<trans-unit id="Regex_Character_class">
<source>Regex - Character class</source>
<target state="new">Regex - Character class</target>
<note />
</trans-unit>
<trans-unit id="Regex_Quantifier">
<source>Regex - Quantifier</source>
<target state="new">Regex - Quantifier</target>
<note />
</trans-unit>
<trans-unit id="Regex_Anchor">
<source>Regex - Anchor</source>
<target state="new">Regex - Anchor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Alternation">
<source>Regex - Alternation</source>
<target state="new">Regex - Alternation</target>
<note />
</trans-unit>
<trans-unit id="Regex_Text">
<source>Regex - Text</source>
<target state="new">Regex - Text</target>
<note />
</trans-unit>
<trans-unit id="Regex_Grouping">
<source>Regex - Grouping</source>
<target state="new">Regex - Grouping</target>
<note />
</trans-unit>
<trans-unit id="Regex_SelfEscapedCharacter">
<source>Regex - Self Escaped Character</source>
<target state="new">Regex - Self Escaped Character</target>
<note />
</trans-unit>
<trans-unit id="Regex_OtherEscape">
<source>Regex - Other Escape</source>
<target state="new">Regex - Other Escape</target>
<note />
</trans-unit>
<trans-unit id="JSON_Property_Name">
<source>JSON Property Name</source>
<target state="new">JSON Property Name</target>
......
......@@ -17,51 +17,6 @@
<target state="translated">Baixando o índice do IntelliSense para {0}</target>
<note />
</trans-unit>
<trans-unit id="Regex_Comment">
<source>Regex - Comment</source>
<target state="new">Regex - Comment</target>
<note />
</trans-unit>
<trans-unit id="Regex_Character_class">
<source>Regex - Character class</source>
<target state="new">Regex - Character class</target>
<note />
</trans-unit>
<trans-unit id="Regex_Quantifier">
<source>Regex - Quantifier</source>
<target state="new">Regex - Quantifier</target>
<note />
</trans-unit>
<trans-unit id="Regex_Anchor">
<source>Regex - Anchor</source>
<target state="new">Regex - Anchor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Alternation">
<source>Regex - Alternation</source>
<target state="new">Regex - Alternation</target>
<note />
</trans-unit>
<trans-unit id="Regex_Text">
<source>Regex - Text</source>
<target state="new">Regex - Text</target>
<note />
</trans-unit>
<trans-unit id="Regex_Grouping">
<source>Regex - Grouping</source>
<target state="new">Regex - Grouping</target>
<note />
</trans-unit>
<trans-unit id="Regex_SelfEscapedCharacter">
<source>Regex - Self Escaped Character</source>
<target state="new">Regex - Self Escaped Character</target>
<note />
</trans-unit>
<trans-unit id="Regex_OtherEscape">
<source>Regex - Other Escape</source>
<target state="new">Regex - Other Escape</target>
<note />
</trans-unit>
<trans-unit id="JSON_Property_Name">
<source>JSON Property Name</source>
<target state="new">JSON Property Name</target>
......
......@@ -17,51 +17,6 @@
<target state="translated">Загрузка индекса IntelliSense для {0}</target>
<note />
</trans-unit>
<trans-unit id="Regex_Comment">
<source>Regex - Comment</source>
<target state="new">Regex - Comment</target>
<note />
</trans-unit>
<trans-unit id="Regex_Character_class">
<source>Regex - Character class</source>
<target state="new">Regex - Character class</target>
<note />
</trans-unit>
<trans-unit id="Regex_Quantifier">
<source>Regex - Quantifier</source>
<target state="new">Regex - Quantifier</target>
<note />
</trans-unit>
<trans-unit id="Regex_Anchor">
<source>Regex - Anchor</source>
<target state="new">Regex - Anchor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Alternation">
<source>Regex - Alternation</source>
<target state="new">Regex - Alternation</target>
<note />
</trans-unit>
<trans-unit id="Regex_Text">
<source>Regex - Text</source>
<target state="new">Regex - Text</target>
<note />
</trans-unit>
<trans-unit id="Regex_Grouping">
<source>Regex - Grouping</source>
<target state="new">Regex - Grouping</target>
<note />
</trans-unit>
<trans-unit id="Regex_SelfEscapedCharacter">
<source>Regex - Self Escaped Character</source>
<target state="new">Regex - Self Escaped Character</target>
<note />
</trans-unit>
<trans-unit id="Regex_OtherEscape">
<source>Regex - Other Escape</source>
<target state="new">Regex - Other Escape</target>
<note />
</trans-unit>
<trans-unit id="JSON_Property_Name">
<source>JSON Property Name</source>
<target state="new">JSON Property Name</target>
......
......@@ -17,51 +17,6 @@
<target state="translated">{0} için IntelliSense dizini indiriliyor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Comment">
<source>Regex - Comment</source>
<target state="new">Regex - Comment</target>
<note />
</trans-unit>
<trans-unit id="Regex_Character_class">
<source>Regex - Character class</source>
<target state="new">Regex - Character class</target>
<note />
</trans-unit>
<trans-unit id="Regex_Quantifier">
<source>Regex - Quantifier</source>
<target state="new">Regex - Quantifier</target>
<note />
</trans-unit>
<trans-unit id="Regex_Anchor">
<source>Regex - Anchor</source>
<target state="new">Regex - Anchor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Alternation">
<source>Regex - Alternation</source>
<target state="new">Regex - Alternation</target>
<note />
</trans-unit>
<trans-unit id="Regex_Text">
<source>Regex - Text</source>
<target state="new">Regex - Text</target>
<note />
</trans-unit>
<trans-unit id="Regex_Grouping">
<source>Regex - Grouping</source>
<target state="new">Regex - Grouping</target>
<note />
</trans-unit>
<trans-unit id="Regex_SelfEscapedCharacter">
<source>Regex - Self Escaped Character</source>
<target state="new">Regex - Self Escaped Character</target>
<note />
</trans-unit>
<trans-unit id="Regex_OtherEscape">
<source>Regex - Other Escape</source>
<target state="new">Regex - Other Escape</target>
<note />
</trans-unit>
<trans-unit id="JSON_Property_Name">
<source>JSON Property Name</source>
<target state="new">JSON Property Name</target>
......
......@@ -17,51 +17,6 @@
<target state="translated">正在下载用于 {0} 的 IntelliSense 索引</target>
<note />
</trans-unit>
<trans-unit id="Regex_Comment">
<source>Regex - Comment</source>
<target state="new">Regex - Comment</target>
<note />
</trans-unit>
<trans-unit id="Regex_Character_class">
<source>Regex - Character class</source>
<target state="new">Regex - Character class</target>
<note />
</trans-unit>
<trans-unit id="Regex_Quantifier">
<source>Regex - Quantifier</source>
<target state="new">Regex - Quantifier</target>
<note />
</trans-unit>
<trans-unit id="Regex_Anchor">
<source>Regex - Anchor</source>
<target state="new">Regex - Anchor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Alternation">
<source>Regex - Alternation</source>
<target state="new">Regex - Alternation</target>
<note />
</trans-unit>
<trans-unit id="Regex_Text">
<source>Regex - Text</source>
<target state="new">Regex - Text</target>
<note />
</trans-unit>
<trans-unit id="Regex_Grouping">
<source>Regex - Grouping</source>
<target state="new">Regex - Grouping</target>
<note />
</trans-unit>
<trans-unit id="Regex_SelfEscapedCharacter">
<source>Regex - Self Escaped Character</source>
<target state="new">Regex - Self Escaped Character</target>
<note />
</trans-unit>
<trans-unit id="Regex_OtherEscape">
<source>Regex - Other Escape</source>
<target state="new">Regex - Other Escape</target>
<note />
</trans-unit>
<trans-unit id="JSON_Property_Name">
<source>JSON Property Name</source>
<target state="new">JSON Property Name</target>
......
......@@ -17,51 +17,6 @@
<target state="translated">正在為 {0} 下載 IntelliSense 索引</target>
<note />
</trans-unit>
<trans-unit id="Regex_Comment">
<source>Regex - Comment</source>
<target state="new">Regex - Comment</target>
<note />
</trans-unit>
<trans-unit id="Regex_Character_class">
<source>Regex - Character class</source>
<target state="new">Regex - Character class</target>
<note />
</trans-unit>
<trans-unit id="Regex_Quantifier">
<source>Regex - Quantifier</source>
<target state="new">Regex - Quantifier</target>
<note />
</trans-unit>
<trans-unit id="Regex_Anchor">
<source>Regex - Anchor</source>
<target state="new">Regex - Anchor</target>
<note />
</trans-unit>
<trans-unit id="Regex_Alternation">
<source>Regex - Alternation</source>
<target state="new">Regex - Alternation</target>
<note />
</trans-unit>
<trans-unit id="Regex_Text">
<source>Regex - Text</source>
<target state="new">Regex - Text</target>
<note />
</trans-unit>
<trans-unit id="Regex_Grouping">
<source>Regex - Grouping</source>
<target state="new">Regex - Grouping</target>
<note />
</trans-unit>
<trans-unit id="Regex_SelfEscapedCharacter">
<source>Regex - Self Escaped Character</source>
<target state="new">Regex - Self Escaped Character</target>
<note />
</trans-unit>
<trans-unit id="Regex_OtherEscape">
<source>Regex - Other Escape</source>
<target state="new">Regex - Other Escape</target>
<note />
</trans-unit>
<trans-unit id="JSON_Property_Name">
<source>JSON Property Name</source>
<target state="new">JSON Property Name</target>
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions;
using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.BraceMatching
{
internal static class CommonRegexBraceMatcher
{
internal static async Task<BraceMatchingResult?> FindBracesAsync(
Document document, int position, CancellationToken cancellationToken)
{
var option = document.Project.Solution.Workspace.Options.GetOption(
RegularExpressionsOptions.HighlightRelatedRegexComponentsUnderCursor, document.Project.Language);
if (!option)
{
return default;
}
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(position);
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
if (RegexPatternDetector.IsDefinitelyNotPattern(token, syntaxFacts))
{
return null;
}
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var detector = RegexPatternDetector.TryGetOrCreate(semanticModel, syntaxFacts, document.GetLanguageService<ISemanticFactsService>());
var tree = detector?.TryParseRegexPattern(token, document.GetLanguageService<IVirtualCharService>(), cancellationToken);
if (tree == null)
{
return null;
}
return GetMatchingBraces(tree, position);
}
private static BraceMatchingResult? GetMatchingBraces(RegexTree tree, int position)
{
var virtualChar = tree.Text.FirstOrNullable(vc => vc.Span.Contains(position));
if (virtualChar == null)
{
return null;
}
var ch = virtualChar.Value;
if (ch != '(' && ch != ')')
{
return null;
}
return FindBraceHighlights(tree, ch);
}
private static BraceMatchingResult? FindBraceHighlights(RegexTree tree, VirtualChar ch)
{
var node = FindGroupingNode(tree.Root, ch);
if (node == null)
{
return null;
}
if (node.OpenParenToken.IsMissing || node.CloseParenToken.IsMissing)
{
return null;
}
return new BraceMatchingResult(
node.OpenParenToken.VirtualChars[0].Span,
node.CloseParenToken.VirtualChars[0].Span);
}
private static RegexGroupingNode FindGroupingNode(RegexNode node, VirtualChar ch)
{
if (node is RegexGroupingNode grouping &&
(grouping.OpenParenToken.VirtualChars.Contains(ch) || grouping.CloseParenToken.VirtualChars.Contains(ch)))
{
return grouping;
}
foreach (var child in node)
{
if (child.IsNode)
{
var result = FindGroupingNode(child.Node, ch);
if (result != null)
{
return result;
}
}
}
return null;
}
}
}
......@@ -188,53 +188,6 @@ internal sealed class ClassificationTypeDefinitions
internal readonly ClassificationTypeDefinition XmlDocCommentTextTypeDefinition;
#endregion
#region Regex
[Export]
[Name(ClassificationTypeNames.RegexComment)]
[BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)]
internal readonly ClassificationTypeDefinition RegexCommentTypeDefinition;
[Export]
[Name(ClassificationTypeNames.RegexText)]
[BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)]
internal readonly ClassificationTypeDefinition RegexTextTypeDefinition;
[Export]
[Name(ClassificationTypeNames.RegexCharacterClass)]
[BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)]
internal readonly ClassificationTypeDefinition RegexCharacterClassTypeDefinition;
[Export]
[Name(ClassificationTypeNames.RegexQuantifier)]
[BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)]
internal readonly ClassificationTypeDefinition RegexQuantifierTypeDefinition;
[Export]
[Name(ClassificationTypeNames.RegexAnchor)]
[BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)]
internal readonly ClassificationTypeDefinition RegexAnchorTypeDefinition;
[Export]
[Name(ClassificationTypeNames.RegexAlternation)]
[BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)]
internal readonly ClassificationTypeDefinition RegexAlternationTypeDefinition;
[Export]
[Name(ClassificationTypeNames.RegexOtherEscape)]
[BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)]
internal readonly ClassificationTypeDefinition RegexOtherEscapeTypeDefinition;
[Export]
[Name(ClassificationTypeNames.RegexSelfEscapedCharacter)]
[BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)]
internal readonly ClassificationTypeDefinition RegexSelfEscapedCharacterTypeDefinition;
[Export]
[Name(ClassificationTypeNames.RegexGrouping)]
[BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)]
internal readonly ClassificationTypeDefinition RegexGroupingTypeDefinition;
#endregion
#region JSON
[Export]
[Name(ClassificationTypeNames.JsonComment)]
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics;
using Microsoft.CodeAnalysis.Classification;
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Classification
{
public static partial class FormattedClassifications
{
public static class Regex
{
[DebuggerStepThrough]
public static FormattedClassification Anchor(string value) => New(value, ClassificationTypeNames.RegexAnchor);
[DebuggerStepThrough]
public static FormattedClassification Grouping(string value) => New(value, ClassificationTypeNames.RegexGrouping);
[DebuggerStepThrough]
public static FormattedClassification OtherEscape(string value) => New(value, ClassificationTypeNames.RegexOtherEscape);
[DebuggerStepThrough]
public static FormattedClassification SelfEscapedCharacter(string value) => New(value, ClassificationTypeNames.RegexSelfEscapedCharacter);
[DebuggerStepThrough]
public static FormattedClassification Alternation(string value) => New(value, ClassificationTypeNames.RegexAlternation);
[DebuggerStepThrough]
public static FormattedClassification CharacterClass(string value) => New(value, ClassificationTypeNames.RegexCharacterClass);
[DebuggerStepThrough]
public static FormattedClassification Text(string value) => New(value, ClassificationTypeNames.RegexText);
[DebuggerStepThrough]
public static FormattedClassification Quantifier(string value) => New(value, ClassificationTypeNames.RegexQuantifier);
[DebuggerStepThrough]
public static FormattedClassification Comment(string value) => New(value, ClassificationTypeNames.RegexComment);
}
}
}
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Threading
Imports System.Threading.Tasks
Imports Microsoft.CodeAnalysis.Editor.Implementation.BraceMatching
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.BraceMatching
<ExportBraceMatcher(LanguageNames.VisualBasic)>
Friend Class VisualBasicRegexBraceMatcher
Implements IBraceMatcher
Public Function FindBracesAsync(document As Document, position As Integer, Optional cancellationToken As CancellationToken = Nothing) As Task(Of BraceMatchingResult?) Implements IBraceMatcher.FindBracesAsync
Return CommonRegexBraceMatcher.FindBracesAsync(document, position, cancellationToken)
End Function
End Class
End Namespace
......@@ -531,44 +531,6 @@ End Class"
[Class]("AttributeUsage"))
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.Classification)>
Public Async Function TestRegex1() As Task
Await TestAsync(
"
imports System.Text.RegularExpressions
class Program
sub Goo()
' language=regex
var r = ""$(\b\G\z)|(?<name>sub){0,5}?^""
end sub
end class",
Regex.Anchor("$"),
Regex.Grouping("("),
Regex.Anchor("\"),
Regex.Anchor("b"),
Regex.Anchor("\"),
Regex.Anchor("G"),
Regex.Anchor("\"),
Regex.Anchor("z"),
Regex.Grouping(")"),
Regex.Alternation("|"),
Regex.Grouping("("),
Regex.Grouping("?"),
Regex.Grouping("<"),
Regex.Grouping("name"),
Regex.Grouping(">"),
Regex.Text("sub"),
Regex.Grouping(")"),
Regex.Quantifier("{"),
Regex.Quantifier("0"),
Regex.Quantifier(","),
Regex.Quantifier("5"),
Regex.Quantifier("}"),
Regex.Quantifier("?"),
Regex.Anchor("^"))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Classification)>
Public Async Function TestConstField() As Task
Dim code =
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics
Imports Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.VisualBasic.ValidateRegexString
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ValidateRegexString
Public Class ValidateRegexStringTests
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest
Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider)
Return (New VisualBasicValidateRegexStringDiagnosticAnalyzer(), Nothing)
End Function
Private Function OptionOn() As IDictionary(Of OptionKey, Object)
Dim values = New Dictionary(Of OptionKey, Object)
values.Add(New OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.CSharp), True)
values.Add(New OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.VisualBasic), True)
Return values
End Function
Private Function OptionOff() As IDictionary(Of OptionKey, Object)
Dim values = New Dictionary(Of OptionKey, Object)
values.Add(New OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.CSharp), False)
values.Add(New OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.VisualBasic), False)
Return values
End Function
<Fact, Trait(Traits.Feature, Traits.Features.ValidateRegexString)>
Public Async Function TestWarning1() As Task
Await TestDiagnosticInfoAsync("
imports System.Text.RegularExpressions
class Program
sub Main()
var r = new Regex(""[|)|]"")
end sub
end class",
options:=OptionOn(),
diagnosticId:=IDEDiagnosticIds.RegexPatternDiagnosticId,
diagnosticSeverity:=DiagnosticSeverity.Warning,
diagnosticMessage:=String.Format(FeaturesResources.Regex_issue_0, WorkspacesResources.Too_many_close_parens))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.ValidateRegexString)>
Public Async Function TestWarning2() As Task
Await TestDiagnosticInfoAsync("
imports System.Text.RegularExpressions
class Program
sub Main()
var r = new Regex(""""""[|)|]"")
end sub
end class",
options:=OptionOn(),
diagnosticId:=IDEDiagnosticIds.RegexPatternDiagnosticId,
diagnosticSeverity:=DiagnosticSeverity.Warning,
diagnosticMessage:=String.Format(FeaturesResources.Regex_issue_0, WorkspacesResources.Too_many_close_parens))
End Function
End Class
End Namespace
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.EmbeddedLanguages.VirtualChars;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.ValidateRegexString;
namespace Microsoft.CodeAnalysis.CSharp.ValidateRegexString
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class CSharpValidateRegexStringDiagnosticAnalyzer : AbstractValidateRegexStringDiagnosticAnalyzer
{
public CSharpValidateRegexStringDiagnosticAnalyzer()
: base((int)SyntaxKind.StringLiteralToken,
CSharpSyntaxFactsService.Instance,
CSharpSemanticFactsService.Instance,
CSharpVirtualCharService.Instance)
{
}
}
}
......@@ -66,8 +66,6 @@ internal static class IDEDiagnosticIds
public const string MakeFieldReadonlyDiagnosticId = "IDE0044";
public const string RegexPatternDiagnosticId = "IDE0045";
// diagnostic for when we detect a string that should have /*language=json*/ added to it.
public const string JsonDetectionDiagnosticId = "IDE0046";
// diagnostic for when we have a known json string and we detect something wrong with it.
......
......@@ -60,12 +60,6 @@ internal abstract partial class AbstractDocumentHighlightsService : IDocumentHig
private async Task<ImmutableArray<DocumentHighlights>> GetDocumentHighlightsInCurrentProcessAsync(
Document document, int position, IImmutableSet<Document> documentsToSearch, CancellationToken cancellationToken)
{
var result = await TryGetRegexPatternHighlightsAsync(document, position, cancellationToken).ConfigureAwait(false);
if (!result.IsDefaultOrEmpty)
{
return result;
}
// use speculative semantic model to see whether we are on a symbol we can do HR
var span = new TextSpan(position, 0);
var solution = document.Project.Solution;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.EmbeddedLanguages.Common;
using Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions;
using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.DocumentHighlighting
{
using static EmbeddedSyntaxHelpers;
using RegexToken = EmbeddedSyntaxToken<RegexKind>;
internal abstract partial class AbstractDocumentHighlightsService : IDocumentHighlightsService
{
private async Task<ImmutableArray<DocumentHighlights>> TryGetRegexPatternHighlightsAsync(
Document document, int position, CancellationToken cancellationToken)
{
var option = document.Project.Solution.Workspace.Options.GetOption(RegularExpressionsOptions.HighlightRelatedRegexComponentsUnderCursor, document.Project.Language);
if (!option)
{
return default;
}
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(position);
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
if (RegexPatternDetector.IsDefinitelyNotPattern(token, syntaxFacts))
{
return default;
}
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var detector = RegexPatternDetector.TryGetOrCreate(semanticModel, syntaxFacts, document.GetLanguageService<ISemanticFactsService>());
var tree = detector?.TryParseRegexPattern(token, document.GetLanguageService<IVirtualCharService>(), cancellationToken);
if (tree == null)
{
return default;
}
return GetHighlights(document, tree, position);
}
private ImmutableArray<DocumentHighlights> GetHighlights(
Document document, RegexTree tree, int position)
{
var referencesOnTheRight = GetReferences(document, tree, position, caretOnLeft: true);
if (!referencesOnTheRight.IsEmpty)
{
return referencesOnTheRight;
}
// Nothing was on the right of the caret. Return anything we were able to find on
// the left of the caret
var referencesOnTheLeft = GetReferences(document, tree, position - 1, caretOnLeft: false);
return referencesOnTheLeft;
}
private ImmutableArray<DocumentHighlights> GetReferences(
Document document, RegexTree tree, int position, bool caretOnLeft)
{
var virtualChar = tree.Text.FirstOrNullable(vc => vc.Span.Contains(position));
if (virtualChar == null)
{
return ImmutableArray<DocumentHighlights>.Empty;
}
var ch = virtualChar.Value;
return FindReferenceHighlights(document, tree, ch);
}
private ImmutableArray<DocumentHighlights> FindReferenceHighlights(
Document document, RegexTree tree, VirtualChar ch)
{
var node = FindReferenceNode(tree.Root, ch);
if (node == null)
{
return ImmutableArray<DocumentHighlights>.Empty;
}
var captureToken = GetCaptureToken(node);
if (captureToken.Kind == RegexKind.NumberToken)
{
var val = (int)captureToken.Value;
if (tree.CaptureNumbersToSpan.TryGetValue(val, out var captureSpan))
{
return CreateHighlights(document, node, captureSpan);
}
}
else
{
var val = (string)captureToken.Value;
if (tree.CaptureNamesToSpan.TryGetValue(val, out var captureSpan))
{
return CreateHighlights(document, node, captureSpan);
}
}
return ImmutableArray<DocumentHighlights>.Empty;
}
private ImmutableArray<DocumentHighlights> CreateHighlights(
Document document, RegexEscapeNode node, TextSpan captureSpan)
{
return ImmutableArray.Create(new DocumentHighlights(document,
ImmutableArray.Create(
new HighlightSpan(node.GetSpan(), HighlightSpanKind.None),
new HighlightSpan(captureSpan, HighlightSpanKind.None))));
}
private RegexToken GetCaptureToken(RegexEscapeNode node)
{
switch (node)
{
case RegexBackreferenceEscapeNode backReference:
return backReference.NumberToken;
case RegexCaptureEscapeNode captureEscape:
return captureEscape.CaptureToken;
case RegexKCaptureEscapeNode kCaptureEscape:
return kCaptureEscape.CaptureToken;
}
throw new InvalidOperationException();
}
private RegexEscapeNode FindReferenceNode(RegexNode node, VirtualChar virtualChar)
{
if (node.Kind == RegexKind.BackreferenceEscape ||
node.Kind == RegexKind.CaptureEscape ||
node.Kind == RegexKind.KCaptureEscape)
{
if (node.Contains(virtualChar))
{
return (RegexEscapeNode)node;
}
}
foreach (var child in node)
{
if (child.IsNode)
{
var result = FindReferenceNode(child.Node, virtualChar);
if (result != null)
{
return result;
}
}
}
return null;
}
}
}
......@@ -2589,15 +2589,6 @@ internal class FeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Regex issue: {0}.
/// </summary>
internal static string Regex_issue_0 {
get {
return ResourceManager.GetString("Regex_issue_0", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Remarks:.
/// </summary>
......
......@@ -1334,9 +1334,6 @@ This version used in: {2}</value>
<data name="indexer_" xml:space="preserve">
<value>indexer</value>
</data>
<data name="Regex_issue_0" xml:space="preserve">
<value>Regex issue: {0}</value>
</data>
<data name="Enable_JSON_editor_features" xml:space="preserve">
<value>Enable JSON editor features</value>
</data>
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions;
using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars;
namespace Microsoft.CodeAnalysis.ValidateRegexString
{
internal abstract class AbstractValidateRegexStringDiagnosticAnalyzer : AbstractCodeStyleDiagnosticAnalyzer
{
private readonly int _stringLiteralKind;
private readonly ISyntaxFactsService _syntaxFacts;
private readonly ISemanticFactsService _semanticFacts;
private readonly IVirtualCharService _virtualCharService;
protected AbstractValidateRegexStringDiagnosticAnalyzer(
int stringLiteralKind,
ISyntaxFactsService syntaxFacts,
ISemanticFactsService semanticFacts,
IVirtualCharService virtualCharService)
: base(IDEDiagnosticIds.RegexPatternDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Regex_issue_0), FeaturesResources.ResourceManager, typeof(FeaturesResources)))
{
_stringLiteralKind = stringLiteralKind;
_syntaxFacts = syntaxFacts;
_semanticFacts = semanticFacts;
_virtualCharService = virtualCharService;
}
public override DiagnosticAnalyzerCategory GetAnalyzerCategory()
=> DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
public override bool OpenFileOnly(Workspace workspace)
=> false;
protected override void InitializeWorker(AnalysisContext context)
=> context.RegisterSemanticModelAction(AnalyzeSemanticModel);
private void AnalyzeSemanticModel(SemanticModelAnalysisContext context)
{
var semanticModel = context.SemanticModel;
var syntaxTree = semanticModel.SyntaxTree;
var cancellationToken = context.CancellationToken;
var options = context.Options;
var optionSet = options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();
if (optionSet == null)
{
return;
}
var option = optionSet.GetOption(RegularExpressionsOptions.ReportInvalidRegexPatterns, syntaxTree.Options.Language);
if (!option)
{
return;
}
var detector = RegexPatternDetector.TryGetOrCreate(semanticModel, _syntaxFacts, _semanticFacts);
if (detector == null)
{
return;
}
var root = syntaxTree.GetRoot(cancellationToken);
Analyze(context, detector, _virtualCharService, root, cancellationToken);
}
private void Analyze(
SemanticModelAnalysisContext context, RegexPatternDetector detector,
IVirtualCharService virtualCharService, SyntaxNode node, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
foreach (var child in node.ChildNodesAndTokens())
{
if (child.IsNode)
{
Analyze(context, detector, virtualCharService, child.AsNode(), cancellationToken);
}
else
{
var token = child.AsToken();
if (token.RawKind == _stringLiteralKind)
{
var tree = detector.TryParseRegexPattern(token, virtualCharService, cancellationToken);
if (tree != null)
{
foreach (var diag in tree.Diagnostics)
{
context.ReportDiagnostic(Diagnostic.Create(
this.GetDescriptorWithSeverity(DiagnosticSeverity.Warning),
Location.Create(context.SemanticModel.SyntaxTree, diag.Span),
diag.Message));
}
}
}
}
}
}
}
}
......@@ -1985,11 +1985,6 @@ Tato verze se používá zde: {2}.</target>
<target state="translated">indexer</target>
<note />
</trans-unit>
<trans-unit id="Regex_issue_0">
<source>Regex issue: {0}</source>
<target state="new">Regex issue: {0}</target>
<note />
</trans-unit>
<trans-unit id="Probable_JSON_string_detected">
<source>Probable JSON string detected</source>
<target state="new">Probable JSON string detected</target>
......
......@@ -1985,11 +1985,6 @@ Diese Version wird verwendet in: {2}</target>
<target state="translated">Indexer</target>
<note />
</trans-unit>
<trans-unit id="Regex_issue_0">
<source>Regex issue: {0}</source>
<target state="new">Regex issue: {0}</target>
<note />
</trans-unit>
<trans-unit id="Probable_JSON_string_detected">
<source>Probable JSON string detected</source>
<target state="new">Probable JSON string detected</target>
......
......@@ -1985,11 +1985,6 @@ Esta versión se utiliza en: {2}</target>
<target state="translated">indizador</target>
<note />
</trans-unit>
<trans-unit id="Regex_issue_0">
<source>Regex issue: {0}</source>
<target state="new">Regex issue: {0}</target>
<note />
</trans-unit>
<trans-unit id="Probable_JSON_string_detected">
<source>Probable JSON string detected</source>
<target state="new">Probable JSON string detected</target>
......
......@@ -1985,11 +1985,6 @@ Version utilisée dans : {2}</target>
<target state="translated">indexeur</target>
<note />
</trans-unit>
<trans-unit id="Regex_issue_0">
<source>Regex issue: {0}</source>
<target state="new">Regex issue: {0}</target>
<note />
</trans-unit>
<trans-unit id="Probable_JSON_string_detected">
<source>Probable JSON string detected</source>
<target state="new">Probable JSON string detected</target>
......
......@@ -1985,11 +1985,6 @@ Questa versione è usata {2}</target>
<target state="translated">indicizzatore</target>
<note />
</trans-unit>
<trans-unit id="Regex_issue_0">
<source>Regex issue: {0}</source>
<target state="new">Regex issue: {0}</target>
<note />
</trans-unit>
<trans-unit id="Probable_JSON_string_detected">
<source>Probable JSON string detected</source>
<target state="new">Probable JSON string detected</target>
......
......@@ -1985,11 +1985,6 @@ This version used in: {2}</source>
<target state="translated">インデクサー</target>
<note />
</trans-unit>
<trans-unit id="Regex_issue_0">
<source>Regex issue: {0}</source>
<target state="new">Regex issue: {0}</target>
<note />
</trans-unit>
<trans-unit id="Probable_JSON_string_detected">
<source>Probable JSON string detected</source>
<target state="new">Probable JSON string detected</target>
......
......@@ -1985,11 +1985,6 @@ This version used in: {2}</source>
<target state="translated">인덱서</target>
<note />
</trans-unit>
<trans-unit id="Regex_issue_0">
<source>Regex issue: {0}</source>
<target state="new">Regex issue: {0}</target>
<note />
</trans-unit>
<trans-unit id="Probable_JSON_string_detected">
<source>Probable JSON string detected</source>
<target state="new">Probable JSON string detected</target>
......
......@@ -1985,11 +1985,6 @@ Ta wersja jest używana wersja: {2}</target>
<target state="translated">indeksator</target>
<note />
</trans-unit>
<trans-unit id="Regex_issue_0">
<source>Regex issue: {0}</source>
<target state="new">Regex issue: {0}</target>
<note />
</trans-unit>
<trans-unit id="Probable_JSON_string_detected">
<source>Probable JSON string detected</source>
<target state="new">Probable JSON string detected</target>
......
......@@ -1985,11 +1985,6 @@ Essa versão é usada no: {2}</target>
<target state="translated">indexador</target>
<note />
</trans-unit>
<trans-unit id="Regex_issue_0">
<source>Regex issue: {0}</source>
<target state="new">Regex issue: {0}</target>
<note />
</trans-unit>
<trans-unit id="Probable_JSON_string_detected">
<source>Probable JSON string detected</source>
<target state="new">Probable JSON string detected</target>
......
......@@ -1985,11 +1985,6 @@ This version used in: {2}</source>
<target state="translated">индексатор</target>
<note />
</trans-unit>
<trans-unit id="Regex_issue_0">
<source>Regex issue: {0}</source>
<target state="new">Regex issue: {0}</target>
<note />
</trans-unit>
<trans-unit id="Probable_JSON_string_detected">
<source>Probable JSON string detected</source>
<target state="new">Probable JSON string detected</target>
......
......@@ -1985,11 +1985,6 @@ Bu sürüm şurada kullanılır: {2}</target>
<target state="translated">dizin oluşturucu</target>
<note />
</trans-unit>
<trans-unit id="Regex_issue_0">
<source>Regex issue: {0}</source>
<target state="new">Regex issue: {0}</target>
<note />
</trans-unit>
<trans-unit id="Probable_JSON_string_detected">
<source>Probable JSON string detected</source>
<target state="new">Probable JSON string detected</target>
......
......@@ -1985,11 +1985,6 @@ This version used in: {2}</source>
<target state="translated">索引器</target>
<note />
</trans-unit>
<trans-unit id="Regex_issue_0">
<source>Regex issue: {0}</source>
<target state="new">Regex issue: {0}</target>
<note />
</trans-unit>
<trans-unit id="Probable_JSON_string_detected">
<source>Probable JSON string detected</source>
<target state="new">Probable JSON string detected</target>
......
......@@ -1985,11 +1985,6 @@ This version used in: {2}</source>
<target state="translated">索引子</target>
<note />
</trans-unit>
<trans-unit id="Regex_issue_0">
<source>Regex issue: {0}</source>
<target state="new">Regex issue: {0}</target>
<note />
</trans-unit>
<trans-unit id="Probable_JSON_string_detected">
<source>Probable JSON string detected</source>
<target state="new">Probable JSON string detected</target>
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.ValidateRegexString
Imports Microsoft.CodeAnalysis.VisualBasic.EmbeddedLanguages.VirtualChars
Namespace Microsoft.CodeAnalysis.VisualBasic.ValidateRegexString
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend Class VisualBasicValidateRegexStringDiagnosticAnalyzer
Inherits AbstractValidateRegexStringDiagnosticAnalyzer
Public Sub New()
MyBase.New(SyntaxKind.StringLiteralToken,
VisualBasicSyntaxFactsService.Instance,
VisualBasicSemanticFactsService.Instance,
VisualBasicVirtualCharService.Instance)
End Sub
End Class
End Namespace
......@@ -6,7 +6,6 @@
using Microsoft.CodeAnalysis.Editor.CSharp.SplitStringLiteral;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.EmbeddedLanguages.Json;
using Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions;
using Microsoft.CodeAnalysis.ExtractMethod;
using Microsoft.CodeAnalysis.Fading;
using Microsoft.CodeAnalysis.ImplementType;
......@@ -64,10 +63,6 @@ public AdvancedOptionPageControl(IServiceProvider serviceProvider) : base(servic
BindToOption(Report_invalid_placeholders_in_string_dot_format_calls, ValidateFormatStringOption.ReportInvalidPlaceholdersInStringDotFormatCalls, LanguageNames.CSharp);
BindToOption(Colorize_regular_expressions, RegularExpressionsOptions.ColorizeRegexPatterns, LanguageNames.CSharp);
BindToOption(Report_invalid_regular_expressions, RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.CSharp);
BindToOption(Highlight_related_regex_components_under_cursor, RegularExpressionsOptions.HighlightRelatedRegexComponentsUnderCursor, LanguageNames.CSharp);
BindToOption(Detect_and_offer_editor_features_for_likely_JSON_strings, JsonOptions.DetectAndOfferEditorFeaturesForProbableJsonStrings, LanguageNames.CSharp);
BindToOption(Colorize_JSON_strings, JsonOptions.ColorizeJsonPatterns, LanguageNames.CSharp);
BindToOption(Report_invalid_JSON_strings, JsonOptions.ReportInvalidJsonPatterns, LanguageNames.CSharp);
......
......@@ -75,16 +75,6 @@ public void RefreshThemeColors()
UpdateForegroundColor(ClassificationTypeNames.XmlDocCommentComment, sourceFormatMap, targetFormatMap);
UpdateForegroundColor(ClassificationTypeNames.XmlDocCommentCDataSection, sourceFormatMap, targetFormatMap);
UpdateForegroundColor(ClassificationTypeNames.RegexComment, sourceFormatMap, targetFormatMap);
UpdateForegroundColor(ClassificationTypeNames.RegexText, sourceFormatMap, targetFormatMap);
UpdateForegroundColor(ClassificationTypeNames.RegexCharacterClass, sourceFormatMap, targetFormatMap);
UpdateForegroundColor(ClassificationTypeNames.RegexQuantifier, sourceFormatMap, targetFormatMap);
UpdateForegroundColor(ClassificationTypeNames.RegexAnchor, sourceFormatMap, targetFormatMap);
UpdateForegroundColor(ClassificationTypeNames.RegexAlternation, sourceFormatMap, targetFormatMap);
UpdateForegroundColor(ClassificationTypeNames.RegexGrouping, sourceFormatMap, targetFormatMap);
UpdateForegroundColor(ClassificationTypeNames.RegexOtherEscape, sourceFormatMap, targetFormatMap);
UpdateForegroundColor(ClassificationTypeNames.RegexSelfEscapedCharacter, sourceFormatMap, targetFormatMap);
UpdateForegroundColor(ClassificationTypeNames.JsonComment, sourceFormatMap, targetFormatMap);
UpdateForegroundColor(ClassificationTypeNames.JsonNumber, sourceFormatMap, targetFormatMap);
UpdateForegroundColor(ClassificationTypeNames.JsonString, sourceFormatMap, targetFormatMap);
......
......@@ -4,7 +4,6 @@ Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editing
Imports Microsoft.CodeAnalysis.Editor.Shared.Options
Imports Microsoft.CodeAnalysis.EmbeddedLanguages.Json
Imports Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions
Imports Microsoft.CodeAnalysis.ExtractMethod
Imports Microsoft.CodeAnalysis.Fading
Imports Microsoft.CodeAnalysis.ImplementType
......@@ -61,10 +60,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options
BindToOption(Report_invalid_placeholders_in_string_dot_format_calls, ValidateFormatStringOption.ReportInvalidPlaceholdersInStringDotFormatCalls, LanguageNames.VisualBasic)
BindToOption(Colorize_regular_expressions, RegularExpressionsOptions.ColorizeRegexPatterns, LanguageNames.VisualBasic)
BindToOption(Report_invalid_regular_expressions, RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.VisualBasic)
BindToOption(Highlight_related_regex_components_under_cursor, RegularExpressionsOptions.HighlightRelatedRegexComponentsUnderCursor, LanguageNames.VisualBasic)
BindToOption(Detect_and_offer_editor_features_for_likely_JSON_strings, JsonOptions.DetectAndOfferEditorFeaturesForProbableJsonStrings, LanguageNames.VisualBasic)
BindToOption(Colorize_JSON_strings, JsonOptions.ColorizeJsonPatterns, LanguageNames.VisualBasic)
BindToOption(Report_invalid_JSON_strings, JsonOptions.ReportInvalidJsonPatterns, LanguageNames.VisualBasic)
......
......@@ -20,7 +20,6 @@ internal class CSharpSyntaxClassificationService : AbstractSyntaxClassificationS
new JsonPatternTokenClassifier(),
new NameSyntaxClassifier(),
new SyntaxTokenClassifier(),
new RegexPatternTokenClassifier(),
new UsingDirectiveSyntaxClassifier());
public override ImmutableArray<ISyntaxClassifier> GetDefaultSyntaxClassifiers()
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.CSharp.EmbeddedLanguages.VirtualChars;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.CSharp.Classification.Classifiers
{
internal class RegexPatternTokenClassifier : AbstractSyntaxClassifier
{
public override ImmutableArray<int> SyntaxTokenKinds { get; } = ImmutableArray.Create((int)SyntaxKind.StringLiteralToken);
public override void AddClassifications(Workspace workspace, SyntaxToken token, SemanticModel semanticModel, ArrayBuilder<ClassifiedSpan> result, CancellationToken cancellationToken)
{
Debug.Assert(token.Kind() == SyntaxKind.StringLiteralToken);
CommonRegexPatternTokenClassifier.AddClassifications(
workspace, token, semanticModel, result,
CSharpSyntaxFactsService.Instance,
CSharpSemanticFactsService.Instance,
CSharpVirtualCharService.Instance,
cancellationToken);
}
}
}
......@@ -60,16 +60,6 @@ public static class ClassificationTypeNames
public const string XmlLiteralProcessingInstruction = "xml literal - processing instruction";
public const string XmlLiteralText = "xml literal - text";
internal const string RegexComment = "regex - comment";
internal const string RegexCharacterClass = "regex - character class";
internal const string RegexAnchor = "regex - anchor";
internal const string RegexQuantifier = "regex - quantifier";
internal const string RegexGrouping = "regex - grouping";
internal const string RegexAlternation = "regex - alternation";
internal const string RegexText = "regex - text";
internal const string RegexSelfEscapedCharacter = "regex - self escaped character";
internal const string RegexOtherEscape = "regex - other escape";
internal const string JsonComment = "json - comment";
internal const string JsonNumber = "json - number";
internal const string JsonString = "json - string";
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.EmbeddedLanguages.Common;
using Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions;
using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.Classification
{
using static EmbeddedSyntaxHelpers;
using RegexToken = EmbeddedSyntaxToken<RegexKind>;
using RegexTrivia = EmbeddedSyntaxTrivia<RegexKind>;
internal static class CommonRegexPatternTokenClassifier
{
private static ObjectPool<Visitor> _visitorPool = new ObjectPool<Visitor>(() => new Visitor());
public static void AddClassifications(
Workspace workspace, SyntaxToken token, SemanticModel semanticModel, ArrayBuilder<ClassifiedSpan> result,
ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, IVirtualCharService virtualCharService,
CancellationToken cancellationToken)
{
if (!workspace.Options.GetOption(RegularExpressionsOptions.ColorizeRegexPatterns, LanguageNames.CSharp))
{
return;
}
// Do some quick syntactic checks before doing any complex work.
if (RegexPatternDetector.IsDefinitelyNotPattern(token, syntaxFacts))
{
return;
}
var detector = RegexPatternDetector.TryGetOrCreate(semanticModel, syntaxFacts, semanticFacts);
var tree = detector?.TryParseRegexPattern(token, virtualCharService, cancellationToken);
if (tree == null)
{
return;
}
var visitor = _visitorPool.Allocate();
try
{
visitor.Result = result;
AddClassifications(tree.Root, visitor, result);
}
finally
{
visitor.Result = null;
_visitorPool.Free(visitor);
}
}
private static void AddClassifications(RegexNode node, Visitor visitor, ArrayBuilder<ClassifiedSpan> result)
{
node.Accept(visitor);
foreach (var child in node)
{
if (child.IsNode)
{
AddClassifications(child.Node, visitor, result);
}
else
{
AddTriviaClassifications(child.Token, result);
}
}
}
private static void AddTriviaClassifications(RegexToken token, ArrayBuilder<ClassifiedSpan> result)
{
foreach (var trivia in token.LeadingTrivia)
{
AddTriviaClassifications(trivia, result);
}
}
private static void AddTriviaClassifications(RegexTrivia trivia, ArrayBuilder<ClassifiedSpan> result)
{
if (trivia.Kind == RegexKind.CommentTrivia &&
trivia.VirtualChars.Length > 0)
{
result.Add(new ClassifiedSpan(
ClassificationTypeNames.RegexComment, GetSpan(trivia.VirtualChars)));
}
}
private class Visitor : IRegexNodeVisitor
{
public ArrayBuilder<ClassifiedSpan> Result;
private void AddClassification(RegexToken token, string typeName)
{
if (!token.IsMissing)
{
Result.Add(new ClassifiedSpan(typeName, token.GetSpan()));
}
}
private void ClassifyWholeNode(RegexNode node, string typeName)
{
foreach (var child in node)
{
if (child.IsNode)
{
ClassifyWholeNode(child.Node, typeName);
}
else
{
AddClassification(child.Token, typeName);
}
}
}
public void Visit(RegexCompilationUnit node)
{
// Nothing to highlight.
}
public void Visit(RegexSequenceNode node)
{
// Nothing to highlight.
}
#region Character classes
public void Visit(RegexWildcardNode node)
=> AddClassification(node.DotToken, ClassificationTypeNames.RegexCharacterClass);
public void Visit(RegexCharacterClassNode node)
{
AddClassification(node.OpenBracketToken, ClassificationTypeNames.RegexCharacterClass);
AddClassification(node.CloseBracketToken, ClassificationTypeNames.RegexCharacterClass);
}
public void Visit(RegexNegatedCharacterClassNode node)
{
AddClassification(node.OpenBracketToken, ClassificationTypeNames.RegexCharacterClass);
AddClassification(node.CaretToken, ClassificationTypeNames.RegexCharacterClass);
AddClassification(node.CloseBracketToken, ClassificationTypeNames.RegexCharacterClass);
}
public void Visit(RegexCharacterClassRangeNode node)
=> AddClassification(node.MinusToken, ClassificationTypeNames.RegexCharacterClass);
public void Visit(RegexCharacterClassSubtractionNode node)
=> AddClassification(node.MinusToken, ClassificationTypeNames.RegexCharacterClass);
public void Visit(RegexCharacterClassEscapeNode node)
=> ClassifyWholeNode(node, ClassificationTypeNames.RegexCharacterClass);
public void Visit(RegexCategoryEscapeNode node)
=> ClassifyWholeNode(node, ClassificationTypeNames.RegexCharacterClass);
#endregion
#region Quantifiers
public void Visit(RegexZeroOrMoreQuantifierNode node)
=> AddClassification(node.AsteriskToken, ClassificationTypeNames.RegexQuantifier);
public void Visit(RegexOneOrMoreQuantifierNode node)
=> AddClassification(node.PlusToken, ClassificationTypeNames.RegexQuantifier);
public void Visit(RegexZeroOrOneQuantifierNode node)
=> AddClassification(node.QuestionToken, ClassificationTypeNames.RegexQuantifier);
public void Visit(RegexLazyQuantifierNode node)
=> AddClassification(node.QuestionToken, ClassificationTypeNames.RegexQuantifier);
public void Visit(RegexExactNumericQuantifierNode node)
{
AddClassification(node.OpenBraceToken, ClassificationTypeNames.RegexQuantifier);
AddClassification(node.FirstNumberToken, ClassificationTypeNames.RegexQuantifier);
AddClassification(node.CloseBraceToken, ClassificationTypeNames.RegexQuantifier);
}
public void Visit(RegexOpenNumericRangeQuantifierNode node)
{
AddClassification(node.OpenBraceToken, ClassificationTypeNames.RegexQuantifier);
AddClassification(node.FirstNumberToken, ClassificationTypeNames.RegexQuantifier);
AddClassification(node.CommaToken, ClassificationTypeNames.RegexQuantifier);
AddClassification(node.CloseBraceToken, ClassificationTypeNames.RegexQuantifier);
}
public void Visit(RegexClosedNumericRangeQuantifierNode node)
{
AddClassification(node.OpenBraceToken, ClassificationTypeNames.RegexQuantifier);
AddClassification(node.FirstNumberToken, ClassificationTypeNames.RegexQuantifier);
AddClassification(node.CommaToken, ClassificationTypeNames.RegexQuantifier);
AddClassification(node.SecondNumberToken, ClassificationTypeNames.RegexQuantifier);
AddClassification(node.CloseBraceToken, ClassificationTypeNames.RegexQuantifier);
}
#endregion
#region Groupings
public void Visit(RegexSimpleGroupingNode node)
=> ClassifyGrouping(node);
public void Visit(RegexSimpleOptionsGroupingNode node)
=> ClassifyGrouping(node);
public void Visit(RegexNestedOptionsGroupingNode node)
=> ClassifyGrouping(node);
public void Visit(RegexNonCapturingGroupingNode node)
=> ClassifyGrouping(node);
public void Visit(RegexPositiveLookaheadGroupingNode node)
=> ClassifyGrouping(node);
public void Visit(RegexNegativeLookaheadGroupingNode node)
=> ClassifyGrouping(node);
public void Visit(RegexPositiveLookbehindGroupingNode node)
=> ClassifyGrouping(node);
public void Visit(RegexNegativeLookbehindGroupingNode node)
=> ClassifyGrouping(node);
public void Visit(RegexNonBacktrackingGroupingNode node)
=> ClassifyGrouping(node);
public void Visit(RegexCaptureGroupingNode node)
=> ClassifyGrouping(node);
public void Visit(RegexBalancingGroupingNode node)
=> ClassifyGrouping(node);
public void Visit(RegexConditionalCaptureGroupingNode node)
=> ClassifyGrouping(node);
public void Visit(RegexConditionalExpressionGroupingNode node)
=> ClassifyGrouping(node);
// Captures and backreferences refer to groups. So we classify them the same way as groups.
public void Visit(RegexCaptureEscapeNode node)
=> ClassifyWholeNode(node, ClassificationTypeNames.RegexGrouping);
public void Visit(RegexKCaptureEscapeNode node)
=> ClassifyWholeNode(node, ClassificationTypeNames.RegexGrouping);
public void Visit(RegexBackreferenceEscapeNode node)
=> ClassifyWholeNode(node, ClassificationTypeNames.RegexGrouping);
private void ClassifyGrouping(RegexGroupingNode node)
{
foreach (var child in node)
{
if (!child.IsNode)
{
AddClassification(child.Token, ClassificationTypeNames.RegexGrouping);
}
}
}
#endregion
#region Other Escapes
public void Visit(RegexControlEscapeNode node)
=> ClassifyOtherEscape(node);
public void Visit(RegexHexEscapeNode node)
=> ClassifyOtherEscape(node);
public void Visit(RegexUnicodeEscapeNode node)
=> ClassifyOtherEscape(node);
public void Visit(RegexOctalEscapeNode node)
=> ClassifyOtherEscape(node);
public void ClassifyOtherEscape(RegexNode node)
=> ClassifyWholeNode(node, ClassificationTypeNames.RegexOtherEscape);
#endregion
#region Anchors
public void Visit(RegexAnchorNode node)
=> AddClassification(node.AnchorToken, ClassificationTypeNames.RegexAnchor);
public void Visit(RegexAnchorEscapeNode node)
=> ClassifyWholeNode(node, ClassificationTypeNames.RegexAnchor);
#endregion
public void Visit(RegexTextNode node)
=> AddClassification(node.TextToken, ClassificationTypeNames.RegexText);
public void Visit(RegexPosixPropertyNode node)
{
// The .net parser just interprets the [ of the node, and skips the rest. So
// classify the end part as a comment.
Result.Add(new ClassifiedSpan(node.TextToken.VirtualChars[0].Span, ClassificationTypeNames.RegexText));
Result.Add(new ClassifiedSpan(
GetSpan(node.TextToken.VirtualChars[1], node.TextToken.VirtualChars.Last()),
ClassificationTypeNames.RegexComment));
}
public void Visit(RegexAlternationNode node)
=> AddClassification(node.BarToken, ClassificationTypeNames.RegexAlternation);
public void Visit(RegexSimpleEscapeNode node)
=> ClassifyWholeNode(node, node.IsSelfEscape()
? ClassificationTypeNames.RegexSelfEscapedCharacter
: ClassificationTypeNames.RegexOtherEscape);
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions
{
internal interface IRegexNodeVisitor
{
void Visit(RegexCompilationUnit node);
void Visit(RegexSequenceNode node);
void Visit(RegexTextNode node);
void Visit(RegexCharacterClassNode node);
void Visit(RegexNegatedCharacterClassNode node);
void Visit(RegexCharacterClassRangeNode node);
void Visit(RegexCharacterClassSubtractionNode node);
void Visit(RegexPosixPropertyNode node);
void Visit(RegexWildcardNode node);
void Visit(RegexZeroOrMoreQuantifierNode node);
void Visit(RegexOneOrMoreQuantifierNode node);
void Visit(RegexZeroOrOneQuantifierNode node);
void Visit(RegexLazyQuantifierNode node);
void Visit(RegexExactNumericQuantifierNode node);
void Visit(RegexOpenNumericRangeQuantifierNode node);
void Visit(RegexClosedNumericRangeQuantifierNode node);
void Visit(RegexAnchorNode node);
void Visit(RegexAlternationNode node);
void Visit(RegexSimpleGroupingNode node);
void Visit(RegexSimpleOptionsGroupingNode node);
void Visit(RegexNestedOptionsGroupingNode node);
void Visit(RegexNonCapturingGroupingNode node);
void Visit(RegexPositiveLookaheadGroupingNode node);
void Visit(RegexNegativeLookaheadGroupingNode node);
void Visit(RegexPositiveLookbehindGroupingNode node);
void Visit(RegexNegativeLookbehindGroupingNode node);
void Visit(RegexNonBacktrackingGroupingNode node);
void Visit(RegexCaptureGroupingNode node);
void Visit(RegexBalancingGroupingNode node);
void Visit(RegexConditionalCaptureGroupingNode node);
void Visit(RegexConditionalExpressionGroupingNode node);
void Visit(RegexSimpleEscapeNode node);
void Visit(RegexAnchorEscapeNode node);
void Visit(RegexCharacterClassEscapeNode node);
void Visit(RegexControlEscapeNode node);
void Visit(RegexHexEscapeNode node);
void Visit(RegexUnicodeEscapeNode node);
void Visit(RegexCaptureEscapeNode node);
void Visit(RegexKCaptureEscapeNode node);
void Visit(RegexOctalEscapeNode node);
void Visit(RegexBackreferenceEscapeNode node);
void Visit(RegexCategoryEscapeNode node);
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
namespace Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions
{
/// <summary>
/// Minimal copy of https://github.com/dotnet/corefx/blob/master/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
/// Used to accurately determine if something is a WordChar according to the .Net regex engine.
/// </summary>
internal static class RegexCharClass
{
private const int FLAGS = 0;
private const int SETLENGTH = 1;
private const int CATEGORYLENGTH = 2;
private const int SETSTART = 3;
private const short SpaceConst = 100;
private const short NotSpaceConst = -100;
private const char ZeroWidthJoiner = '\u200D';
private const char ZeroWidthNonJoiner = '\u200C';
private const string WordClass = "\u0000\u0000\u000A\u0000\u0002\u0004\u0005\u0003\u0001\u0006\u0009\u0013\u0000";
private static readonly HashSet<string> s_escapeCategories = new HashSet<string>
{
// Others
"Cc", "Cf", "Cn", "Co", "Cs", "C",
// Letters
"Ll", "Lm", "Lo", "Lt", "Lu", "L",
// Marks
"Mc", "Me", "Mn", "M",
// Numbers
"Nd", "Nl", "No", "N",
// Punctuation
"Pc", "Pd", "Pe", "Po", "Ps", "Pf", "Pi", "P",
// Symbols
"Sc", "Sk", "Sm", "So", "S",
// Separators
"Zl", "Zp", "Zs", "Z",
"IsAlphabeticPresentationForms",
"IsArabic",
"IsArabicPresentationForms-A",
"IsArabicPresentationForms-B",
"IsArmenian",
"IsArrows",
"IsBasicLatin",
"IsBengali",
"IsBlockElements",
"IsBopomofo",
"IsBopomofoExtended",
"IsBoxDrawing",
"IsBraillePatterns",
"IsBuhid",
"IsCJKCompatibility",
"IsCJKCompatibilityForms",
"IsCJKCompatibilityIdeographs",
"IsCJKRadicalsSupplement",
"IsCJKSymbolsandPunctuation",
"IsCJKUnifiedIdeographs",
"IsCJKUnifiedIdeographsExtensionA",
"IsCherokee",
"IsCombiningDiacriticalMarks",
"IsCombiningDiacriticalMarksforSymbols",
"IsCombiningHalfMarks",
"IsCombiningMarksforSymbols",
"IsControlPictures",
"IsCurrencySymbols",
"IsCyrillic",
"IsCyrillicSupplement",
"IsDevanagari",
"IsDingbats",
"IsEnclosedAlphanumerics",
"IsEnclosedCJKLettersandMonths",
"IsEthiopic",
"IsGeneralPunctuation",
"IsGeometricShapes",
"IsGeorgian",
"IsGreek",
"IsGreekExtended",
"IsGreekandCoptic",
"IsGujarati",
"IsGurmukhi",
"IsHalfwidthandFullwidthForms",
"IsHangulCompatibilityJamo",
"IsHangulJamo",
"IsHangulSyllables",
"IsHanunoo",
"IsHebrew",
"IsHighPrivateUseSurrogates",
"IsHighSurrogates",
"IsHiragana",
"IsIPAExtensions",
"IsIdeographicDescriptionCharacters",
"IsKanbun",
"IsKangxiRadicals",
"IsKannada",
"IsKatakana",
"IsKatakanaPhoneticExtensions",
"IsKhmer",
"IsKhmerSymbols",
"IsLao",
"IsLatin-1Supplement",
"IsLatinExtended-A",
"IsLatinExtended-B",
"IsLatinExtendedAdditional",
"IsLetterlikeSymbols",
"IsLimbu",
"IsLowSurrogates",
"IsMalayalam",
"IsMathematicalOperators",
"IsMiscellaneousMathematicalSymbols-A",
"IsMiscellaneousMathematicalSymbols-B",
"IsMiscellaneousSymbols",
"IsMiscellaneousSymbolsandArrows",
"IsMiscellaneousTechnical",
"IsMongolian",
"IsMyanmar",
"IsNumberForms",
"IsOgham",
"IsOpticalCharacterRecognition",
"IsOriya",
"IsPhoneticExtensions",
"IsPrivateUse",
"IsPrivateUseArea",
"IsRunic",
"IsSinhala",
"IsSmallFormVariants",
"IsSpacingModifierLetters",
"IsSpecials",
"IsSuperscriptsandSubscripts",
"IsSupplementalArrows-A",
"IsSupplementalArrows-B",
"IsSupplementalMathematicalOperators",
"IsSyriac",
"IsTagalog",
"IsTagbanwa",
"IsTaiLe",
"IsTamil",
"IsTelugu",
"IsThaana",
"IsThai",
"IsTibetan",
"IsUnifiedCanadianAboriginalSyllabics",
"IsVariationSelectors",
"IsYiRadicals",
"IsYiSyllables",
"IsYijingHexagramSymbols",
"_xmlC",
"_xmlD",
"_xmlI",
"_xmlW",
};
public static bool IsEscapeCategory(string value)
{
return s_escapeCategories.Contains(value);
}
public static bool IsWordChar(char ch)
{
// According to UTS#18 Unicode Regular Expressions (http://www.unicode.org/reports/tr18/)
// RL 1.4 Simple Word Boundaries The class of <word_character> includes all Alphabetic
// values from the Unicode character database, from UnicodeData.txt [UData], plus the U+200C
// ZERO WIDTH NON-JOINER and U+200D ZERO WIDTH JOINER.
return CharInClass(ch, WordClass) || ch == ZeroWidthJoiner || ch == ZeroWidthNonJoiner;
}
internal static bool CharInClass(char ch, string set)
{
return CharInClassRecursive(ch, set, 0);
}
internal static bool CharInClassRecursive(char ch, string set, int start)
{
int mySetLength = set[start + SETLENGTH];
int myCategoryLength = set[start + CATEGORYLENGTH];
int myEndPosition = start + SETSTART + mySetLength + myCategoryLength;
bool subtracted = false;
if (set.Length > myEndPosition)
{
subtracted = CharInClassRecursive(ch, set, myEndPosition);
}
bool b = CharInClassInternal(ch, set, start, mySetLength, myCategoryLength);
// Note that we apply the negation *before* performing the subtraction. This is because
// the negation only applies to the first char class, not the entire subtraction.
if (set[start + FLAGS] == 1)
b = !b;
return b && !subtracted;
}
/// <summary>
/// Determines a character's membership in a character class (via the
/// string representation of the class).
/// </summary>
private static bool CharInClassInternal(char ch, string set, int start, int mySetLength, int myCategoryLength)
{
int min;
int max;
int mid;
min = start + SETSTART;
max = min + mySetLength;
while (min != max)
{
mid = (min + max) / 2;
if (ch < set[mid])
max = mid;
else
min = mid + 1;
}
// The starting position of the set within the character class determines
// whether what an odd or even ending position means. If the start is odd,
// an *even* ending position means the character was in the set. With recursive
// subtractions in the mix, the starting position = start+SETSTART. Since we know that
// SETSTART is odd, we can simplify it out of the equation. But if it changes we need to
// reverse this check.
Debug.Assert((SETSTART & 0x1) == 1, "If SETSTART is not odd, the calculation below this will be reversed");
if ((min & 0x1) == (start & 0x1))
return true;
else
{
if (myCategoryLength == 0)
return false;
return CharInCategory(ch, set, start, mySetLength, myCategoryLength);
}
}
private static bool CharInCategory(char ch, string set, int start, int mySetLength, int myCategoryLength)
{
UnicodeCategory chcategory = CharUnicodeInfo.GetUnicodeCategory(ch);
int i = start + SETSTART + mySetLength;
int end = i + myCategoryLength;
while (i < end)
{
int curcat = unchecked((short)set[i]);
if (curcat == 0)
{
// zero is our marker for a group of categories - treated as a unit
if (CharInCategoryGroup(ch, chcategory, set, ref i))
return true;
}
else if (curcat > 0)
{
// greater than zero is a positive case
if (curcat == SpaceConst)
{
if (char.IsWhiteSpace(ch))
return true;
else
{
i++;
continue;
}
}
--curcat;
if (chcategory == (UnicodeCategory)curcat)
return true;
}
else
{
// less than zero is a negative case
if (curcat == NotSpaceConst)
{
if (!char.IsWhiteSpace(ch))
return true;
else
{
i++;
continue;
}
}
//curcat = -curcat;
//--curcat;
curcat = -1 - curcat;
if (chcategory != (UnicodeCategory)curcat)
return true;
}
i++;
}
return false;
}
/// <summary>
/// This is used for categories which are composed of other categories - L, N, Z, W...
/// These groups need special treatment when they are negated
/// </summary>
private static bool CharInCategoryGroup(char ch, UnicodeCategory chcategory, string category, ref int i)
{
i++;
int curcat = unchecked((short)category[i]);
if (curcat > 0)
{
// positive case - the character must be in ANY of the categories in the group
bool answer = false;
while (curcat != 0)
{
if (!answer)
{
--curcat;
if (chcategory == (UnicodeCategory)curcat)
answer = true;
}
i++;
curcat = (short)category[i];
}
return answer;
}
else
{
// negative case - the character must be in NONE of the categories in the group
bool answer = true;
while (curcat != 0)
{
if (answer)
{
//curcat = -curcat;
//--curcat;
curcat = -1 - curcat;
if (chcategory == (UnicodeCategory)curcat)
answer = false;
}
i++;
curcat = unchecked((short)category[i]);
}
return answer;
}
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis.EmbeddedLanguages.Common;
using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars;
namespace Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions
{
using RegexToken = EmbeddedSyntaxToken<RegexKind>;
using RegexTrivia = EmbeddedSyntaxTrivia<RegexKind>;
internal static class RegexHelpers
{
public static bool HasOption(RegexOptions options, RegexOptions val)
=> (options & val) != 0;
public static RegexToken CreateToken(RegexKind kind, ImmutableArray<RegexTrivia> leadingTrivia, ImmutableArray<VirtualChar> virtualChars)
=> new RegexToken(kind, leadingTrivia, virtualChars, ImmutableArray<RegexTrivia>.Empty, ImmutableArray<EmbeddedDiagnostic>.Empty, value: null);
public static RegexToken CreateMissingToken(RegexKind kind)
=> CreateToken(kind, ImmutableArray<RegexTrivia>.Empty, ImmutableArray<VirtualChar>.Empty);
public static RegexTrivia CreateTrivia(RegexKind kind, ImmutableArray<VirtualChar> virtualChars)
=> CreateTrivia(kind, virtualChars, ImmutableArray<EmbeddedDiagnostic>.Empty);
public static RegexTrivia CreateTrivia(RegexKind kind, ImmutableArray<VirtualChar> virtualChars, ImmutableArray<EmbeddedDiagnostic> diagnostics)
=> new RegexTrivia(kind, virtualChars, diagnostics);
public static bool IsSelfEscape(this RegexSimpleEscapeNode node)
{
if (node.TypeToken.VirtualChars.Length > 0)
{
switch (node.TypeToken.VirtualChars[0].Char)
{
case 'a':
case 'b':
case 'e':
case 'f':
case 'n':
case 'r':
case 't':
case 'v':
return false;
}
}
return true;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions
{
internal enum RegexKind
{
None,
EndOfFile,
Sequence,
CompilationUnit,
Text,
StartAnchor,
EndAnchor,
Alternation,
Wildcard,
CharacterClass,
NegatedCharacterClass,
CharacterClassRange,
CharacterClassSubtraction,
PosixProperty,
ZeroOrMoreQuantifier,
OneOrMoreQuantifier,
ZeroOrOneQuantifier,
ExactNumericQuantifier,
OpenRangeNumericQuantifier,
ClosedRangeNumericQuantifier,
LazyQuantifier,
SimpleGrouping,
SimpleOptionsGrouping,
NestedOptionsGrouping,
NonCapturingGrouping,
PositiveLookaheadGrouping,
NegativeLookaheadGrouping,
PositiveLookbehindGrouping,
NegativeLookbehindGrouping,
NonBacktrackingGrouping,
CaptureGrouping,
BalancingGrouping,
ConditionalCaptureGrouping,
ConditionalExpressionGrouping,
SimpleEscape,
AnchorEscape,
CharacterClassEscape,
CategoryEscape,
ControlEscape,
HexEscape,
UnicodeEscape,
OctalEscape,
CaptureEscape,
KCaptureEscape,
BackreferenceEscape,
// Tokens
DollarToken,
OpenBraceToken,
CloseBraceToken,
OpenBracketToken,
CloseBracketToken,
OpenParenToken,
CloseParenToken,
BarToken,
DotToken,
CaretToken,
TextToken,
QuestionToken,
AsteriskToken,
PlusToken,
CommaToken,
BackslashToken,
ColonToken,
EqualsToken,
ExclamationToken,
GreaterThanToken,
LessThanToken,
MinusToken,
SingleQuoteToken,
// Special multi-character tokens that have to be explicitly requested.
OptionsToken,
NumberToken,
CaptureNameToken,
EscapeCategoryToken,
// Trivia
CommentTrivia,
WhitespaceTrivia,
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis.EmbeddedLanguages.Common;
using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions
{
using static EmbeddedSyntaxHelpers;
using static RegexHelpers;
using RegexToken = EmbeddedSyntaxToken<RegexKind>;
using RegexTrivia = EmbeddedSyntaxTrivia<RegexKind>;
/// <summary>
/// Produces tokens from the sequence of <see cref="VirtualChar"/> characters. Unlike the
/// native C# and VB lexer, this lexer is much more tightly controlled by the parser. For
/// example, while C# can have trivia on virtual every token, the same is not true for
/// RegexTokens. As such, instead of automatically lexing out tokens to make them available for
/// the parser, the parser asks for each token as necessary passing the right information to
/// indicate which types and shapes of tokens are allowed.
///
/// The tight coupling means that the parser is allowed direct control of the position of the
/// lexer.
///
/// Note: most of the time, tokens returned are just a single character long, including for long
/// sequences of text characters (like ```"goo"```). This is just three <see
/// cref="RegexTextNode"/>s in a row (each containing a <see cref="RegexKind.TextToken"/> a
/// single character long).
///
/// There are multi-character tokens though. For example ```10``` in ```a{10,}``` or ```name```
/// in ```\k'name'```
/// </summary>
internal struct RegexLexer
{
public readonly ImmutableArray<VirtualChar> Text;
public int Position;
public RegexLexer(ImmutableArray<VirtualChar> text) : this()
{
Text = text;
}
public VirtualChar CurrentChar => Position < Text.Length ? Text[Position] : new VirtualChar((char)0, default);
public ImmutableArray<VirtualChar> GetSubPatternToCurrentPos(int start)
=> GetSubPattern(start, Position);
public ImmutableArray<VirtualChar> GetSubPattern(int start, int end)
{
var result = ArrayBuilder<VirtualChar>.GetInstance(end - start);
for (var i = start; i < end; i++)
{
result.Add(Text[i]);
}
return result.ToImmutableAndFree();
}
public RegexToken ScanNextToken(bool allowTrivia, RegexOptions options)
{
var trivia = ScanLeadingTrivia(allowTrivia, options);
if (Position == Text.Length)
{
return CreateToken(RegexKind.EndOfFile, trivia, ImmutableArray<VirtualChar>.Empty);
}
var ch = this.CurrentChar;
Position++;
return CreateToken(GetKind(ch), trivia, ImmutableArray.Create(ch));
}
private static RegexKind GetKind(char ch)
{
switch (ch)
{
case '|': return RegexKind.BarToken;
case '*': return RegexKind.AsteriskToken;
case '+': return RegexKind.PlusToken;
case '?': return RegexKind.QuestionToken;
case '{': return RegexKind.OpenBraceToken;
case '}': return RegexKind.CloseBraceToken;
case '\\': return RegexKind.BackslashToken;
case '[': return RegexKind.OpenBracketToken;
case ']': return RegexKind.CloseBracketToken;
case '.': return RegexKind.DotToken;
case '^': return RegexKind.CaretToken;
case '$': return RegexKind.DollarToken;
case '(': return RegexKind.OpenParenToken;
case ')': return RegexKind.CloseParenToken;
case ',': return RegexKind.CommaToken;
case ':': return RegexKind.ColonToken;
case '=': return RegexKind.EqualsToken;
case '!': return RegexKind.ExclamationToken;
case '<': return RegexKind.LessThanToken;
case '>': return RegexKind.GreaterThanToken;
case '-': return RegexKind.MinusToken;
case '\'': return RegexKind.SingleQuoteToken;
default: return RegexKind.TextToken;
}
}
private ImmutableArray<RegexTrivia> ScanLeadingTrivia(bool allowTrivia, RegexOptions options)
{
if (!allowTrivia)
{
return ImmutableArray<RegexTrivia>.Empty;
}
var result = ArrayBuilder<RegexTrivia>.GetInstance();
var start = Position;
while (Position < Text.Length)
{
var comment = ScanComment(options);
if (comment != null)
{
result.Add(comment.Value);
continue;
}
var whitespace = ScanWhitespace(options);
if (whitespace != null)
{
result.Add(whitespace.Value);
continue;
}
break;
}
return result.ToImmutableAndFree();
}
public RegexTrivia? ScanComment(RegexOptions options)
{
if (Position >= Text.Length)
{
return null;
}
if (HasOption(options, RegexOptions.IgnorePatternWhitespace))
{
if (Text[Position] == '#')
{
var start = Position;
// Note: \n is the only newline the native regex parser looks for.
while (Position < Text.Length &&
Text[Position] != '\n')
{
Position++;
}
return CreateTrivia(RegexKind.CommentTrivia, GetSubPatternToCurrentPos(start));
}
}
if (IsAt("(?#"))
{
var start = Position;
while (Position < Text.Length &&
Text[Position] != ')')
{
Position++;
}
if (Position == Text.Length)
{
var diagnostics = ImmutableArray.Create(new EmbeddedDiagnostic(
WorkspacesResources.Unterminated_regex_comment,
GetTextSpan(start, Position)));
return CreateTrivia(RegexKind.CommentTrivia, GetSubPatternToCurrentPos(start), diagnostics);
}
Position++;
return CreateTrivia(RegexKind.CommentTrivia, GetSubPatternToCurrentPos(start));
}
return null;
}
public TextSpan GetTextSpan(int startInclusive, int endExclusive)
=> TextSpan.FromBounds(Text[startInclusive].Span.Start, Text[endExclusive - 1].Span.End);
public bool IsAt(string val)
=> TextAt(this.Position, val);
private bool TextAt(int position, string val)
{
for (var i = 0; i < val.Length; i++)
{
if (position + i >= Text.Length ||
Text[position + i] != val[i])
{
return false;
}
}
return true;
}
private RegexTrivia? ScanWhitespace(RegexOptions options)
{
if (HasOption(options, RegexOptions.IgnorePatternWhitespace))
{
var start = Position;
while (Position < Text.Length && IsBlank(Text[Position]))
{
Position++;
}
if (Position > start)
{
return CreateTrivia(RegexKind.WhitespaceTrivia, GetSubPatternToCurrentPos(start));
}
}
return null;
}
private bool IsBlank(char ch)
{
// List taken from the native regex parser.
switch (ch)
{
case '\u0009':
case '\u000A':
case '\u000C':
case '\u000D':
case ' ':
return true;
default:
return false;
}
}
public RegexToken? TryScanEscapeCategory()
{
var start = Position;
while (Position < Text.Length &&
IsEscapeCategoryChar(this.CurrentChar))
{
Position++;
}
if (Position == start)
{
return null;
}
var token = CreateToken(RegexKind.EscapeCategoryToken, ImmutableArray<RegexTrivia>.Empty, GetSubPatternToCurrentPos(start));
var category = token.VirtualChars.CreateString();
if (!RegexCharClass.IsEscapeCategory(category))
{
token = token.AddDiagnosticIfNone(new EmbeddedDiagnostic(
string.Format(WorkspacesResources.Unknown_property_0, category),
token.GetSpan()));
}
return token;
}
private static bool IsEscapeCategoryChar(VirtualChar ch)
=> ch == '-' ||
(ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z');
public RegexToken? TryScanNumber()
{
if (Position == Text.Length)
{
return null;
}
const int MaxValueDiv10 = int.MaxValue / 10;
const int MaxValueMod10 = int.MaxValue % 10;
var value = 0;
var start = Position;
var error = false;
while (Position < Text.Length && this.CurrentChar is var ch && IsDecimalDigit(ch))
{
Position++;
unchecked
{
var charVal = ch - '0';
if (value > MaxValueDiv10 || (value == MaxValueDiv10 && charVal > MaxValueMod10))
{
error = true;
}
value *= 10;
value += charVal;
}
}
if (Position == start)
{
return null;
}
var token = CreateToken(RegexKind.NumberToken, ImmutableArray<RegexTrivia>.Empty, GetSubPatternToCurrentPos(start));
token = token.With(value: value);
if (error)
{
token = token.AddDiagnosticIfNone(new EmbeddedDiagnostic(
WorkspacesResources.Capture_group_numbers_must_be_less_than_or_equal_to_Int32_MaxValue,
token.GetSpan()));
}
return token;
}
public RegexToken? TryScanCaptureName()
{
if (Position == Text.Length)
{
return null;
}
var start = Position;
while (Position < Text.Length && RegexCharClass.IsWordChar(this.CurrentChar))
{
Position++;
}
if (Position == start)
{
return null;
}
var token = CreateToken(RegexKind.CaptureNameToken, ImmutableArray<RegexTrivia>.Empty, GetSubPatternToCurrentPos(start));
token = token.With(value: token.VirtualChars.CreateString());
return token;
}
public RegexToken? TryScanNumberOrCaptureName()
=> TryScanNumber() ?? TryScanCaptureName();
public RegexToken? TryScanOptions()
{
var start = Position;
while (Position < Text.Length && IsOptionChar(this.CurrentChar))
{
Position++;
}
return start == Position
? default(RegexToken?)
: CreateToken(RegexKind.OptionsToken, ImmutableArray<RegexTrivia>.Empty, GetSubPatternToCurrentPos(start));
}
private bool IsOptionChar(char ch)
{
switch (ch)
{
case '+': case '-':
case 'i': case 'I':
case 'm': case 'M':
case 'n': case 'N':
case 's': case 'S':
case 'x': case 'X':
return true;
default:
return false;
}
}
public RegexToken ScanHexCharacters(int count)
{
var start = Position;
var beforeSlash = start - 2;
// Make sure we're right after the \x or \u.
Debug.Assert(Text[beforeSlash].Char == '\\');
Debug.Assert(Text[beforeSlash + 1].Char == 'x' || Text[beforeSlash + 1].Char == 'u');
for (int i = 0; i < count; i++)
{
if (Position < Text.Length && IsHexChar(this.CurrentChar))
{
Position++;
}
}
var result = CreateToken(
RegexKind.TextToken, ImmutableArray<RegexTrivia>.Empty, GetSubPatternToCurrentPos(start));
var length = Position - start;
if (length != count)
{
result = result.AddDiagnosticIfNone(new EmbeddedDiagnostic(
WorkspacesResources.Insufficient_hexadecimal_digits,
TextSpan.FromBounds(Text[beforeSlash].Span.Start, Text[Position - 1].Span.End)));
}
return result;
}
public static bool IsHexChar(char ch)
=> IsDecimalDigit(ch) ||
(ch >= 'a' && ch <= 'f') ||
(ch >= 'A' && ch <= 'F');
private static bool IsDecimalDigit(char ch)
=> ch >= '0' && ch <= '9';
private static bool IsOctalDigit(char ch)
=> ch >= '0' && ch <= '7';
public RegexToken ScanOctalCharacters(RegexOptions options)
{
var start = Position;
var beforeSlash = start - 1;
// Make sure we're right after the \
// And we only should have been called if we were \octal-char
Debug.Assert(Text[beforeSlash].Char == '\\');
Debug.Assert(IsOctalDigit(Text[start].Char));
const int maxChars = 3;
int currentVal = 0;
for (int i = 0; i < maxChars; i++)
{
if (Position < Text.Length && IsOctalDigit(this.CurrentChar))
{
var octalVal = this.CurrentChar - '0';
Debug.Assert(octalVal >= 0 && octalVal <= 7);
currentVal *= 8;
currentVal += octalVal;
Position++;
// Ecmascript doesn't allow octal values above 32 (0x20 in hex). Note: we do
// *not* add a diagnostic. This is not an error situation. The .net lexer
// simply stops once it hits a value greater than a legal octal value.
if (HasOption(options, RegexOptions.ECMAScript) && currentVal >= 0x20)
{
break;
}
}
}
Debug.Assert(Position - start > 0);
var result = CreateToken(
RegexKind.TextToken, ImmutableArray<RegexTrivia>.Empty, GetSubPatternToCurrentPos(start));
return result;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.EmbeddedLanguages.Common;
namespace Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions
{
internal abstract class RegexNode : EmbeddedSyntaxNode<RegexKind, RegexNode>
{
protected RegexNode(RegexKind kind) : base(kind)
{
}
public abstract void Accept(IRegexNodeVisitor visitor);
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis.EmbeddedLanguages.Common;
using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions
{
using static EmbeddedSyntaxHelpers;
using static RegexHelpers;
using RegexToken = EmbeddedSyntaxToken<RegexKind>;
internal partial struct RegexParser
{
/// <summary>
/// Analyzes the first parsed tree to determine the set of capture numbers and names. These are
/// then used to do the second parsing pass as they can change how the regex engine interprets
/// some parts of the pattern (though not the groups themselves).
/// </summary>
private struct CaptureInfoAnalyzer
{
private readonly ImmutableArray<VirtualChar> _text;
private readonly ImmutableDictionary<int, TextSpan>.Builder _captureNumberToSpan;
private readonly ImmutableDictionary<string, TextSpan>.Builder _captureNameToSpan;
private readonly ArrayBuilder<string> _captureNames;
private int _autoNumber;
public CaptureInfoAnalyzer(ImmutableArray<VirtualChar> text)
{
_text = text;
_captureNumberToSpan = ImmutableDictionary.CreateBuilder<int, TextSpan>();
_captureNameToSpan = ImmutableDictionary.CreateBuilder<string, TextSpan>();
_captureNames = ArrayBuilder<string>.GetInstance();
_autoNumber = 1;
_captureNumberToSpan.Add(0, text.IsEmpty ? default : GetSpan(text));
}
public (ImmutableDictionary<string, TextSpan>, ImmutableDictionary<int, TextSpan>) Analyze(
RegexCompilationUnit root, RegexOptions options)
{
CollectCaptures(root, options);
AssignNumbersToCaptureNames();
_captureNames.Free();
return (_captureNameToSpan.ToImmutable(), _captureNumberToSpan.ToImmutable());
}
private void CollectCaptures(RegexNode node, RegexOptions options)
{
switch (node.Kind)
{
case RegexKind.CaptureGrouping:
var captureGrouping = (RegexCaptureGroupingNode)node;
RecordCapture(captureGrouping.CaptureToken, GetGroupingSpan(captureGrouping));
break;
case RegexKind.BalancingGrouping:
var balancingGroup = (RegexBalancingGroupingNode)node;
RecordCapture(balancingGroup.FirstCaptureToken, GetGroupingSpan(balancingGroup));
break;
case RegexKind.ConditionalExpressionGrouping:
// Explicitly recurse into conditionalGrouping.Grouping. That grouping
// itself does not create a capture group, but nested groupings inside of it
// will.
var conditionalGrouping = (RegexConditionalExpressionGroupingNode)node;
RecurseIntoChildren(conditionalGrouping.Grouping, options);
CollectCaptures(conditionalGrouping.Result, options);
return;
case RegexKind.SimpleGrouping:
RecordSimpleGroupingCapture((RegexSimpleGroupingNode)node, options);
break;
case RegexKind.NestedOptionsGrouping:
// When we see (?opts:...)
// Recurse explicitly, setting the new options as we process the inner expression.
// When this pops out we'll be back to these options we're currently at now.
var nestedOptions = (RegexNestedOptionsGroupingNode)node;
CollectCaptures(nestedOptions.Expression, GetNewOptionsFromToken(options, nestedOptions.OptionsToken));
return;
}
RecurseIntoChildren(node, options);
}
private void RecurseIntoChildren(RegexNode node, RegexOptions options)
{
foreach (var child in node)
{
if (child.IsNode)
{
// When we see a SimpleOptionsGroup ```(?opts)``` then determine what the options will
// be for successive nodes in the sequence.
var childNode = child.Node;
if (childNode is RegexSimpleOptionsGroupingNode simpleOptions)
{
options = GetNewOptionsFromToken(options, simpleOptions.OptionsToken);
}
CollectCaptures(child.Node, options);
}
}
}
private TextSpan GetGroupingSpan(RegexGroupingNode grouping)
{
Debug.Assert(!grouping.OpenParenToken.IsMissing);
var lastChar = grouping.CloseParenToken.IsMissing
? _text.Last()
: grouping.CloseParenToken.VirtualChars.Last();
return GetSpan(grouping.OpenParenToken.VirtualChars[0], lastChar);
}
private void RecordSimpleGroupingCapture(RegexSimpleGroupingNode node, RegexOptions options)
{
if (HasOption(options, RegexOptions.ExplicitCapture))
{
// Don't automatically add simple groups if the explicit capture option is on.
// Only add captures for 'CaptureGrouping' and 'BalancingGrouping' nodes.
return;
}
// Don't count a bogus (? node as a capture node. We only have this to keep our error
// messages in line with the native parser. i.e. even though the bogus (? code would
// cause an exception, we might get an earlier exception if there's a reference to
// this grouping. So if we note this grouping we'll end up not causing that error
// to happen, bringing out behavior out of sync with the native system.
var expr = node.Expression;
while (expr is RegexAlternationNode alternation)
{
expr = alternation.Left;
}
if (expr is RegexSequenceNode sequence &&
sequence.ChildCount > 0)
{
var leftMost = sequence.ChildAt(0);
if (leftMost.Node is RegexTextNode textNode &&
IsTextChar(textNode.TextToken, '?'))
{
return;
}
}
AddIfMissing(_captureNumberToSpan, list: null, _autoNumber++, GetGroupingSpan(node));
}
private void RecordCapture(RegexToken token, TextSpan span)
{
if (!token.IsMissing)
{
if (token.Kind == RegexKind.NumberToken)
{
AddIfMissing(_captureNumberToSpan, list: null, (int)token.Value, span);
}
else
{
AddIfMissing(_captureNameToSpan, list: _captureNames, (string)token.Value, span);
}
}
}
private static void AddIfMissing<T>(
ImmutableDictionary<T, TextSpan>.Builder mapping,
ArrayBuilder<T> list,
T val, TextSpan span)
{
if (!mapping.ContainsKey(val))
{
mapping.Add(val, span);
list?.Add(val);
}
}
/// <summary>
/// Give numbers to all named captures. They will get successive <see
/// cref="_autoNumber"/> values that have not already been handed out to existing
/// numbered capture groups.
/// </summary>
private void AssignNumbersToCaptureNames()
{
foreach (var name in _captureNames)
{
while (_captureNumberToSpan.ContainsKey(_autoNumber))
{
_autoNumber++;
}
_captureNumberToSpan.Add(_autoNumber, _captureNameToSpan[name]);
_autoNumber++;
}
}
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Threading;
using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions
{
/// <summary>
/// Helper class to detect regex pattern tokens in a document efficiently.
/// </summary>
internal class RegexPatternDetector
{
private const string _patternName = "pattern";
private static readonly ConditionalWeakTable<SemanticModel, RegexPatternDetector> _modelToDetector =
new ConditionalWeakTable<SemanticModel, RegexPatternDetector>();
private readonly SemanticModel _semanticModel;
private readonly ISyntaxFactsService _syntaxFacts;
private readonly ISemanticFactsService _semanticFacts;
private readonly INamedTypeSymbol _regexType;
private readonly HashSet<string> _methodNamesOfInterest;
/// <summary>
/// Helps match patterns of the form: language=regex,option1,option2,option3
///
/// All matching is case insensitive, with spaces allowed between the punctuation.
/// 'regex' or 'regexp' are both allowed. Option values will be or'ed together
/// to produce final options value. If an unknown option is encountered, processing
/// will stop with whatever value has accumulated so far.
///
/// Option names are the values from the <see cref="RegexOptions"/> enum.
/// </summary>
private static readonly Regex s_languageCommentDetector =
new Regex(@"language\s*=\s*regex(p)?((\s*,\s*)(?<option>[a-zA-Z]+))*",
RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Dictionary<string, RegexOptions> s_nameToOption =
typeof(RegexOptions).GetTypeInfo().DeclaredFields
.Where(f => f.FieldType == typeof(RegexOptions))
.ToDictionary(f => f.Name, f => (RegexOptions)f.GetValue(null), StringComparer.OrdinalIgnoreCase);
public RegexPatternDetector(
SemanticModel semanticModel,
ISyntaxFactsService syntaxFacts,
ISemanticFactsService semanticFacts,
INamedTypeSymbol regexType,
HashSet<string> methodNamesOfInterest)
{
_semanticModel = semanticModel;
_syntaxFacts = syntaxFacts;
_semanticFacts = semanticFacts;
_regexType = regexType;
_methodNamesOfInterest = methodNamesOfInterest;
}
public static RegexPatternDetector TryGetOrCreate(
SemanticModel semanticModel,
ISyntaxFactsService syntaxFacts,
ISemanticFactsService semanticFacts)
{
// Do a quick non-allocating check first.
if (_modelToDetector.TryGetValue(semanticModel, out var detector))
{
return detector;
}
return _modelToDetector.GetValue(
semanticModel, _ => TryCreate(semanticModel, syntaxFacts, semanticFacts));
}
private static RegexPatternDetector TryCreate(
SemanticModel semanticModel,
ISyntaxFactsService syntaxFacts,
ISemanticFactsService semanticFacts)
{
var regexType = semanticModel.Compilation.GetTypeByMetadataName(typeof(Regex).FullName);
if (regexType == null)
{
return null;
}
var methodNamesOfInterest = GetMethodNamesOfInterest(regexType, syntaxFacts);
return new RegexPatternDetector(
semanticModel, syntaxFacts, semanticFacts,
regexType, methodNamesOfInterest);
}
public static bool IsDefinitelyNotPattern(SyntaxToken token, ISyntaxFactsService syntaxFacts)
{
// We only support string literals passed in arguments to something.
// In the future we could support any string literal, as long as it has
// some marker (like a comment on it) stating it's a regex.
if (!syntaxFacts.IsStringLiteral(token))
{
return true;
}
if (!IsMethodOrConstructorArgument(token, syntaxFacts) &&
!HasRegexLanguageComment(token, syntaxFacts, out _))
{
return true;
}
return false;
}
private static bool HasRegexLanguageComment(
SyntaxToken token, ISyntaxFactsService syntaxFacts, out RegexOptions options)
{
if (HasRegexLanguageComment(token.GetPreviousToken().TrailingTrivia, syntaxFacts, out options))
{
return true;
}
for (var node = token.Parent; node != null; node = node.Parent)
{
if (HasRegexLanguageComment(node.GetLeadingTrivia(), syntaxFacts, out options))
{
return true;
}
}
options = default;
return false;
}
private static bool HasRegexLanguageComment(
SyntaxTriviaList list, ISyntaxFactsService syntaxFacts, out RegexOptions options)
{
foreach (var trivia in list)
{
if (HasRegexLanguageComment(trivia, syntaxFacts, out options))
{
return true;
}
}
options = default;
return false;
}
private static bool HasRegexLanguageComment(
SyntaxTrivia trivia, ISyntaxFactsService syntaxFacts, out RegexOptions options)
{
if (syntaxFacts.IsRegularComment(trivia))
{
var text = trivia.ToString();
var match = s_languageCommentDetector.Match(text);
if (match.Success)
{
options = RegexOptions.None;
var optionGroup = match.Groups["option"];
foreach (Capture capture in optionGroup.Captures)
{
if (s_nameToOption.TryGetValue(capture.Value, out var specificOption))
{
options |= specificOption;
}
else
{
break;
}
}
return true;
}
}
options = default;
return false;
}
private static bool IsMethodOrConstructorArgument(SyntaxToken token, ISyntaxFactsService syntaxFacts)
=> syntaxFacts.IsLiteralExpression(token.Parent) &&
syntaxFacts.IsArgument(token.Parent.Parent);
private static HashSet<string> GetMethodNamesOfInterest(INamedTypeSymbol regexType, ISyntaxFactsService syntaxFacts)
{
var result = syntaxFacts.IsCaseSensitive
? new HashSet<string>()
: new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var methods = from method in regexType.GetMembers().OfType<IMethodSymbol>()
where method.DeclaredAccessibility == Accessibility.Public
where method.IsStatic
where method.Parameters.Any(p => p.Name == _patternName)
select method.Name;
result.AddRange(methods);
return result;
}
public bool IsRegexPattern(SyntaxToken token, CancellationToken cancellationToken, out RegexOptions options)
{
options = default;
if (IsDefinitelyNotPattern(token, _syntaxFacts))
{
return false;
}
if (HasRegexLanguageComment(token, _syntaxFacts, out options))
{
return true;
}
var stringLiteral = token;
var literalNode = stringLiteral.Parent;
var argumentNode = literalNode.Parent;
Debug.Assert(_syntaxFacts.IsArgument(argumentNode));
var argumentList = argumentNode.Parent;
var invocationOrCreation = argumentList.Parent;
if (_syntaxFacts.IsInvocationExpression(invocationOrCreation))
{
var invokedExpression = _syntaxFacts.GetExpressionOfInvocationExpression(invocationOrCreation);
var name = GetNameOfInvokedExpression(invokedExpression);
if (_methodNamesOfInterest.Contains(name))
{
// Is a string argument to a method that looks like it could be a Regex method.
// Need to do deeper analysis
var method = _semanticModel.GetSymbolInfo(invocationOrCreation, cancellationToken).GetAnySymbol();
if (method != null &&
method.DeclaredAccessibility == Accessibility.Public &&
method.IsStatic &&
_regexType.Equals(method.ContainingType))
{
return AnalyzeStringLiteral(
stringLiteral, argumentNode, cancellationToken, out options);
}
}
}
else if (_syntaxFacts.IsObjectCreationExpression(invocationOrCreation))
{
var typeNode = _syntaxFacts.GetObjectCreationType(invocationOrCreation);
var name = GetNameOfType(typeNode, _syntaxFacts);
if (name != null)
{
if (_syntaxFacts.StringComparer.Compare(nameof(Regex), name) == 0)
{
var constructor = _semanticModel.GetSymbolInfo(invocationOrCreation, cancellationToken).GetAnySymbol();
if (_regexType.Equals(constructor?.ContainingType))
{
// Argument to "new Regex". Need to do deeper analysis
return AnalyzeStringLiteral(
stringLiteral, argumentNode, cancellationToken, out options);
}
}
}
}
return false;
}
public RegexTree TryParseRegexPattern(SyntaxToken token, IVirtualCharService virtualCharService, CancellationToken cancellationToken)
{
if (!this.IsRegexPattern(token, cancellationToken, out var options))
{
return null;
}
var chars = virtualCharService.TryConvertToVirtualChars(token);
if (chars.IsDefaultOrEmpty)
{
return null;
}
return RegexParser.TryParse(chars, options);
}
private bool AnalyzeStringLiteral(
SyntaxToken stringLiteral, SyntaxNode argumentNode,
CancellationToken cancellationToken, out RegexOptions options)
{
options = default;
var parameter = _semanticFacts.FindParameterForArgument(_semanticModel, argumentNode, cancellationToken);
if (parameter?.Name != _patternName)
{
return false;
}
options = GetRegexOptions(argumentNode, cancellationToken);
return true;
}
private RegexOptions GetRegexOptions(SyntaxNode argumentNode, CancellationToken cancellationToken)
{
var argumentList = argumentNode.Parent;
var arguments = _syntaxFacts.GetArgumentsOfArgumentList(argumentList);
foreach (var siblingArg in arguments)
{
if (siblingArg != argumentNode)
{
var expr = _syntaxFacts.GetExpressionOfArgument(siblingArg);
if (expr != null)
{
var exprType = _semanticModel.GetTypeInfo(expr, cancellationToken);
if (exprType.Type?.Name == nameof(RegexOptions))
{
var constVal = _semanticModel.GetConstantValue(expr, cancellationToken);
if (constVal.HasValue)
{
return (RegexOptions)(int)constVal.Value;
}
}
}
}
}
return RegexOptions.None;
}
private string GetNameOfType(SyntaxNode typeNode, ISyntaxFactsService syntaxFacts)
{
if (syntaxFacts.IsQualifiedName(typeNode))
{
return GetNameOfType(syntaxFacts.GetRightSideOfDot(typeNode), syntaxFacts);
}
else if (syntaxFacts.IsIdentifierName(typeNode))
{
return syntaxFacts.GetIdentifierOfSimpleName(typeNode).ValueText;
}
return null;
}
private string GetNameOfInvokedExpression(SyntaxNode invokedExpression)
{
if (_syntaxFacts.IsSimpleMemberAccessExpression(invokedExpression))
{
return _syntaxFacts.GetIdentifierOfSimpleName(_syntaxFacts.GetNameOfMemberAccessExpression(invokedExpression)).ValueText;
}
else if (_syntaxFacts.IsIdentifierName(invokedExpression))
{
return _syntaxFacts.GetIdentifierOfSimpleName(invokedExpression).ValueText;
}
return null;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.EmbeddedLanguages.Common;
using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions
{
internal sealed class RegexTree : EmbeddedSyntaxTree<RegexKind, RegexNode, RegexCompilationUnit>
{
public readonly ImmutableDictionary<string, TextSpan> CaptureNamesToSpan;
public readonly ImmutableDictionary<int, TextSpan> CaptureNumbersToSpan;
public RegexTree(
ImmutableArray<VirtualChar> text,
RegexCompilationUnit root,
ImmutableArray<EmbeddedDiagnostic> diagnostics,
ImmutableDictionary<string, TextSpan> captureNamesToSpan,
ImmutableDictionary<int, TextSpan> captureNumbersToSpan)
: base(text, root, diagnostics)
{
CaptureNamesToSpan = captureNamesToSpan;
CaptureNumbersToSpan = captureNumbersToSpan;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Composition;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Options.Providers;
namespace Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions
{
internal class RegularExpressionsOptions
{
public static PerLanguageOption<bool> ColorizeRegexPatterns =
new PerLanguageOption<bool>(
nameof(RegularExpressionsOptions),
nameof(ColorizeRegexPatterns),
defaultValue: true,
storageLocations: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ColorizeRegexPatterns"));
public static PerLanguageOption<bool> ReportInvalidRegexPatterns =
new PerLanguageOption<bool>(
nameof(RegularExpressionsOptions),
nameof(ReportInvalidRegexPatterns),
defaultValue: true,
storageLocations: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ReportInvalidRegexPatterns"));
public static PerLanguageOption<bool> HighlightRelatedRegexComponentsUnderCursor =
new PerLanguageOption<bool>(
nameof(RegularExpressionsOptions),
nameof(HighlightRelatedRegexComponentsUnderCursor),
defaultValue: true,
storageLocations: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.HighlightRelatedRegexComponentsUnderCursor"));
}
[ExportOptionProvider, Shared]
internal class RegularExpressionsOptionsProvider : IOptionProvider
{
public ImmutableArray<IOption> Options { get; } = ImmutableArray.Create<IOption>(
RegularExpressionsOptions.ColorizeRegexPatterns,
RegularExpressionsOptions.ReportInvalidRegexPatterns,
RegularExpressionsOptions.HighlightRelatedRegexComponentsUnderCursor);
}
}
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports System.Threading
Imports Microsoft.CodeAnalysis.Classification
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.EmbeddedLanguages.VirtualChars
Namespace Microsoft.CodeAnalysis.VisualBasic.Classification.Classifiers
Friend Class RegexPatternTokenClassifier
Inherits AbstractSyntaxClassifier
Public Overrides ReadOnly Property SyntaxTokenKinds As ImmutableArray(Of Integer) = ImmutableArray.Create(Of Integer)(SyntaxKind.StringLiteralToken)
Public Overrides Sub AddClassifications(workspace As Workspace, token As SyntaxToken, semanticModel As SemanticModel, result As ArrayBuilder(Of ClassifiedSpan), cancellationToken As CancellationToken)
Debug.Assert(token.Kind() = SyntaxKind.StringLiteralToken)
CommonRegexPatternTokenClassifier.AddClassifications(
workspace, token, semanticModel, result,
VisualBasicSyntaxFactsService.Instance,
VisualBasicSemanticFactsService.Instance,
VisualBasicVirtualCharService.Instance,
cancellationToken)
End Sub
End Class
End Namespace
......@@ -20,8 +20,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification
New NameSyntaxClassifier(),
New ImportAliasClauseSyntaxClassifier(),
New IdentifierNameSyntaxClassifier(),
New JsonPatternTokenClassifier(),
New RegexPatternTokenClassifier())
New JsonPatternTokenClassifier())
Public Overrides Function GetDefaultSyntaxClassifiers() As ImmutableArray(Of ISyntaxClassifier)
Return s_defaultSyntaxClassifiers
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册