diff --git a/RetailCoder.VBE/Root/RubberduckModule.cs b/RetailCoder.VBE/Root/RubberduckModule.cs index 1a0c38b542..bc6955184d 100644 --- a/RetailCoder.VBE/Root/RubberduckModule.cs +++ b/RetailCoder.VBE/Root/RubberduckModule.cs @@ -144,6 +144,7 @@ public override void Load() ConfigureFormDesignerContextMenu(); ConfigureFormDesignerControlContextMenu(); ConfigureProjectExplorerContextMenu(); + BindWindowsHooks(); } @@ -383,9 +384,9 @@ private IEnumerable GetRubberduckMenuItems() Kernel.Get(), Kernel.Get(), Kernel.Get(), - Kernel.Get(), GetUnitTestingParentMenu(), GetSmartIndenterParentMenu(), + GetToolsParentMenu(), GetRefactoringsParentMenu(), GetNavigateParentMenu(), }; @@ -427,7 +428,6 @@ private IMenuItem GetNavigateParentMenu() var items = new IMenuItem[] { Kernel.Get(), - Kernel.Get(), //Kernel.Get(), Kernel.Get(), Kernel.Get(), @@ -454,13 +454,24 @@ private IEnumerable GetCodePaneContextMenuItems() { GetRefactoringsParentMenu(), GetSmartIndenterParentMenu(), - //Kernel.Get(), Kernel.Get(), Kernel.Get(), Kernel.Get(), }; } + private IMenuItem GetToolsParentMenu() + { + var items = new IMenuItem[] + { + Kernel.Get(), + Kernel.Get(), + Kernel.Get(), + }; + + return new ToolsParentMenu(items); + } + private IEnumerable GetFormDesignerContextMenuItems() { return new IMenuItem[] diff --git a/RetailCoder.VBE/Rubberduck.csproj b/RetailCoder.VBE/Rubberduck.csproj index 681c2dddb8..2f29c09c47 100644 --- a/RetailCoder.VBE/Rubberduck.csproj +++ b/RetailCoder.VBE/Rubberduck.csproj @@ -433,6 +433,9 @@ + + + LinkButton.xaml @@ -618,7 +621,6 @@ - @@ -691,11 +693,26 @@ ExtractInterfaceDialog.cs + + RegexAssistant.xaml + + + Form + + + RegexAssistantDialog.cs + + True True RubberduckUI.de.resx + + True + True + RubberduckUI.resx + True True @@ -820,11 +837,6 @@ True True - - True - True - RubberduckUI.resx - BranchesView.xaml @@ -1031,6 +1043,9 @@ ExtractInterfaceDialog.cs + + RegexAssistantDialog.cs + PublicResXFileCodeGenerator RubberduckUI.de.Designer.cs @@ -1352,6 +1367,10 @@ {A4A618E1-CBCA-435F-9C6C-5181E030ADFC} Rubberduck.Parsing + + {40cc03e3-756c-4674-af07-384115deaee2} + Rubberduck.RegexAssistant + {e85e1253-86d6-45ee-968b-f37348d44132} Rubberduck.SettingsProvider @@ -1406,6 +1425,10 @@ Designer MSBuild:Compile + + MSBuild:Compile + Designer + Designer MSBuild:Compile diff --git a/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/NavigateParentMenu.cs b/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/NavigateParentMenu.cs index e3a45ad7c2..9c3cf7ed5d 100644 --- a/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/NavigateParentMenu.cs +++ b/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/NavigateParentMenu.cs @@ -11,4 +11,13 @@ public NavigateParentMenu(IEnumerable items) public override int DisplayOrder { get { return (int)RubberduckMenuItemDisplayOrder.Navigate; } } } + + public enum NavigationMenuItemDisplayOrder + { + CodeExplorer, + RegexSearchReplace, + FindSymbol, + FindAllReferences, + FindImplementations + } } diff --git a/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/NavigationMenuItemDisplayOrder.cs b/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/NavigationMenuItemDisplayOrder.cs deleted file mode 100644 index 9136679ca9..0000000000 --- a/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/NavigationMenuItemDisplayOrder.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Rubberduck.UI.Command.MenuItems.ParentMenus -{ - public enum NavigationMenuItemDisplayOrder - { - CodeExplorer, - ToDoExplorer, - RegexSearchReplace, - FindSymbol, - FindAllReferences, - FindImplementations - } -} diff --git a/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/RubberduckParentMenu.cs b/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/RubberduckParentMenu.cs index 63e2fa8488..d4eda856a0 100644 --- a/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/RubberduckParentMenu.cs +++ b/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/RubberduckParentMenu.cs @@ -15,9 +15,9 @@ public enum RubberduckMenuItemDisplayOrder UnitTesting, Refactorings, Navigate, + Tools, CodeInspections, - SourceControl, Settings, - About + About, } } diff --git a/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/ToolsParentMenu.cs b/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/ToolsParentMenu.cs new file mode 100644 index 0000000000..a9af5efd90 --- /dev/null +++ b/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/ToolsParentMenu.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace Rubberduck.UI.Command.MenuItems.ParentMenus +{ + public class ToolsParentMenu : ParentMenuItemBase + { + public ToolsParentMenu(IEnumerable items) + : base("ToolsMenu", items) + { + } + + public override int DisplayOrder + { + get + { + return (int)RubberduckMenuItemDisplayOrder.Tools; + } + } + + + } + + public enum ToolsMenuItemDisplayOrder + { + SourceControl, + ToDoExplorer, + RegexAssistant, + } +} diff --git a/RetailCoder.VBE/UI/Command/MenuItems/RegexAssistantCommand.cs b/RetailCoder.VBE/UI/Command/MenuItems/RegexAssistantCommand.cs new file mode 100644 index 0000000000..de488fff54 --- /dev/null +++ b/RetailCoder.VBE/UI/Command/MenuItems/RegexAssistantCommand.cs @@ -0,0 +1,25 @@ +using NLog; +using Rubberduck.UI.RegexAssistant; +using System.Runtime.InteropServices; + +namespace Rubberduck.UI.Command +{ + /// + /// A command that displays the RegexAssistantDialog + /// + [ComVisible(false)] + class RegexAssistantCommand : CommandBase + { + public RegexAssistantCommand() : base (LogManager.GetCurrentClassLogger()) + { + } + + protected override void ExecuteImpl(object parameter) + { + using (var window = new RegexAssistantDialog()) + { + window.ShowDialog(); + } + } + } +} diff --git a/RetailCoder.VBE/UI/Command/MenuItems/RegexAssistantCommandMenuItem.cs b/RetailCoder.VBE/UI/Command/MenuItems/RegexAssistantCommandMenuItem.cs new file mode 100644 index 0000000000..06ea229f6d --- /dev/null +++ b/RetailCoder.VBE/UI/Command/MenuItems/RegexAssistantCommandMenuItem.cs @@ -0,0 +1,16 @@ +using Rubberduck.UI.Command.MenuItems.ParentMenus; +using System.Windows.Input; + +namespace Rubberduck.UI.Command.MenuItems +{ + class RegexAssistantCommandMenuItem : CommandMenuItemBase + { + public RegexAssistantCommandMenuItem(CommandBase command) : base(command) + { + } + + public override string Key { get { return "ToolsMenu_RegexAssistant"; } } + + public override int DisplayOrder { get { return (int)ToolsMenuItemDisplayOrder.RegexAssistant; } } + } +} diff --git a/RetailCoder.VBE/UI/Command/MenuItems/ShowSourceControlPanelCommandMenuItem.cs b/RetailCoder.VBE/UI/Command/MenuItems/ShowSourceControlPanelCommandMenuItem.cs index 789fcb73f7..b4b327f772 100644 --- a/RetailCoder.VBE/UI/Command/MenuItems/ShowSourceControlPanelCommandMenuItem.cs +++ b/RetailCoder.VBE/UI/Command/MenuItems/ShowSourceControlPanelCommandMenuItem.cs @@ -10,7 +10,7 @@ public ShowSourceControlPanelCommandMenuItem(CommandBase command) { } - public override string Key { get { return "RubberduckMenu_SourceControl"; } } - public override int DisplayOrder { get { return (int)RubberduckMenuItemDisplayOrder.SourceControl; } } + public override string Key { get { return "ToolsMenu_SourceControl"; } } + public override int DisplayOrder { get { return (int)ToolsMenuItemDisplayOrder.SourceControl; } } } } diff --git a/RetailCoder.VBE/UI/Command/MenuItems/ToDoExplorerCommandMenuItem.cs b/RetailCoder.VBE/UI/Command/MenuItems/ToDoExplorerCommandMenuItem.cs index 980d76a5b6..c6e85efba1 100644 --- a/RetailCoder.VBE/UI/Command/MenuItems/ToDoExplorerCommandMenuItem.cs +++ b/RetailCoder.VBE/UI/Command/MenuItems/ToDoExplorerCommandMenuItem.cs @@ -1,5 +1,4 @@ -using System.Windows.Input; -using Rubberduck.UI.Command.MenuItems.ParentMenus; +using Rubberduck.UI.Command.MenuItems.ParentMenus; namespace Rubberduck.UI.Command.MenuItems { @@ -10,7 +9,7 @@ public ToDoExplorerCommandMenuItem(CommandBase command) { } - public override string Key { get { return "RubberduckMenu_TodoItems"; } } - public override int DisplayOrder { get { return (int)NavigationMenuItemDisplayOrder.ToDoExplorer; } } + public override string Key { get { return "ToolsMenu_TodoItems"; } } + public override int DisplayOrder { get { return (int)ToolsMenuItemDisplayOrder.ToDoExplorer; } } } } diff --git a/RetailCoder.VBE/UI/Command/MenuItems/ToolsParentMenu.cs b/RetailCoder.VBE/UI/Command/MenuItems/ToolsParentMenu.cs new file mode 100644 index 0000000000..a9af5efd90 --- /dev/null +++ b/RetailCoder.VBE/UI/Command/MenuItems/ToolsParentMenu.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace Rubberduck.UI.Command.MenuItems.ParentMenus +{ + public class ToolsParentMenu : ParentMenuItemBase + { + public ToolsParentMenu(IEnumerable items) + : base("ToolsMenu", items) + { + } + + public override int DisplayOrder + { + get + { + return (int)RubberduckMenuItemDisplayOrder.Tools; + } + } + + + } + + public enum ToolsMenuItemDisplayOrder + { + SourceControl, + ToDoExplorer, + RegexAssistant, + } +} diff --git a/RetailCoder.VBE/UI/Command/RegexAssistantCommand.cs b/RetailCoder.VBE/UI/Command/RegexAssistantCommand.cs new file mode 100644 index 0000000000..de488fff54 --- /dev/null +++ b/RetailCoder.VBE/UI/Command/RegexAssistantCommand.cs @@ -0,0 +1,25 @@ +using NLog; +using Rubberduck.UI.RegexAssistant; +using System.Runtime.InteropServices; + +namespace Rubberduck.UI.Command +{ + /// + /// A command that displays the RegexAssistantDialog + /// + [ComVisible(false)] + class RegexAssistantCommand : CommandBase + { + public RegexAssistantCommand() : base (LogManager.GetCurrentClassLogger()) + { + } + + protected override void ExecuteImpl(object parameter) + { + using (var window = new RegexAssistantDialog()) + { + window.ShowDialog(); + } + } + } +} diff --git a/RetailCoder.VBE/UI/RegexAssistant/RegexAssistant.xaml b/RetailCoder.VBE/UI/RegexAssistant/RegexAssistant.xaml new file mode 100644 index 0000000000..715a04a2c4 --- /dev/null +++ b/RetailCoder.VBE/UI/RegexAssistant/RegexAssistant.xaml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + diff --git a/RetailCoder.VBE/UI/RegexAssistant/RegexAssistant.xaml.cs b/RetailCoder.VBE/UI/RegexAssistant/RegexAssistant.xaml.cs new file mode 100644 index 0000000000..949a0295ad --- /dev/null +++ b/RetailCoder.VBE/UI/RegexAssistant/RegexAssistant.xaml.cs @@ -0,0 +1,10 @@ +namespace Rubberduck.UI.RegexAssistant +{ + public partial class RegexAssistant + { + public RegexAssistant() + { + InitializeComponent(); + } + } +} diff --git a/RetailCoder.VBE/UI/RegexAssistant/RegexAssistantDialog.Designer.cs b/RetailCoder.VBE/UI/RegexAssistant/RegexAssistantDialog.Designer.cs new file mode 100644 index 0000000000..48d9a509d0 --- /dev/null +++ b/RetailCoder.VBE/UI/RegexAssistant/RegexAssistantDialog.Designer.cs @@ -0,0 +1,61 @@ +namespace Rubberduck.UI.RegexAssistant +{ + partial class RegexAssistantDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.ElementHost = new System.Windows.Forms.Integration.ElementHost(); + this.RegexAssistant = new Rubberduck.UI.RegexAssistant.RegexAssistant(); + this.SuspendLayout(); + // + // AssistantControl + // + this.ElementHost.Dock = System.Windows.Forms.DockStyle.Fill; + this.ElementHost.ImeMode = System.Windows.Forms.ImeMode.NoControl; + this.ElementHost.Location = new System.Drawing.Point(0, 0); + this.ElementHost.Name = "AssistantControl"; + this.ElementHost.Size = new System.Drawing.Size(509, 544); + this.ElementHost.TabIndex = 0; + this.ElementHost.Child = this.RegexAssistant; + // + // RegexAssistantDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(509, 544); + this.Controls.Add(this.ElementHost); + this.Name = "RegexAssistantDialog"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Integration.ElementHost ElementHost; + private RegexAssistant RegexAssistant; + } +} \ No newline at end of file diff --git a/RetailCoder.VBE/UI/RegexAssistant/RegexAssistantDialog.cs b/RetailCoder.VBE/UI/RegexAssistant/RegexAssistantDialog.cs new file mode 100644 index 0000000000..c2fd215ce9 --- /dev/null +++ b/RetailCoder.VBE/UI/RegexAssistant/RegexAssistantDialog.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Rubberduck.UI.RegexAssistant +{ + public partial class RegexAssistantDialog : Form + { + public RegexAssistantDialog() + { + InitializeComponent(); + ViewModel = new RegexAssistantViewModel(); + } + + private RegexAssistantViewModel _viewModel; + private RegexAssistantViewModel ViewModel { get { return _viewModel; } + set + { + _viewModel = value; + + RegexAssistant.DataContext = _viewModel; + } + } + } +} diff --git a/RetailCoder.VBE/UI/RegexAssistant/RegexAssistantDialog.resx b/RetailCoder.VBE/UI/RegexAssistant/RegexAssistantDialog.resx new file mode 100644 index 0000000000..1af7de150c --- /dev/null +++ b/RetailCoder.VBE/UI/RegexAssistant/RegexAssistantDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/RetailCoder.VBE/UI/RegexAssistant/RegexAssistantViewModel.cs b/RetailCoder.VBE/UI/RegexAssistant/RegexAssistantViewModel.cs new file mode 100644 index 0000000000..1bf2d187e6 --- /dev/null +++ b/RetailCoder.VBE/UI/RegexAssistant/RegexAssistantViewModel.cs @@ -0,0 +1,168 @@ +using Rubberduck.RegexAssistant; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Controls; + +namespace Rubberduck.UI.RegexAssistant +{ + public class RegexAssistantViewModel : ViewModelBase + { + public RegexAssistantViewModel() + { + _pattern = string.Empty; + RecalculateDescription(); + } + + public bool GlobalFlag { + get + { + return _globalFlag; + } + set + { + _globalFlag = value; + RecalculateDescription(); + } + } + public bool IgnoreCaseFlag + { + get + { + return _ignoreCaseFlag; + } + set + { + _ignoreCaseFlag = value; + RecalculateDescription(); + } + } + public string Pattern + { + get + { + return _pattern; + } + set + { + _pattern = value; + RecalculateDescription(); + } + } + + private string _description; + private bool _globalFlag; + private bool _ignoreCaseFlag; + private string _pattern; + + private List _resultItems; + public List ResultItems + { + get + { + return _resultItems; + } + set + { + _resultItems = value; + OnPropertyChanged(); + } + } + + private void RecalculateDescription() + { + if (_pattern.Equals(string.Empty)) + { + _description = RubberduckUI.RegexAssistant_NoPatternError; + var results = new List(); + var rootTreeItem = new TreeViewItem(); + rootTreeItem.Header = _description; + results.Add(rootTreeItem); + ResultItems = results; + return; + } + ResultItems = ToTreeViewItems(new Pattern(_pattern, _ignoreCaseFlag, _globalFlag)); + } + + private List ToTreeViewItems(Pattern pattern) + { + var resultItems = new List(); + if (pattern.IgnoreCase) + { + resultItems.Add(TreeViewItemFromHeader(pattern.CasingDescription)); + } + if (pattern.AnchoredAtStart) + { + resultItems.Add(TreeViewItemFromHeader(pattern.StartAnchorDescription)); + } + resultItems.Add(AsTreeViewItem((dynamic)pattern.RootExpression)); + if (pattern.AnchoredAtEnd) + { + resultItems.Add(TreeViewItemFromHeader(pattern.EndAnchorDescription)); + } + return resultItems; + } + + private TreeViewItem TreeViewItemFromHeader(string header) + { + var result = new TreeViewItem(); + result.Header = header; + return result; + } + + public string DescriptionResults + { + get + { + return _description; + } + } + + private static TreeViewItem AsTreeViewItem(IRegularExpression expression) + { + throw new InvalidOperationException("Some unknown IRegularExpression subtype was in RegexAssistantViewModel"); + } + + private static TreeViewItem AsTreeViewItem(ErrorExpression expression) + { + var result = new TreeViewItem(); + result.Header = expression.Description; + return result; + } + + private static TreeViewItem AsTreeViewItem(ConcatenatedExpression expression) + { + var result = new TreeViewItem(); + result.Header = expression.Description; + foreach (var subtree in expression.Subexpressions.Select(exp => AsTreeViewItem((dynamic)exp))) + { + result.Items.Add(subtree); + } + return result; + } + + private static TreeViewItem AsTreeViewItem(AlternativesExpression expression) + { + var result = new TreeViewItem(); + result.Header = expression.Description; + foreach (var subtree in expression.Subexpressions.Select(exp => AsTreeViewItem((dynamic)exp))) + { + result.Items.Add(subtree); + } + return result; + } + + private static TreeViewItem AsTreeViewItem(SingleAtomExpression expression) + { + var result = new TreeViewItem(); + result.Header = expression.Description; + // no other Atom has Subexpressions we care about + if (expression.Atom.GetType() == typeof(Group)) + { + result.Items.Add(AsTreeViewItem((dynamic)((expression.Atom) as Group).Subexpression)); + } + + return result; + } + } +} diff --git a/RetailCoder.VBE/UI/RubberduckUI.Designer.cs b/RetailCoder.VBE/UI/RubberduckUI.Designer.cs index 761f2cf79f..ea9f9f2dc4 100644 --- a/RetailCoder.VBE/UI/RubberduckUI.Designer.cs +++ b/RetailCoder.VBE/UI/RubberduckUI.Designer.cs @@ -2430,6 +2430,60 @@ public static string Refresh { } } + /// + /// Looks up a localized string similar to Regex Helper. + /// + public static string RegexAssistant_Caption { + get { + return ResourceManager.GetString("RegexAssistant_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to What the RegExp does:. + /// + public static string RegexAssistant_DescriptionResultsLabel { + get { + return ResourceManager.GetString("RegexAssistant_DescriptionResultsLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The flag "Global" is set. + /// + public static string RegexAssistant_GlobalFlag { + get { + return ResourceManager.GetString("RegexAssistant_GlobalFlag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The flag "IgnoreCase" is set. + /// + public static string RegexAssistant_IgnoreCaseFlag { + get { + return ResourceManager.GetString("RegexAssistant_IgnoreCaseFlag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No Pattern given. + /// + public static string RegexAssistant_NoPatternError { + get { + return ResourceManager.GetString("RegexAssistant_NoPatternError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The RegExp:. + /// + public static string RegexAssistant_RegexPatternLabel { + get { + return ResourceManager.GetString("RegexAssistant_RegexPatternLabel", resourceCulture); + } + } + /// /// Looks up a localized string similar to Regex Search & Replace. /// @@ -2890,24 +2944,6 @@ public static string RubberduckMenu_Settings { } } - /// - /// Looks up a localized string similar to &Source Control. - /// - public static string RubberduckMenu_SourceControl { - get { - return ResourceManager.GetString("RubberduckMenu_SourceControl", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to To&Do Items. - /// - public static string RubberduckMenu_TodoItems { - get { - return ResourceManager.GetString("RubberduckMenu_TodoItems", resourceCulture); - } - } - /// /// Looks up a localized string similar to Unit &Tests. /// @@ -4609,6 +4645,42 @@ public static string TodoSettings_TokenListLabel { } } + /// + /// Looks up a localized string similar to T&ools. + /// + public static string ToolsMenu { + get { + return ResourceManager.GetString("ToolsMenu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Regex &Assistant. + /// + public static string ToolsMenu_RegexAssistant { + get { + return ResourceManager.GetString("ToolsMenu_RegexAssistant", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Source Control. + /// + public static string ToolsMenu_SourceControl { + get { + return ResourceManager.GetString("ToolsMenu_SourceControl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to To&Do Items. + /// + public static string ToolsMenu_TodoItems { + get { + return ResourceManager.GetString("ToolsMenu_TodoItems", resourceCulture); + } + } + /// /// Looks up a localized string similar to Type. /// diff --git a/RetailCoder.VBE/UI/RubberduckUI.de.resx b/RetailCoder.VBE/UI/RubberduckUI.de.resx index 7b925db713..09552f496d 100644 --- a/RetailCoder.VBE/UI/RubberduckUI.de.resx +++ b/RetailCoder.VBE/UI/RubberduckUI.de.resx @@ -453,7 +453,7 @@ Warnung: Alle eigenen Einstellungen gehen dabei verloren. Die Originaldatei wird Test Explorer - Todo Explorer + ToDo Liste Warnung diff --git a/RetailCoder.VBE/UI/RubberduckUI.resx b/RetailCoder.VBE/UI/RubberduckUI.resx index dc8898ae0c..8273849538 100644 --- a/RetailCoder.VBE/UI/RubberduckUI.resx +++ b/RetailCoder.VBE/UI/RubberduckUI.resx @@ -338,10 +338,10 @@ &Refactor - + &Source Control - + To&Do Items @@ -1733,4 +1733,28 @@ All our stargazers, likers & followers, for the warm fuzzies Source Control + + Regex Helper + + + What the RegExp does: + + + The flag "Global" is set + + + The flag "IgnoreCase" is set + + + The RegExp: + + + Regex &Assistant + + + T&ools + + + No Pattern given + \ No newline at end of file diff --git a/Rubberduck.RegexAssistant/Atom.cs b/Rubberduck.RegexAssistant/Atom.cs new file mode 100644 index 0000000000..3ac61fb40e --- /dev/null +++ b/Rubberduck.RegexAssistant/Atom.cs @@ -0,0 +1,263 @@ +using Rubberduck.RegexAssistant.i18n; +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace Rubberduck.RegexAssistant +{ + public interface IAtom : IDescribable + { + string Specifier { get; } + } + + internal class CharacterClass : IAtom + { + public static readonly string Pattern = @"(?.*?)(? _characterSpecifiers; + public IList CharacterSpecifiers { get { return _characterSpecifiers; } } + private readonly string _specifier; + + public CharacterClass(string specifier) + { + Match m = Matcher.Match(specifier); + if (!m.Success) + { + throw new ArgumentException("The given specifier does not denote a character class"); + } + this._specifier = specifier; + string actualSpecifier = m.Groups["expression"].Value; + _inverseMatching = actualSpecifier.StartsWith("^"); + _characterSpecifiers= ExtractCharacterSpecifiers(InverseMatching ? actualSpecifier.Substring(1) : actualSpecifier); + } + + public string Specifier + { + get + { + return _specifier; + } + } + + private static readonly Regex CharacterRanges = new Regex(@"(\\[dDwWsS]|(\\[ntfvr]|\\([0-7]{3}|x[\dA-F]{2}|u[\dA-F]{4}|[\\\.\[\]])|.)(-(\\[ntfvr]|\\([0-7]{3}|x[A-F]{2}|u[\dA-F]{4}|[\.\\\[\]])|.))?)"); + private IList ExtractCharacterSpecifiers(string characterClass) + { + MatchCollection specifiers = CharacterRanges.Matches(characterClass); + var result = new List(); + + foreach (Match specifier in specifiers) + { + if (specifier.Value.Contains("\\")) + { + if (specifier.Value.EndsWith("-\\")) + { + throw new ArgumentException("Character Ranges that have incorrectly escaped characters as target are not allowed"); + } + else if (specifier.Value.Length == 1) + { + // Something's bork with the Pattern. For now we skip this it shouldn't affect anyone + continue; + } + } + result.Add(specifier.Value); + } + return result; + } + + public string Description + { + get + { + return string.Format(InverseMatching + ? AssistantResources.AtomDescription_CharacterClass_Inverted + : AssistantResources.AtomDescription_CharacterClass + , HumanReadableClass()); + } + } + + private string HumanReadableClass() + { + return string.Join(", ", CharacterSpecifiers); // join last with and? + } + + public override bool Equals(object obj) + { + if (obj is CharacterClass) + { + return (obj as CharacterClass)._specifier.Equals(_specifier); + } + return false; + } + + public override int GetHashCode() + { + return _specifier.GetHashCode(); + } + } + + public class Group : IAtom + { + public static readonly string Pattern = @"(?.*(?\\(u[\dA-F]{4}|x[\dA-F]{2}|[0-7]{3}|[bB\(\){}\\\[\]\.+*?1-9nftvrdDwWsS])|[^()\[\]{}\\*+?^$])"; + private static readonly Regex Matcher = new Regex("^" + Pattern + "$"); + private static readonly ISet EscapeLiterals = new HashSet(); + private readonly string _specifier; + + static Literal() { + foreach (char escape in new char[]{ '.', '+', '*', '?', '(', ')', '{', '}', '[', ']', '|', '\\' }) + { + EscapeLiterals.Add(escape); + } + _escapeDescriptions.Add('d', AssistantResources.AtomDescription_Digit); + _escapeDescriptions.Add('D', AssistantResources.AtomDescription_NonDigit); + _escapeDescriptions.Add('b', AssistantResources.AtomDescription_WordBoundary); + _escapeDescriptions.Add('B', AssistantResources.AtomDescription_NonWordBoundary); + _escapeDescriptions.Add('w', AssistantResources.AtomDescription_WordCharacter); + _escapeDescriptions.Add('W', AssistantResources.AtomDescription_NonWordCharacter); + _escapeDescriptions.Add('s', AssistantResources.AtomDescription_Whitespace); + _escapeDescriptions.Add('S', AssistantResources.AtomDescription_NonWhitespace); + _escapeDescriptions.Add('n', AssistantResources.AtomDescription_Newline); + _escapeDescriptions.Add('r', AssistantResources.AtomDescription_CarriageReturn); + _escapeDescriptions.Add('f', AssistantResources.AtomDescription_FormFeed); + _escapeDescriptions.Add('v', AssistantResources.AtomDescription_VTab); + _escapeDescriptions.Add('t', AssistantResources.AtomDescription_HTab); + } + + public Literal(string specifier) + { + Match m = Matcher.Match(specifier); + if (!m.Success) + { + throw new ArgumentException("The given specifier does not denote a Literal"); + } + _specifier = specifier; + } + + public string Specifier + { + get + { + return _specifier; + } + } + + + private static readonly Dictionary _escapeDescriptions = new Dictionary(); + public string Description + { + get + { + // here be dragons! + // keep track of: + // - escaped chars + // - escape sequences (each having a different description) + // - codepoint escapes (belongs into above category but kept separate) + // - and actually boring literal matches + if (_specifier.Length > 1) + { + string relevant = _specifier.Substring(1); // skip the damn Backslash at the start + if (relevant.Length > 1) // longer sequences + { + if (relevant.StartsWith("u")) + { + return string.Format(AssistantResources.AtomDescription_Literal_UnicodePoint, relevant.Substring(1)); //skip u + } + else if (relevant.StartsWith("x")) + { + return string.Format(AssistantResources.AtomDescription_Literal_HexCodepoint, relevant.Substring(1)); // skip x + } + else + { + return string.Format(AssistantResources.AtomDescription_Literal_OctalCodepoint, relevant); // no format specifier to skip + } + } + else if (EscapeLiterals.Contains(relevant[0])) + { + return string.Format(AssistantResources.AtomDescription_Literal_EscapedLiteral, relevant); + } + else if (char.IsDigit(relevant[0])) + { + return string.Format(AssistantResources.AtomDescription_Literal_Backreference, relevant); + } + else + { + return _escapeDescriptions[relevant[0]]; + } + } + if (_specifier.Equals(".")) + { + return AssistantResources.AtomDescription_Dot; + } + return string.Format(AssistantResources.AtomDescription_Literal_ActualLiteral, _specifier); + + } + } + + public override bool Equals(object obj) + { + if (obj is Literal) + { + return (obj as Literal)._specifier.Equals(_specifier); + } + return false; + } + + public override int GetHashCode() + { + return _specifier.GetHashCode(); + } + } +} diff --git a/Rubberduck.RegexAssistant/IDescribable.cs b/Rubberduck.RegexAssistant/IDescribable.cs new file mode 100644 index 0000000000..c6dc828cf1 --- /dev/null +++ b/Rubberduck.RegexAssistant/IDescribable.cs @@ -0,0 +1,7 @@ +namespace Rubberduck.RegexAssistant +{ + public interface IDescribable + { + string Description { get; } + } +} \ No newline at end of file diff --git a/Rubberduck.RegexAssistant/IRegularExpression.cs b/Rubberduck.RegexAssistant/IRegularExpression.cs new file mode 100644 index 0000000000..426086597e --- /dev/null +++ b/Rubberduck.RegexAssistant/IRegularExpression.cs @@ -0,0 +1,300 @@ +using Rubberduck.RegexAssistant.Extensions; +using Rubberduck.RegexAssistant.i18n; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace Rubberduck.RegexAssistant +{ + public interface IRegularExpression : IDescribable + { + Quantifier Quantifier { get; } + IList Subexpressions { get; } + } + + public class ConcatenatedExpression : IRegularExpression + { + private readonly Quantifier _quantifier; + private readonly IList _subexpressions; + + public ConcatenatedExpression(IList subexpressions) + { + _subexpressions = subexpressions; + _quantifier = new Quantifier(string.Empty); // these are always exactly once. Quantifying happens through groups + } + + public string Description + { + get + { + return AssistantResources.ExpressionDescription_ConcatenatedExpression; + } + } + + public Quantifier Quantifier + { + get + { + return _quantifier; + } + } + + public IList Subexpressions + { + get + { + return _subexpressions; + } + } + } + + public class AlternativesExpression : IRegularExpression + { + private readonly Quantifier _quantifier; + private readonly IList _subexpressions; + + public AlternativesExpression(IList subexpressions) + { + _subexpressions = subexpressions; + _quantifier = new Quantifier(string.Empty); // these are always exactly once. Quantifying happens through groups + } + + public string Description + { + get + { + return AssistantResources.ExpressionDescription_AlternativesExpression; + } + } + + public Quantifier Quantifier + { + get + { + return _quantifier; + } + } + + public IList Subexpressions + { + get + { + return _subexpressions; + } + } + } + + public class SingleAtomExpression : IRegularExpression + { + public readonly IAtom Atom; + private readonly Quantifier _quantifier; + + public SingleAtomExpression(IAtom atom, Quantifier quantifier) + { + Atom = atom; + _quantifier = quantifier; + } + + public string Description + { + get + { + return string.Format("{0} {1}.", Atom.Description, Quantifier.HumanReadable()); + } + } + + public Quantifier Quantifier + { + get + { + return _quantifier; + } + } + + public IList Subexpressions + { + get + { + return new List(Enumerable.Empty()); + } + } + + public override bool Equals(object obj) + { + if (obj is SingleAtomExpression) + { + SingleAtomExpression other = obj as SingleAtomExpression; + return other.Atom.Equals(Atom) && other.Quantifier.Equals(Quantifier); + } + return false; + } + + public override int GetHashCode() + { + return Atom.GetHashCode() ^ Quantifier.GetHashCode(); + } + + } + + public class ErrorExpression : IRegularExpression + { + private readonly string _errorToken; + + public ErrorExpression(string errorToken) + { + _errorToken = errorToken; + } + + public string Description + { + get + { + return string.Format(AssistantResources.ExpressionDescription_ErrorExpression, _errorToken); + } + } + + public Quantifier Quantifier + { + get + { + return new Quantifier(string.Empty); + } + } + + public IList Subexpressions + { + get + { + return new List(); + } + } + } + + internal static class RegularExpression + { + + /// + /// We basically run a Chain of Responsibility here. At first we try to parse the whole specifier as one Atom. + /// If this fails, we assume it's a ConcatenatedExpression and proceed to create one of these. + /// That works well until we encounter a non-escaped '|' outside of a CharacterClass. Then we know that we actually have an AlternativesExpression. + /// This means we have to check what we got back and add it to a List of subexpressions to the AlternativesExpression. + /// We then proceed to the next alternative (ParseIntoConcatenatedExpression consumes the tokens it uses) and keep adding to our subexpressions. + /// + /// Note that Atoms (or more specifically Groups) can request a Parse of their subexpressions. + /// Also note that TryParseAtom is responsible for grabbing an Atom and it's Quantifier. + /// If there is no Quantifier following (either because the input is exhausted or there directly is the next atom) then we instead pair with `new Quantifier("")` + /// + /// The full Regular Expression specifier to Parse + /// An IRegularExpression that encompasses the complete given specifier + public static IRegularExpression Parse(string specifier) + { + IRegularExpression expression; + // ByRef requires us to hack around here, because TryParseAsAtom doesn't fail when it doesn't consume the specifier anymore + string specifierCopy = specifier; + if (TryParseAsAtom(ref specifierCopy, out expression) && specifierCopy.Length == 0) + { + return expression; + } + List subexpressions = new List(); + while (specifier.Length != 0) + { + expression = ParseIntoConcatenatedExpression(ref specifier); + // ! actually an AlternativesExpression + if (specifier.Length != 0 || subexpressions.Count != 0) + { + // flatten hierarchy + var parsedSubexpressions = (expression as ConcatenatedExpression).Subexpressions; + if (parsedSubexpressions.Count == 1) + { + expression = parsedSubexpressions[0]; + } + subexpressions.Add(expression); + } + } + return (subexpressions.Count == 0) ? expression : new AlternativesExpression(subexpressions); + } + /// + /// Successively parses the complete specifer into Atoms and returns a ConcatenatedExpression after the specifier has been exhausted or a single '|' is encountered at the start of the remaining specifier. + /// Note: this may fail to work if the last encountered token cannot be parsed into an Atom, but the remaining specifier has nonzero lenght + /// + /// The specifier to Parse into a concatenated expression + /// The ConcatenatedExpression resulting from parsing the given specifier, either completely or up to the first encountered '|' + private static IRegularExpression ParseIntoConcatenatedExpression(ref string specifier) + { + List subexpressions = new List(); + string currentSpecifier = specifier; + int oldSpecifierLength = currentSpecifier.Length + 1; + while (currentSpecifier.Length > 0 && currentSpecifier.Length < oldSpecifierLength) + { + oldSpecifierLength = currentSpecifier.Length; + IRegularExpression expression; + // we actually have an AlternativesExpression, return the current status to Parse after updating the specifier + if (currentSpecifier[0].Equals('|')) + { + specifier = currentSpecifier.Substring(1); // skip leading | + return new ConcatenatedExpression(subexpressions); + } + if (TryParseAsAtom(ref currentSpecifier, out expression)) + { + subexpressions.Add(expression); + } + else if (currentSpecifier.Length == oldSpecifierLength) + { + subexpressions.Add(new ErrorExpression(currentSpecifier.Substring(0, 1))); + currentSpecifier = currentSpecifier.Substring(1); + } + } + specifier = ""; // we've exhausted the specifier, tell Parse about it to prevent infinite looping + return new ConcatenatedExpression(subexpressions); + } + + private static readonly Regex groupWithQuantifier = new Regex("^" + Group.Pattern + Quantifier.Pattern + "?"); + private static readonly Regex characterClassWithQuantifier = new Regex("^" + CharacterClass.Pattern + Quantifier.Pattern + "?"); + private static readonly Regex literalWithQuantifier = new Regex("^" + Literal.Pattern + Quantifier.Pattern + "?"); + /// + /// Tries to parse the given specifier into an Atom. For that all categories of Atoms are checked in the following order: + /// 1. Group + /// 2. Class + /// 3. Literal + /// When it succeeds, the given expression will be assigned a SingleAtomExpression containing the Atom and it's Quantifier. + /// The parsed atom will be removed from the specifier and the method returns true. To check whether the complete specifier was an Atom, + /// one needs to examine the specifier after calling this method. If it was, the specifier is empty after calling. + /// + /// The specifier to extract the leading Atom out of. Will be shortened if an Atom was successfully extracted + /// The resulting SingleAtomExpression + /// True, if an Atom could be extracted, false otherwise + // Note: could be rewritten to not consume the specifier and instead return an integer specifying the consumed length of specifier. This would remove the by-ref passed string hack + internal static bool TryParseAsAtom(ref string specifier, out IRegularExpression expression) + { + Match m = groupWithQuantifier.Match(specifier); + if (m.Success) + { + string atom = m.Groups["expression"].Value; + string quantifier = m.Groups["quantifier"].Value; + specifier = specifier.Substring(atom.Length + 2 + quantifier.Length); + expression = new SingleAtomExpression(new Group("("+atom+")"), new Quantifier(quantifier)); + return true; + } + m = characterClassWithQuantifier.Match(specifier); + if (m.Success) + { + string atom = m.Groups["expression"].Value; + string quantifier = m.Groups["quantifier"].Value; + specifier = specifier.Substring(atom.Length + 2 + quantifier.Length); + expression = new SingleAtomExpression(new CharacterClass("["+atom+"]"), new Quantifier(quantifier)); + return true; + } + m = literalWithQuantifier.Match(specifier); + if (m.Success) + { + string atom = m.Groups["expression"].Value; + string quantifier = m.Groups["quantifier"].Value; + specifier = specifier.Substring(atom.Length + quantifier.Length); + expression = new SingleAtomExpression(new Literal(atom), new Quantifier(quantifier)); + return true; + } + expression = null; + return false; + } + } +} \ No newline at end of file diff --git a/Rubberduck.RegexAssistant/Pattern.cs b/Rubberduck.RegexAssistant/Pattern.cs new file mode 100644 index 0000000000..bd877d1057 --- /dev/null +++ b/Rubberduck.RegexAssistant/Pattern.cs @@ -0,0 +1,93 @@ +using Rubberduck.RegexAssistant.i18n; +using System; + +namespace Rubberduck.RegexAssistant +{ + public class Pattern : IDescribable + { + public IRegularExpression RootExpression; + MatcherFlags Flags; + + private readonly bool _hasStartAnchor; + private readonly bool _hasEndAnchor; + private readonly string _description; + + public string Description + { + get + { + return _description; + } + } + + public Pattern(string expression, bool ignoreCase, bool global) + { + Flags = ignoreCase ? MatcherFlags.IgnoreCase : 0; + Flags = global ? Flags | MatcherFlags.Global : Flags; + + _hasEndAnchor = expression[expression.Length - 1].Equals('$'); + _hasStartAnchor = expression[0].Equals('^'); + + int start = _hasStartAnchor ? 1 : 0; + int end = (_hasEndAnchor ? 1 : 0) + start; + RootExpression = RegularExpression.Parse(expression.Substring(start, expression.Length - end)); + _description = AssembleDescription(); + } + + private string AssembleDescription() + { + string result = string.Empty; + result += CasingDescription; + result += StartAnchorDescription; + result += RootExpression.Description; + result += EndAnchorDescription; + return result; + } + + public string StartAnchorDescription + { + get + { + if (AnchoredAtStart) + { + return Flags.HasFlag(MatcherFlags.Global) + ? AssistantResources.PatternDescription_AnchorStart_GlobalEnabled + : AssistantResources.PatternDescription_AnchorStart; + } + return string.Empty; + } + } + + public string EndAnchorDescription + { + get + { + if (AnchoredAtEnd) + { + return Flags.HasFlag(MatcherFlags.Global) + ? AssistantResources.PatternDescription_AnchorEnd_GlobalEnabled + : AssistantResources.PatternDescription_AnchorEnd; + } + return string.Empty; + } + } + + public bool IgnoreCase { get { return Flags.HasFlag(MatcherFlags.IgnoreCase); } } + public bool Global { get { return Flags.HasFlag(MatcherFlags.Global); } } + public bool AnchoredAtStart { get { return _hasStartAnchor; } } + public bool AnchoredAtEnd { get { return _hasEndAnchor; } } + + public string CasingDescription { get + { + return IgnoreCase ? AssistantResources.PatternDescription_IgnoreCase : string.Empty; + } + } + } + + [Flags] + enum MatcherFlags + { + IgnoreCase = 1 << 0, + Global = 1 << 1, + } +} diff --git a/Rubberduck.RegexAssistant/Properties/AssemblyInfo.cs b/Rubberduck.RegexAssistant/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..32022c89d5 --- /dev/null +++ b/Rubberduck.RegexAssistant/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Rubberduck.RegexAssistant")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Rubberduck.RegexAssistant")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("40cc03e3-756c-4674-af07-384115deaee2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Rubberduck.RegexAssistant/Quantifier.cs b/Rubberduck.RegexAssistant/Quantifier.cs new file mode 100644 index 0000000000..6ba85a23d3 --- /dev/null +++ b/Rubberduck.RegexAssistant/Quantifier.cs @@ -0,0 +1,109 @@ +using System; +using System.Text.RegularExpressions; + +namespace Rubberduck.RegexAssistant +{ + public class Quantifier + { + public static readonly string Pattern = @"(?(?\d+)(?,\d*)?\}$"); + + public readonly QuantifierKind Kind; + public readonly int MinimumMatches; + public readonly int MaximumMatches; + + public Quantifier(string expression) + { + if (expression.Length == 0) + { + Kind = QuantifierKind.None; + MaximumMatches = 1; + MinimumMatches = 1; + } + else if (expression.Length > 1) + { + Kind = QuantifierKind.Expression; + Match m = Matcher.Match(expression); + if (!m.Success) + { + throw new ArgumentException(string.Format("Cannot extract a Quantifier from the expression {1}", expression)); + } + int minimum; + // shouldn't ever happen + if (!int.TryParse(m.Groups["min"].Value, out minimum)) + { + throw new ArgumentException("Cannot Parse Quantifier Expression into Range"); + } + MinimumMatches = minimum; + + string maximumString = m.Groups["max"].Value; // drop the comma + if (maximumString.Length > 1) + { + int maximum; + // shouldn't ever happen + if (!int.TryParse(maximumString.Substring(1), out maximum)) + { + throw new ArgumentException("Cannot Parse Quantifier Expression into Range"); + } + MaximumMatches = maximum; + } + else if (maximumString.Length == 1) // got a comma, so we're unbounded + { + MaximumMatches = int.MaxValue; + } + else // exact match, because no comma + { + MaximumMatches = minimum; + } + } + else + { + switch (expression.ToCharArray()[0]) + { + case '*': + MinimumMatches = 0; + MaximumMatches = int.MaxValue; + Kind = QuantifierKind.Wildcard; + break; + case '+': + MinimumMatches = 1; + MaximumMatches = int.MaxValue; + Kind = QuantifierKind.Wildcard; + break; + case '?': + MinimumMatches = 0; + MaximumMatches = 1; + Kind = QuantifierKind.Wildcard; + break; + default: + throw new ArgumentException("Passed Quantifier String was not an allowed Quantifier"); + } + } + } + + public override bool Equals(object obj) + { + if (obj is Quantifier) + { + var other = obj as Quantifier; + return other.Kind == Kind && other.MinimumMatches == MinimumMatches && other.MaximumMatches == MaximumMatches; + } + return false; + } + + public override int GetHashCode() + { + return MinimumMatches ^ MaximumMatches ^ Kind.GetHashCode(); + } + + public override string ToString() + { + return string.Format("Quantifier[{0}: {1} to {2}", Kind, MinimumMatches, MaximumMatches); + } + } + + public enum QuantifierKind + { + None, Expression, Wildcard + } +} diff --git a/Rubberduck.RegexAssistant/QuantifierExtensions.cs b/Rubberduck.RegexAssistant/QuantifierExtensions.cs new file mode 100644 index 0000000000..c3b9918686 --- /dev/null +++ b/Rubberduck.RegexAssistant/QuantifierExtensions.cs @@ -0,0 +1,39 @@ +using Rubberduck.RegexAssistant.i18n; + +namespace Rubberduck.RegexAssistant.Extensions +{ + static class QuantifierExtensions + { + public static string HumanReadable(this Quantifier quant) + { + switch (quant.Kind) + { + case QuantifierKind.None: + return AssistantResources.Quantifier_None; + case QuantifierKind.Wildcard: + if (quant.MaximumMatches == 1) + { + return AssistantResources.Quantifier_Optional; + } + if (quant.MinimumMatches == 0) + { + return AssistantResources.Quantifier_Asterisk; + } + return AssistantResources.Quantifer_Plus; + case QuantifierKind.Expression: + if (quant.MaximumMatches == quant.MinimumMatches) + { + return string.Format(AssistantResources.Quantifier_Exact, quant.MinimumMatches); + } + if (quant.MaximumMatches == int.MaxValue) + { + return string.Format(AssistantResources.Quantifier_OpenRange, quant.MinimumMatches); + } + return string.Format(AssistantResources.Quantifier_ClosedRange, quant.MinimumMatches, quant.MaximumMatches); + } + return ""; + } + } +} + + \ No newline at end of file diff --git a/Rubberduck.RegexAssistant/Rubberduck.RegexAssistant.csproj b/Rubberduck.RegexAssistant/Rubberduck.RegexAssistant.csproj new file mode 100644 index 0000000000..6106c03ba0 --- /dev/null +++ b/Rubberduck.RegexAssistant/Rubberduck.RegexAssistant.csproj @@ -0,0 +1,79 @@ + + + + + Debug + AnyCPU + {40CC03E3-756C-4674-AF07-384115DEAEE2} + Library + Properties + Rubberduck.RegexAssistant + Rubberduck.RegexAssistant + v4.5 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + True + True + AssistantResources.resx + + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + AssistantResources.Designer.cs + + + + + \ No newline at end of file diff --git a/Rubberduck.RegexAssistant/Tests/CharacterClassTests.cs b/Rubberduck.RegexAssistant/Tests/CharacterClassTests.cs new file mode 100644 index 0000000000..9d48872181 --- /dev/null +++ b/Rubberduck.RegexAssistant/Tests/CharacterClassTests.cs @@ -0,0 +1,205 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rubberduck.RegexAssistant; +using System; +using System.Collections.Generic; + +namespace RegexAssistantTests +{ + [TestClass] + public class CharacterClassTests + { + [TestMethod] + public void InvertedCharacterClass() + { + CharacterClass cut = new CharacterClass("[^ ]"); + Assert.IsTrue(cut.InverseMatching); + List expectedSpecifiers = new List(); + expectedSpecifiers.Add(" "); + + Assert.AreEqual(expectedSpecifiers.Count, cut.CharacterSpecifiers.Count); + for (int i = 0; i < expectedSpecifiers.Count; i++) + { + Assert.AreEqual(expectedSpecifiers[i], cut.CharacterSpecifiers[i]); + } + } + + [TestMethod] + public void SimpleCharacterRange() + { + CharacterClass cut = new CharacterClass("[a-z]"); + Assert.IsFalse(cut.InverseMatching); + List expectedSpecifiers = new List(); + expectedSpecifiers.Add("a-z"); + + Assert.AreEqual(expectedSpecifiers.Count, cut.CharacterSpecifiers.Count); + for (int i = 0; i < expectedSpecifiers.Count; i++) + { + Assert.AreEqual(expectedSpecifiers[i], cut.CharacterSpecifiers[i]); + } + } + + [TestMethod] + public void UnicodeCharacterRange() + { + CharacterClass cut = new CharacterClass(@"[\u00A2-\uFFFF]"); + Assert.IsFalse(cut.InverseMatching); + List expectedSpecifiers = new List(); + expectedSpecifiers.Add(@"\u00A2-\uFFFF"); + + Assert.AreEqual(expectedSpecifiers.Count, cut.CharacterSpecifiers.Count); + for (int i = 0; i < expectedSpecifiers.Count; i++) + { + Assert.AreEqual(expectedSpecifiers[i], cut.CharacterSpecifiers[i]); + } + } + + [TestMethod] + public void OctalCharacterRange() + { + CharacterClass cut = new CharacterClass(@"[\011-\777]"); + Assert.IsFalse(cut.InverseMatching); + List expectedSpecifiers = new List(); + expectedSpecifiers.Add(@"\011-\777"); + + Assert.AreEqual(expectedSpecifiers.Count, cut.CharacterSpecifiers.Count); + for (int i = 0; i < expectedSpecifiers.Count; i++) + { + Assert.AreEqual(expectedSpecifiers[i], cut.CharacterSpecifiers[i]); + } + } + + [TestMethod] + public void HexadecimalCharacterRange() + { + CharacterClass cut = new CharacterClass(@"[\x00-\xFF]"); + Assert.IsFalse(cut.InverseMatching); + List expectedSpecifiers = new List(); + expectedSpecifiers.Add(@"\x00-\xFF"); + + Assert.AreEqual(expectedSpecifiers.Count, cut.CharacterSpecifiers.Count); + for (int i = 0; i < expectedSpecifiers.Count; i++) + { + Assert.AreEqual(expectedSpecifiers[i], cut.CharacterSpecifiers[i]); + } + } + + [TestMethod] + public void MixedCharacterRanges() + { + CharacterClass cut = new CharacterClass(@"[\x00-\777\u001A-Z]"); + Assert.IsFalse(cut.InverseMatching); + List expectedSpecifiers = new List(); + expectedSpecifiers.Add(@"\x00-\777"); + expectedSpecifiers.Add(@"\u001A-Z"); + + Assert.AreEqual(expectedSpecifiers.Count, cut.CharacterSpecifiers.Count); + for (int i = 0; i < expectedSpecifiers.Count; i++) + { + Assert.AreEqual(expectedSpecifiers[i], cut.CharacterSpecifiers[i]); + } + } + + [TestMethod] + public void RangeFailureWithCharacterClass() + { + foreach (string charClass in new string[]{ @"\D", @"\d", @"\s", @"\S", @"\w", @"\W" }){ + CharacterClass cut = new CharacterClass(string.Format("[{0}-F]", charClass)); + Assert.IsFalse(cut.InverseMatching); + List expectedSpecifiers = new List(); + expectedSpecifiers.Add(charClass); + expectedSpecifiers.Add(@"-"); + expectedSpecifiers.Add(@"F"); + + Assert.AreEqual(expectedSpecifiers.Count, cut.CharacterSpecifiers.Count); + for (int i = 0; i < expectedSpecifiers.Count; i++) + { + Assert.AreEqual(expectedSpecifiers[i], cut.CharacterSpecifiers[i]); + } + } + } + + [TestMethod] + public void EscapedLiteralRanges() + { + foreach (string escapedLiteral in new string[] { @"\.", @"\[", @"\]" }) + { + CharacterClass cut = new CharacterClass(string.Format("[{0}-F]", escapedLiteral)); + Assert.IsFalse(cut.InverseMatching); + List expectedSpecifiers = new List(); + expectedSpecifiers.Add(string.Format("{0}-F",escapedLiteral)); + + Assert.AreEqual(expectedSpecifiers.Count, cut.CharacterSpecifiers.Count); + for (int i = 0; i < expectedSpecifiers.Count; i++) + { + Assert.AreEqual(expectedSpecifiers[i], cut.CharacterSpecifiers[i]); + } + // invert this + cut = new CharacterClass(string.Format("[F-{0}]", escapedLiteral)); + Assert.IsFalse(cut.InverseMatching); + expectedSpecifiers.Clear(); + expectedSpecifiers.Add(string.Format("F-{0}", escapedLiteral)); + + Assert.AreEqual(expectedSpecifiers.Count, cut.CharacterSpecifiers.Count); + for (int i = 0; i < expectedSpecifiers.Count; i++) + { + Assert.AreEqual(expectedSpecifiers[i], cut.CharacterSpecifiers[i]); + } + } + } + + [TestMethod] + public void SkipsIncorrectlyEscapedLiterals() + { + foreach (string escapedLiteral in new string[] { @"\(", @"\)", @"\{", @"\}", @"\|", @"\?", @"\*" }) + { + CharacterClass cut = new CharacterClass(string.Format("[{0}-F]", escapedLiteral)); + Assert.IsFalse(cut.InverseMatching); + List expectedSpecifiers = new List(); + expectedSpecifiers.Add(string.Format("{0}-F", escapedLiteral.Substring(1))); + + Assert.AreEqual(expectedSpecifiers.Count, cut.CharacterSpecifiers.Count); + for (int i = 0; i < expectedSpecifiers.Count; i++) + { + Assert.AreEqual(expectedSpecifiers[i], cut.CharacterSpecifiers[i]); + } + // inverted doesn't need to behave the same, because VBA blows up for ranges like R-\( + + } + } + + [TestMethod] + public void IncorrectlyEscapedRangeTargetLiteralsBlowUp() + { + foreach (string escapedLiteral in new string[] { @"\(", @"\)", @"\{", @"\}", @"\|", @"\?", @"\*" }) + { + try + { + CharacterClass cut = new CharacterClass(string.Format("[F-{0}]", escapedLiteral)); + } +#pragma warning disable CS0168 // Variable is declared but never used + catch (ArgumentException ex) +#pragma warning restore CS0168 // Variable is declared but never used + { + continue; + } + Assert.Fail("Incorrectly allowed character range with {0} as target", escapedLiteral); + } + + } + + [TestMethod] + public void IgnoresBackreferenceSpecifiers() + { + CharacterClass cut = new CharacterClass(@"[\1]"); + Assert.IsFalse(cut.InverseMatching); + + List expectedSpecifiers = new List(); + expectedSpecifiers.Add("1"); + Assert.AreEqual(expectedSpecifiers.Count, cut.CharacterSpecifiers.Count); + for (int i = 0; i < expectedSpecifiers.Count; i++) + { + Assert.AreEqual(expectedSpecifiers[i], cut.CharacterSpecifiers[i]); + } + } + } +} diff --git a/Rubberduck.RegexAssistant/Tests/LiteralTests.cs b/Rubberduck.RegexAssistant/Tests/LiteralTests.cs new file mode 100644 index 0000000000..18ea78a95e --- /dev/null +++ b/Rubberduck.RegexAssistant/Tests/LiteralTests.cs @@ -0,0 +1,99 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rubberduck.RegexAssistant; +using System; +using System.Linq; + +namespace RegexAssistantTests +{ + [TestClass] + public class LiteralTests + { + [TestMethod] + public void EscapedLiteralTests() + { + char[] literals = new char[] { '(', ')', '{', '}', '[', ']', '.', '?', '+', '*' }; + foreach (char literal in literals) + { + Literal cut = new Literal("\\" + literal); + Assert.AreEqual("\\" + literal, cut.Specifier); + } + } + + [TestMethod] + public void EscapeSequences() + { + char[] escapes = "sSwWbBdDrnvtf123456789".ToCharArray(); + foreach (char escape in escapes) + { + Literal cut = new Literal("\\" + escape); + Assert.AreEqual("\\" + escape, cut.Specifier); + } + } + + [TestMethod] + public void CodePoints() + { + string[] codePoints = { @"\uFFFF", @"\u0000", @"\xFF", @"\x00", @"\777", @"\000" }; + foreach (string codePoint in codePoints) + { + Literal cut = new Literal(codePoint); + Assert.AreEqual(codePoint, cut.Specifier); + } + } + + [TestMethod] + public void SimpleLiterals() + { + char[] literals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"§%&/=ß#'°".ToCharArray(); + foreach (char literal in literals) + { + Literal cut = new Literal("" + literal); + Assert.AreEqual("" + literal, cut.Specifier); + } + } + + [TestMethod] + public void EverythingElseBlowsUp() + { + char[] allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"§%&/=ß#'°".ToCharArray(); + string[] allowedEscapes = { "(", ")", "{", "}", "[", "]", ".", "?", "+", "*", "$", "^", "uFFFF", "u0000", "xFF", "x00", "777", "000" }; + foreach (string blowup in allowedEscapes.Select(e => "\\"+ e).Concat(allowed.Select(c => ""+c))) + { + try + { + Literal cut = new Literal("a"+blowup); + } +#pragma warning disable CS0168 // Variable is declared but never used + catch (ArgumentException ex) +#pragma warning restore CS0168 // Variable is declared but never used + { + Assert.IsTrue(true); // Assert.Pass(); + continue; + } + Assert.Fail("Did not blow up when trying to parse {0} as literal", blowup); + } + } + + [TestMethod] + public void SingleEscapedCharsAreNotParsedAsLiteral() + { + string[] escapedChars = "(){}[]\\*?+$^".ToCharArray().Select(e => e.ToString()).ToArray(); + foreach (var escape in escapedChars) + { + try + { + Literal cut = new Literal(escape); + } +#pragma warning disable CS0168 // Variable is declared but never used + catch (ArgumentException ex) +#pragma warning restore CS0168 // Variable is declared but never used + { + Assert.IsTrue(true); // Assert.Pass(); + continue; + } + Assert.Fail("Did not blow up when trying to parse {0} as literal", escape); + } + + } + } +} diff --git a/Rubberduck.RegexAssistant/Tests/QuantifierTests.cs b/Rubberduck.RegexAssistant/Tests/QuantifierTests.cs new file mode 100644 index 0000000000..d7cab4a0a0 --- /dev/null +++ b/Rubberduck.RegexAssistant/Tests/QuantifierTests.cs @@ -0,0 +1,64 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rubberduck.RegexAssistant; + +namespace RegexAssistantTests +{ + [TestClass] + public class QuantifierTests + { + [TestMethod] + public void AsteriskQuantifier() + { + Quantifier cut = new Quantifier("*"); + Assert.AreEqual(int.MaxValue, cut.MaximumMatches); + Assert.AreEqual(0, cut.MinimumMatches); + Assert.AreEqual(QuantifierKind.Wildcard, cut.Kind); + } + + [TestMethod] + public void QuestionMarkQuantifier() + { + Quantifier cut = new Quantifier("?"); + Assert.AreEqual(1, cut.MaximumMatches); + Assert.AreEqual(0, cut.MinimumMatches); + Assert.AreEqual(QuantifierKind.Wildcard, cut.Kind); + } + + [TestMethod] + public void PlusQuantifier() + { + Quantifier cut = new Quantifier("+"); + Assert.AreEqual(int.MaxValue, cut.MaximumMatches); + Assert.AreEqual(1, cut.MinimumMatches); + Assert.AreEqual(QuantifierKind.Wildcard, cut.Kind); + } + + [TestMethod] + public void ExactQuantifier() + { + Quantifier cut = new Quantifier("{5}"); + Assert.AreEqual(5, cut.MaximumMatches); + Assert.AreEqual(5, cut.MinimumMatches); + Assert.AreEqual(QuantifierKind.Expression, cut.Kind); + } + + [TestMethod] + public void FullRangeQuantifier() + { + Quantifier cut = new Quantifier("{2,5}"); + Assert.AreEqual(2, cut.MinimumMatches); + Assert.AreEqual(5, cut.MaximumMatches); + Assert.AreEqual(QuantifierKind.Expression, cut.Kind); + } + + [TestMethod] + public void OpenRangeQuantifier() + { + Quantifier cut = new Quantifier("{3,}"); + Assert.AreEqual(3, cut.MinimumMatches); + Assert.AreEqual(int.MaxValue, cut.MaximumMatches); + Assert.AreEqual(QuantifierKind.Expression, cut.Kind); + + } + } +} diff --git a/Rubberduck.RegexAssistant/Tests/RegularExpressionTests.cs b/Rubberduck.RegexAssistant/Tests/RegularExpressionTests.cs new file mode 100644 index 0000000000..ba111e2e1e --- /dev/null +++ b/Rubberduck.RegexAssistant/Tests/RegularExpressionTests.cs @@ -0,0 +1,185 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.RegexAssistant.Tests +{ + [TestClass] + public class RegularExpressionTests + { + [TestMethod] + public void ParseSingleLiteralGroupAsAtomWorks() + { + IRegularExpression expression; + string pattern = "(g){2,4}"; + RegularExpression.TryParseAsAtom(ref pattern, out expression); + Assert.IsInstanceOfType(expression, typeof(SingleAtomExpression)); + Assert.AreEqual(new Quantifier("{2,4}"), expression.Quantifier); + Assert.AreEqual(new Group("(g)"), (expression as SingleAtomExpression).Atom); + } + + [TestMethod] + public void ParseCharacterClassAsAtomWorks() + { + IRegularExpression expression; + string pattern = "[abcd]*"; + RegularExpression.TryParseAsAtom(ref pattern, out expression); + Assert.IsInstanceOfType(expression, typeof(SingleAtomExpression)); + Assert.AreEqual(new Quantifier("*"), expression.Quantifier); + Assert.AreEqual(new CharacterClass("[abcd]"), (expression as SingleAtomExpression).Atom); + } + + [TestMethod] + public void ParseLiteralAsAtomWorks() + { + IRegularExpression expression; + string pattern = "a"; + RegularExpression.TryParseAsAtom(ref pattern, out expression); + Assert.IsInstanceOfType(expression, typeof(SingleAtomExpression)); + Assert.AreEqual(new Quantifier(""), expression.Quantifier); + Assert.AreEqual(new Literal("a"), (expression as SingleAtomExpression).Atom); + } + + [TestMethod] + public void ParseUnicodeEscapeAsAtomWorks() + { + IRegularExpression expression; + string pattern = "\\u1234+"; + RegularExpression.TryParseAsAtom(ref pattern, out expression); + Assert.IsInstanceOfType(expression, typeof(SingleAtomExpression)); + Assert.AreEqual(new Quantifier("+"), expression.Quantifier); + Assert.AreEqual(new Literal("\\u1234"), (expression as SingleAtomExpression).Atom); + } + + [TestMethod] + public void ParseHexEscapeSequenceAsAtomWorks() + { + IRegularExpression expression; + string pattern = "\\x12?"; + RegularExpression.TryParseAsAtom(ref pattern, out expression); + Assert.IsInstanceOfType(expression, typeof(SingleAtomExpression)); + Assert.AreEqual(new Quantifier("?"), expression.Quantifier); + Assert.AreEqual(new Literal("\\x12"), (expression as SingleAtomExpression).Atom); + } + + [TestMethod] + public void ParseOctalEscapeSequenceAsAtomWorks() + { + IRegularExpression expression; + string pattern = "\\712{2}"; + RegularExpression.TryParseAsAtom(ref pattern, out expression); + Assert.IsInstanceOfType(expression, typeof(SingleAtomExpression)); + Assert.AreEqual(new Quantifier("{2}"), expression.Quantifier); + Assert.AreEqual(new Literal("\\712"), (expression as SingleAtomExpression).Atom); + } + + [TestMethod] + public void ParseEscapedLiteralAsAtomWorks() + { + IRegularExpression expression; + string pattern = "\\)"; + RegularExpression.TryParseAsAtom(ref pattern, out expression); + Assert.IsInstanceOfType(expression, typeof(SingleAtomExpression)); + Assert.AreEqual(new Quantifier(""), expression.Quantifier); + Assert.AreEqual(new Literal("\\)"), (expression as SingleAtomExpression).Atom); + } + + [TestMethod] + public void ParseUnescapedSpecialCharAsAtomFails() + { + foreach (string paren in "()[]{}*?+".ToCharArray().Select(c => "" + c)) + { + IRegularExpression expression; + string hack = paren; + Assert.IsFalse(RegularExpression.TryParseAsAtom(ref hack, out expression)); + Assert.IsNull(expression); + } + } + + [TestMethod] + public void ParseSimpleLiteralConcatenationAsConcatenatedExpression() + { + List expected = new List(); + expected.Add(new SingleAtomExpression(new Literal("a"), new Quantifier(""))); + expected.Add(new SingleAtomExpression(new Literal("b"), new Quantifier(""))); + + IRegularExpression expression = RegularExpression.Parse("ab"); + Assert.IsInstanceOfType(expression, typeof(ConcatenatedExpression)); + var subexpressions = (expression as ConcatenatedExpression).Subexpressions; + Assert.AreEqual(expected.Count, subexpressions.Count); + for (int i = 0; i < expected.Count; i++) + { + Assert.AreEqual(expected[i], subexpressions[i]); + } + } + + [TestMethod] + public void ParseSimplisticGroupConcatenationAsConcatenatedExpression() + { + List expected = new List(); + expected.Add(new SingleAtomExpression(new Literal("a"), new Quantifier(""))); + expected.Add(new SingleAtomExpression(new Group("(abc)"), new Quantifier("{1,4}"))); + expected.Add(new SingleAtomExpression(new Literal("b"), new Quantifier(""))); + + IRegularExpression expression = RegularExpression.Parse("a(abc){1,4}b"); + Assert.IsInstanceOfType(expression, typeof(ConcatenatedExpression)); + var subexpressions = (expression as ConcatenatedExpression).Subexpressions; + Assert.AreEqual(expected.Count, subexpressions.Count); + for (int i = 0; i < expected.Count; i++) + { + Assert.AreEqual(expected[i], subexpressions[i]); + } + } + + [TestMethod] + public void ParseSimplisticCharacterClassConcatenationAsConcatenatedExpression() + { + List expected = new List(); + expected.Add(new SingleAtomExpression(new Literal("a"), new Quantifier(""))); + expected.Add(new SingleAtomExpression(new CharacterClass("[abc]"), new Quantifier("*"))); + expected.Add(new SingleAtomExpression(new Literal("b"), new Quantifier(""))); + + IRegularExpression expression = RegularExpression.Parse("a[abc]*b"); + Assert.IsInstanceOfType(expression, typeof(ConcatenatedExpression)); + var subexpressions = (expression as ConcatenatedExpression).Subexpressions; + Assert.AreEqual(expected.Count, subexpressions.Count); + for (int i = 0; i < expected.Count; i++) + { + Assert.AreEqual(expected[i], subexpressions[i]); + } + } + + [TestMethod] + public void ParseSimplisticAlternativesExpression() + { + List expected = new List(); + expected.Add(new SingleAtomExpression(new Literal("a"), new Quantifier(""))); + expected.Add(new SingleAtomExpression(new Literal("b"), new Quantifier(""))); + + IRegularExpression expression = RegularExpression.Parse("a|b"); + Assert.IsInstanceOfType(expression, typeof(AlternativesExpression)); + var subexpressions = (expression as AlternativesExpression).Subexpressions; + Assert.AreEqual(expected.Count, subexpressions.Count); + for (int i = 0; i < expected.Count; i++) + { + Assert.AreEqual(expected[i], subexpressions[i]); + } + } + + [TestMethod] + public void CharacterClassIsNotAnAlternativesExpression() + { + IRegularExpression expression = RegularExpression.Parse("[a|b]"); + Assert.IsInstanceOfType(expression, typeof(SingleAtomExpression)); + Assert.AreEqual(new CharacterClass("[a|b]"), (expression as SingleAtomExpression).Atom); + } + + [TestMethod] + public void GroupIsNotAnAlternativesExpression() + { + IRegularExpression expression = RegularExpression.Parse("(a|b)"); + Assert.IsInstanceOfType(expression, typeof(SingleAtomExpression)); + Assert.AreEqual(new Group("(a|b)"), (expression as SingleAtomExpression).Atom); + } + } +} diff --git a/Rubberduck.RegexAssistant/i18n/AssistantResources.Designer.cs b/Rubberduck.RegexAssistant/i18n/AssistantResources.Designer.cs new file mode 100644 index 0000000000..8575fafca7 --- /dev/null +++ b/Rubberduck.RegexAssistant/i18n/AssistantResources.Designer.cs @@ -0,0 +1,405 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Rubberduck.RegexAssistant.i18n { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class AssistantResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal AssistantResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Rubberduck.RegexAssistant.i18n.AssistantResources", typeof(AssistantResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Matches the ASCII character CR. + /// + internal static string AtomDescription_CarriageReturn { + get { + return ResourceManager.GetString("AtomDescription_CarriageReturn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches any of the following characters {0}. + /// + internal static string AtomDescription_CharacterClass { + get { + return ResourceManager.GetString("AtomDescription_CharacterClass", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches any character that is not one of {0}. + /// + internal static string AtomDescription_CharacterClass_Inverted { + get { + return ResourceManager.GetString("AtomDescription_CharacterClass_Inverted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches any digit. Equivalent to "[0-9]". + /// + internal static string AtomDescription_Digit { + get { + return ResourceManager.GetString("AtomDescription_Digit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches anything. + /// + internal static string AtomDescription_Dot { + get { + return ResourceManager.GetString("AtomDescription_Dot", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches the "Form-Feed"-character. + /// + internal static string AtomDescription_FormFeed { + get { + return ResourceManager.GetString("AtomDescription_FormFeed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches the group {0} as follows. + /// + internal static string AtomDescription_Group { + get { + return ResourceManager.GetString("AtomDescription_Group", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches the horizontal "Tab"-character. + /// + internal static string AtomDescription_HTab { + get { + return ResourceManager.GetString("AtomDescription_HTab", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Literally matches {0}. + /// + internal static string AtomDescription_Literal_ActualLiteral { + get { + return ResourceManager.GetString("AtomDescription_Literal_ActualLiteral", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Exactly matches what the capture group at position {0} matched again. + /// + internal static string AtomDescription_Literal_Backreference { + get { + return ResourceManager.GetString("AtomDescription_Literal_Backreference", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches the escaped literal {0}. + /// + internal static string AtomDescription_Literal_EscapedLiteral { + get { + return ResourceManager.GetString("AtomDescription_Literal_EscapedLiteral", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches the Hexadecimal ASCII value {0}. + /// + internal static string AtomDescription_Literal_HexCodepoint { + get { + return ResourceManager.GetString("AtomDescription_Literal_HexCodepoint", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches the Octal ASCII value {0}. + /// + internal static string AtomDescription_Literal_OctalCodepoint { + get { + return ResourceManager.GetString("AtomDescription_Literal_OctalCodepoint", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches the Unicode Codepoint {0}. + /// + internal static string AtomDescription_Literal_UnicodePoint { + get { + return ResourceManager.GetString("AtomDescription_Literal_UnicodePoint", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches the ASCII character LF, also known as newline. + /// + internal static string AtomDescription_Newline { + get { + return ResourceManager.GetString("AtomDescription_Newline", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches non-digits. Equivalent to "[^\d]". + /// + internal static string AtomDescription_NonDigit { + get { + return ResourceManager.GetString("AtomDescription_NonDigit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches non-whitespace characters. Equivalent to "[^\s]". + /// + internal static string AtomDescription_NonWhitespace { + get { + return ResourceManager.GetString("AtomDescription_NonWhitespace", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ensures that the current position is at a "non-word-boundary". + /// + internal static string AtomDescription_NonWordBoundary { + get { + return ResourceManager.GetString("AtomDescription_NonWordBoundary", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches "non-word characters". Equivalent to "[^\w]". + /// + internal static string AtomDescription_NonWordCharacter { + get { + return ResourceManager.GetString("AtomDescription_NonWordCharacter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches the vertical "Tab"-character. + /// + internal static string AtomDescription_VTab { + get { + return ResourceManager.GetString("AtomDescription_VTab", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches any whitespace character. Equivalent to "[ \t\r\n\v\f]".. + /// + internal static string AtomDescription_Whitespace { + get { + return ResourceManager.GetString("AtomDescription_Whitespace", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ensures that the current position is at a "word boundary". + /// + internal static string AtomDescription_WordBoundary { + get { + return ResourceManager.GetString("AtomDescription_WordBoundary", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches any "word character". Equivalent to "[a-zA-Z_0-9]". + /// + internal static string AtomDescription_WordCharacter { + get { + return ResourceManager.GetString("AtomDescription_WordCharacter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches one of the following alternatives:. + /// + internal static string ExpressionDescription_AlternativesExpression { + get { + return ResourceManager.GetString("ExpressionDescription_AlternativesExpression", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matches the exact sequence given as follows:. + /// + internal static string ExpressionDescription_ConcatenatedExpression { + get { + return ResourceManager.GetString("ExpressionDescription_ConcatenatedExpression", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not parse {0} as literal. Check your input, please. + /// + internal static string ExpressionDescription_ErrorExpression { + get { + return ResourceManager.GetString("ExpressionDescription_ErrorExpression", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to $ ensures all characters of the string are consumed. + /// + internal static string PatternDescription_AnchorEnd { + get { + return ResourceManager.GetString("PatternDescription_AnchorEnd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to $ ensures that the line ended or all characters of the input have been consumed. + /// + internal static string PatternDescription_AnchorEnd_GlobalEnabled { + get { + return ResourceManager.GetString("PatternDescription_AnchorEnd_GlobalEnabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ^ ensures we are at the beginning of the string that's to be matched. + /// + internal static string PatternDescription_AnchorStart { + get { + return ResourceManager.GetString("PatternDescription_AnchorStart", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ^ ensures that the matcher starts at the beginning of a line. + /// + internal static string PatternDescription_AnchorStart_GlobalEnabled { + get { + return ResourceManager.GetString("PatternDescription_AnchorStart_GlobalEnabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All matches ignore the case (upper or lower) of literals. + /// + internal static string PatternDescription_IgnoreCase { + get { + return ResourceManager.GetString("PatternDescription_IgnoreCase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to at least once. + /// + internal static string Quantifer_Plus { + get { + return ResourceManager.GetString("Quantifer_Plus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to any number of times, including never. + /// + internal static string Quantifier_Asterisk { + get { + return ResourceManager.GetString("Quantifier_Asterisk", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to between {0} and {1} times. + /// + internal static string Quantifier_ClosedRange { + get { + return ResourceManager.GetString("Quantifier_ClosedRange", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to exactly {0} times. + /// + internal static string Quantifier_Exact { + get { + return ResourceManager.GetString("Quantifier_Exact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to exactly once. + /// + internal static string Quantifier_None { + get { + return ResourceManager.GetString("Quantifier_None", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to at least {0} times. + /// + internal static string Quantifier_OpenRange { + get { + return ResourceManager.GetString("Quantifier_OpenRange", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to either once or never. + /// + internal static string Quantifier_Optional { + get { + return ResourceManager.GetString("Quantifier_Optional", resourceCulture); + } + } + } +} diff --git a/Rubberduck.RegexAssistant/i18n/AssistantResources.resx b/Rubberduck.RegexAssistant/i18n/AssistantResources.resx new file mode 100644 index 0000000000..5d3beae5b0 --- /dev/null +++ b/Rubberduck.RegexAssistant/i18n/AssistantResources.resx @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Matches the ASCII character CR + + + Matches any of the following characters {0} + + + Matches any character that is not one of {0} + + + Matches any digit. Equivalent to "[0-9]" + + + Matches anything + + + Matches the "Form-Feed"-character + + + Matches the group {0} as follows + + + Matches the horizontal "Tab"-character + + + Literally matches {0} + + + Exactly matches what the capture group at position {0} matched again + + + Matches the escaped literal {0} + + + Matches the Hexadecimal ASCII value {0} + + + Matches the Octal ASCII value {0} + + + Matches the Unicode Codepoint {0} + + + Matches the ASCII character LF, also known as newline + + + Matches non-digits. Equivalent to "[^\d]" + + + Matches non-whitespace characters. Equivalent to "[^\s]" + + + Ensures that the current position is at a "non-word-boundary" + + + Matches "non-word characters". Equivalent to "[^\w]" + + + Matches the vertical "Tab"-character + + + Matches any whitespace character. Equivalent to "[ \t\r\n\v\f]". + + + Ensures that the current position is at a "word boundary" + + + Matches any "word character". Equivalent to "[a-zA-Z_0-9]" + + + Matches one of the following alternatives: + + + Matches the exact sequence given as follows: + + + Could not parse {0} as literal. Check your input, please + + + $ ensures all characters of the string are consumed + + + $ ensures that the line ended or all characters of the input have been consumed + + + ^ ensures we are at the beginning of the string that's to be matched + + + ^ ensures that the matcher starts at the beginning of a line + + + All matches ignore the case (upper or lower) of literals + + + at least once + + + any number of times, including never + + + between {0} and {1} times + + + exactly {0} times + + + exactly once + + + at least {0} times + + + either once or never + + \ No newline at end of file diff --git a/Rubberduck.sln b/Rubberduck.sln index 76231b3dcd..d34c83563e 100644 --- a/Rubberduck.sln +++ b/Rubberduck.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rubberduck", "RetailCoder.VBE\Rubberduck.csproj", "{20589DE8-432E-4359-9232-69EB070B7185}" ProjectSection(ProjectDependencies) = postProject @@ -38,9 +38,17 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rubberduck.SmartIndenter", "Rubberduck.SmartIndenter\Rubberduck.SmartIndenter.csproj", "{B9C0BF22-4D8A-4BF4-89F9-E789C0063DEB}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RubberduckTests", "RubberduckTests\RubberduckTests.csproj", "{ADADE971-75E3-40C4-8C19-AB7409372F2E}" + ProjectSection(ProjectDependencies) = postProject + {40CC03E3-756C-4674-AF07-384115DEAEE2} = {40CC03E3-756C-4674-AF07-384115DEAEE2} + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rubberduck.SettingsProvider", "Rubberduck.SettingsProvider\Rubberduck.SettingsProvider.csproj", "{E85E1253-86D6-45EE-968B-F37348D44132}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rubberduck.RegexAssistant", "Rubberduck.RegexAssistant\Rubberduck.RegexAssistant.csproj", "{40CC03E3-756C-4674-AF07-384115DEAEE2}" + ProjectSection(ProjectDependencies) = postProject + {E85E1253-86D6-45EE-968B-F37348D44132} = {E85E1253-86D6-45EE-968B-F37348D44132} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -209,6 +217,36 @@ Global {E85E1253-86D6-45EE-968B-F37348D44132}.Release64|Any CPU.Build.0 = Release|Any CPU {E85E1253-86D6-45EE-968B-F37348D44132}.Release64|x64.ActiveCfg = Release|Any CPU {E85E1253-86D6-45EE-968B-F37348D44132}.Release64|x86.ActiveCfg = Release|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Debug|x64.ActiveCfg = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Debug|x64.Build.0 = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Debug|x86.ActiveCfg = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Debug|x86.Build.0 = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Debug64|Any CPU.ActiveCfg = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Debug64|Any CPU.Build.0 = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Debug64|x64.ActiveCfg = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Debug64|x64.Build.0 = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Debug64|x86.ActiveCfg = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Debug64|x86.Build.0 = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.DebugAccess|Any CPU.ActiveCfg = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.DebugAccess|Any CPU.Build.0 = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.DebugAccess|x64.ActiveCfg = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.DebugAccess|x64.Build.0 = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.DebugAccess|x86.ActiveCfg = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.DebugAccess|x86.Build.0 = Debug|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Release|Any CPU.Build.0 = Release|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Release|x64.ActiveCfg = Release|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Release|x64.Build.0 = Release|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Release|x86.ActiveCfg = Release|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Release|x86.Build.0 = Release|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Release64|Any CPU.ActiveCfg = Release|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Release64|Any CPU.Build.0 = Release|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Release64|x64.ActiveCfg = Release|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Release64|x64.Build.0 = Release|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Release64|x86.ActiveCfg = Release|Any CPU + {40CC03E3-756C-4674-AF07-384115DEAEE2}.Release64|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE