Skip to content

Commit

Permalink
Merge pull request #5044 from IvenBach/Issue2776_RegexAssistantWhites…
Browse files Browse the repository at this point in the history
…pace

Fully spell out whitespace characters in RegexAssistant description
  • Loading branch information
retailcoder committed Aug 21, 2019
2 parents 10a0572 + cbc7c79 commit 8531cfb
Show file tree
Hide file tree
Showing 15 changed files with 277 additions and 90 deletions.
6 changes: 5 additions & 1 deletion Rubberduck.Core/UI/RegexAssistant/RegexAssistant.xaml
Expand Up @@ -79,11 +79,15 @@
<CheckBox Content="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=RegexAssistant_IgnoreCaseFlag}"
IsChecked="{Binding IgnoreCaseFlag}"
Margin="0,0,5,0"/>
<CheckBox Content="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=RegexAssistant_EncloseWhitespaceFlag}"
IsChecked="{Binding SpellOutWhiteSpace}"
Margin="0,0,5,0" />

</StackPanel>
</StackPanel>
</GroupBox>

<GroupBox Grid.Row="1" Margin="5">
<GroupBox Grid.Row="1" Margin="0,5">
<GroupBox.Header>
<Label Content="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=RegexAssistant_DescriptionResultsLabel}" FontWeight="SemiBold" />
</GroupBox.Header>
Expand Down
43 changes: 27 additions & 16 deletions Rubberduck.Core/UI/RegexAssistant/RegexAssistantViewModel.cs
Expand Up @@ -47,10 +47,21 @@ public string Pattern
}
}

private bool _spellOutWhiteSpace;
public bool SpellOutWhiteSpace
{
get => _spellOutWhiteSpace;
set
{
_spellOutWhiteSpace = value;
RecalculateDescription();
}
}

private bool _globalFlag;
private bool _ignoreCaseFlag;
private string _pattern;

private List<TreeViewItem> _resultItems;
public List<TreeViewItem> ResultItems
{
Expand Down Expand Up @@ -78,7 +89,7 @@ private void RecalculateDescription()
ResultItems = results;
return;
}
ResultItems = ToTreeViewItems(new Pattern(_pattern, _ignoreCaseFlag, _globalFlag));
ResultItems = ToTreeViewItems(new Pattern(_pattern, _ignoreCaseFlag, _globalFlag, _spellOutWhiteSpace));
}

private List<TreeViewItem> ToTreeViewItems(Pattern pattern)
Expand All @@ -92,7 +103,7 @@ private List<TreeViewItem> ToTreeViewItems(Pattern pattern)
{
resultItems.Add(TreeViewItemFromHeader(pattern.StartAnchorDescription));
}
resultItems.Add(AsTreeViewItem((dynamic)pattern.RootExpression));
resultItems.Add(AsTreeViewItem((dynamic)pattern.RootExpression, _spellOutWhiteSpace));
if (pattern.AnchoredAtEnd)
{
resultItems.Add(TreeViewItemFromHeader(pattern.EndAnchorDescription));
Expand All @@ -112,60 +123,60 @@ private TreeViewItem TreeViewItemFromHeader(string header)

public string DescriptionResults { get; private set; }

private static TreeViewItem AsTreeViewItem(IRegularExpression expression)
private static TreeViewItem AsTreeViewItem(IRegularExpression expression, bool spellOutWhitespace)
{
throw new InvalidOperationException("Some unknown IRegularExpression subtype was in RegexAssistantViewModel");
throw new InvalidOperationException($"Some unknown {typeof(IRegularExpression)} subtype was in RegexAssistantViewModel");
}

private static TreeViewItem AsTreeViewItem(ErrorExpression expression)
private static TreeViewItem AsTreeViewItem(ErrorExpression expression, bool spellOutWhitespace)
{
var result = new TreeViewItem
{
Header = expression.Description
Header = expression.Description(spellOutWhitespace)
};

return result;
}

private static TreeViewItem AsTreeViewItem(ConcatenatedExpression expression)
private static TreeViewItem AsTreeViewItem(ConcatenatedExpression expression, bool spellOutWhitespace)
{
var result = new TreeViewItem
{
Header = expression.Description
Header = expression.Description(spellOutWhitespace)
};

foreach (var subtree in expression.Subexpressions.Select(exp => AsTreeViewItem((dynamic)exp)))
foreach (var subtree in expression.Subexpressions.Select(exp => AsTreeViewItem((dynamic)exp, spellOutWhitespace)))
{
result.Items.Add(subtree);
}
return result;
}

private static TreeViewItem AsTreeViewItem(AlternativesExpression expression)
private static TreeViewItem AsTreeViewItem(AlternativesExpression expression, bool spellOutWhitespace)
{
var result = new TreeViewItem
{
Header = expression.Description
Header = expression.Description(spellOutWhitespace)
};

foreach (var subtree in expression.Subexpressions.Select(exp => AsTreeViewItem((dynamic)exp)))
foreach (var subtree in expression.Subexpressions.Select(exp => AsTreeViewItem((dynamic)exp, spellOutWhitespace)))
{
result.Items.Add(subtree);
}
return result;
}

private static TreeViewItem AsTreeViewItem(SingleAtomExpression expression)
private static TreeViewItem AsTreeViewItem(SingleAtomExpression expression, bool spellOutWhitespace)
{
var result = new TreeViewItem
{
Header = expression.Description
Header = expression.Description(spellOutWhitespace)
};

// no other Atom has Subexpressions we care about
if (expression.Atom.GetType() == typeof(Group))
{
result.Items.Add(AsTreeViewItem((dynamic)(expression.Atom as Group).Subexpression));
result.Items.Add(AsTreeViewItem((dynamic)(expression.Atom as Group).Subexpression, spellOutWhitespace));
}

return result;
Expand Down
18 changes: 13 additions & 5 deletions Rubberduck.RegexAssistant/Atoms/CharacterClass.cs
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Rubberduck.Resources;

namespace Rubberduck.RegexAssistant.Atoms
{
Expand All @@ -11,7 +12,7 @@ internal class CharacterClass : IAtom
public bool InverseMatching { get; }
public IList<string> CharacterSpecifiers { get; }

public CharacterClass(string specifier, Quantifier quantifier)
public CharacterClass(string specifier, Quantifier quantifier, bool spellOutWhiteSpace = false)
{
if (specifier == null || quantifier == null)
{
Expand All @@ -27,15 +28,19 @@ public CharacterClass(string specifier, Quantifier quantifier)
// trim leading and closing bracket
var actualSpecifier = specifier.Substring(1, specifier.Length - 2);
InverseMatching = actualSpecifier.StartsWith("^");
CharacterSpecifiers= ExtractCharacterSpecifiers(InverseMatching ? actualSpecifier.Substring(1) : actualSpecifier);
CharacterSpecifiers = ExtractCharacterSpecifiers(InverseMatching
? actualSpecifier.Substring(1)
: actualSpecifier
, spellOutWhiteSpace);
}

public string Specifier { get; }

public Quantifier Quantifier { get; }

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}|[\.\\\[\]])|.))?)", RegexOptions.Compiled);
private IList<string> ExtractCharacterSpecifiers(string characterClass)

private IList<string> ExtractCharacterSpecifiers(string characterClass, bool spellOutWhitespace)
{
var specifiers = CharacterRanges.Matches(characterClass);
var result = new List<string>();
Expand All @@ -55,12 +60,15 @@ private IList<string> ExtractCharacterSpecifiers(string characterClass)
continue;
}
}
result.Add(specifier.Value);

result.Add(spellOutWhitespace && WhitespaceToString.IsFullySpellingOutApplicable(specifier.Value, out var spelledOutWhiteSpace)
? spelledOutWhiteSpace
: specifier.Value);
}
return result;
}

public string Description => string.Format(InverseMatching
public string Description(bool spellOutWhitespace) => string.Format(InverseMatching
? AssistantResources.AtomDescription_CharacterClass_Inverted
: AssistantResources.AtomDescription_CharacterClass
, HumanReadableClass());
Expand Down
5 changes: 4 additions & 1 deletion Rubberduck.RegexAssistant/Atoms/Group.cs
Expand Up @@ -23,7 +23,10 @@ public class Group : IAtom

public string Specifier { get; }

public string Description => string.Format(AssistantResources.AtomDescription_Group, Specifier);
public string Description(bool spellOutWhitespace) => string.Format(AssistantResources.AtomDescription_Group,
spellOutWhitespace && WhitespaceToString.IsFullySpellingOutApplicable(Specifier, out var spelledOutWhiteSpace)
? spelledOutWhiteSpace
: Specifier);


public override string ToString() => Specifier;
Expand Down
75 changes: 39 additions & 36 deletions Rubberduck.RegexAssistant/Atoms/Literal.cs
Expand Up @@ -45,6 +45,7 @@ public Literal(string specifier, Quantifier quantifier)
{
throw new ArgumentException("The given specifier does not denote a Literal");
}

Specifier = specifier;
}

Expand All @@ -53,52 +54,54 @@ public Literal(string specifier, Quantifier quantifier)
public Quantifier Quantifier { get; }

private static readonly Dictionary<char, string> _escapeDescriptions = new Dictionary<char, string>();
public string Description
public string Description(bool spellOutWhitespace)
{
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)
{
// 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)
var relevant = Specifier.Substring(1); // skip the damn Backslash at the start
if (relevant.Length > 1) // longer sequences
{
var 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]))
if (relevant.StartsWith("u"))
{
return string.Format(AssistantResources.AtomDescription_Literal_Backreference, relevant);
return string.Format(AssistantResources.AtomDescription_Literal_UnicodePoint, relevant.Substring(1)); //skip u
}
else

if (relevant.StartsWith("x"))
{
return _escapeDescriptions[relevant[0]];
return string.Format(AssistantResources.AtomDescription_Literal_HexCodepoint, relevant.Substring(1)); // skip x
}

return string.Format(AssistantResources.AtomDescription_Literal_OctalCodepoint, relevant); // no format specifier to skip
}

return Specifier.Equals(".")
? AssistantResources.AtomDescription_Dot
: string.Format(AssistantResources.AtomDescription_Literal_ActualLiteral, Specifier);
if (EscapeLiterals.Contains(relevant[0]))
{
return string.Format(AssistantResources.AtomDescription_Literal_EscapedLiteral, relevant);
}

if (char.IsDigit(relevant[0]))
{
return string.Format(AssistantResources.AtomDescription_Literal_Backreference, relevant);
}

return _escapeDescriptions[relevant[0]];
}

if (Specifier.Equals("."))
{
return AssistantResources.AtomDescription_Dot;
}

return string.Format(AssistantResources.AtomDescription_Literal_ActualLiteral,
spellOutWhitespace && WhitespaceToString.IsFullySpellingOutApplicable(Specifier, out var spelledOutWhiteSpace)
? spelledOutWhiteSpace
: Specifier);
}

public override string ToString() => Specifier;
Expand Down
Expand Up @@ -12,11 +12,11 @@ public AlternativesExpression(IList<IRegularExpression> subexpressions)
Subexpressions = subexpressions ?? throw new ArgumentNullException();
}

public string Description => string.Format(AssistantResources.ExpressionDescription_AlternativesExpression, Subexpressions.Count);
public string Description(bool spellOutWhitespace) => string.Format(AssistantResources.ExpressionDescription_AlternativesExpression, Subexpressions.Count);

public IList<IRegularExpression> Subexpressions { get; }

public override string ToString() => $"Aternatives:{Subexpressions.ToString()}";
public override string ToString() => $"Alternatives:{Subexpressions}";
public override bool Equals(object obj) => obj is AlternativesExpression other && Subexpressions.Equals(other.Subexpressions);
public override int GetHashCode() => HashCode.Compute(Subexpressions);
}
Expand Down
Expand Up @@ -12,7 +12,7 @@ public ConcatenatedExpression(IList<IRegularExpression> subexpressions)
Subexpressions = subexpressions ?? throw new ArgumentNullException();
}

public string Description => AssistantResources.ExpressionDescription_ConcatenatedExpression;
public string Description(bool spellOutWhitespace) => AssistantResources.ExpressionDescription_ConcatenatedExpression;

public IList<IRegularExpression> Subexpressions { get; }

Expand Down
2 changes: 1 addition & 1 deletion Rubberduck.RegexAssistant/Expressions/ErrorExpression.cs
Expand Up @@ -13,7 +13,7 @@ public ErrorExpression(string errorToken)
_errorToken = errorToken ?? throw new ArgumentNullException();
}

public string Description => string.Format(AssistantResources.ExpressionDescription_ErrorExpression, _errorToken);
public string Description(bool spellOutWhitespace) => string.Format(AssistantResources.ExpressionDescription_ErrorExpression, _errorToken);

public IList<IRegularExpression> Subexpressions => new List<IRegularExpression>();

Expand Down
Expand Up @@ -13,11 +13,10 @@ public SingleAtomExpression(IAtom atom)
Atom = atom ?? throw new ArgumentNullException();
}

public string Description => $"{Atom.Description} {Atom.Quantifier.HumanReadable()}.";
public string Description(bool spellOutWhitespace) => $"{Atom.Description(spellOutWhitespace)} {Atom.Quantifier.HumanReadable()}.";

public IList<IRegularExpression> Subexpressions => new List<IRegularExpression>(Enumerable.Empty<IRegularExpression>());


public override string ToString() => $"Atom: {Atom}";
public override bool Equals(object obj) => obj is SingleAtomExpression other && other.Atom.Equals(Atom);
public override int GetHashCode() => Atom.GetHashCode();
Expand Down
2 changes: 1 addition & 1 deletion Rubberduck.RegexAssistant/IDescribable.cs
Expand Up @@ -2,6 +2,6 @@
{
public interface IDescribable
{
string Description { get; }
string Description(bool spellOutWhiteSpace = false);
}
}
15 changes: 10 additions & 5 deletions Rubberduck.RegexAssistant/Pattern.cs
Expand Up @@ -8,9 +8,10 @@ public class Pattern : IDescribable
public IRegularExpression RootExpression;
readonly MatcherFlags Flags;

public string Description { get; }
private readonly string _description;
public string Description(bool spellOutWhitespace) => _description;

public Pattern(string expression, bool ignoreCase = false, bool global = false)
public Pattern(string expression, bool ignoreCase = false, bool global = false, bool spellOutWhitespace = false)
{
if (expression == null)
{
Expand All @@ -25,16 +26,20 @@ public Pattern(string expression, bool ignoreCase = false, bool global = false)

var start = AnchoredAtStart ? 1 : 0;
var end = (AnchoredAtEnd ? 1 : 0) + start;
RootExpression = VBRegexParser.Parse(expression.Substring(start, expression.Length - end));
Description = AssembleDescription();

_spellOutWhiteSpace = spellOutWhitespace;
RootExpression = VBRegexParser.Parse(expression.Substring(start, expression.Length - end), _spellOutWhiteSpace);
_description = AssembleDescription();
}

private readonly bool _spellOutWhiteSpace;

private string AssembleDescription()
{
var result = string.Empty;
result += CasingDescription;
result += StartAnchorDescription;
result += RootExpression.Description;
result += RootExpression.Description(_spellOutWhiteSpace);
result += EndAnchorDescription;
return result;
}
Expand Down

0 comments on commit 8531cfb

Please sign in to comment.