Skip to content

Commit 8531cfb

Browse files
authored
Merge pull request #5044 from IvenBach/Issue2776_RegexAssistantWhitespace
Fully spell out whitespace characters in RegexAssistant description
2 parents 10a0572 + cbc7c79 commit 8531cfb

File tree

15 files changed

+277
-90
lines changed

15 files changed

+277
-90
lines changed

Rubberduck.Core/UI/RegexAssistant/RegexAssistant.xaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,15 @@
7979
<CheckBox Content="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=RegexAssistant_IgnoreCaseFlag}"
8080
IsChecked="{Binding IgnoreCaseFlag}"
8181
Margin="0,0,5,0"/>
82+
<CheckBox Content="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=RegexAssistant_EncloseWhitespaceFlag}"
83+
IsChecked="{Binding SpellOutWhiteSpace}"
84+
Margin="0,0,5,0" />
85+
8286
</StackPanel>
8387
</StackPanel>
8488
</GroupBox>
8589

86-
<GroupBox Grid.Row="1" Margin="5">
90+
<GroupBox Grid.Row="1" Margin="0,5">
8791
<GroupBox.Header>
8892
<Label Content="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=RegexAssistant_DescriptionResultsLabel}" FontWeight="SemiBold" />
8993
</GroupBox.Header>

Rubberduck.Core/UI/RegexAssistant/RegexAssistantViewModel.cs

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,21 @@ public string Pattern
4747
}
4848
}
4949

50+
private bool _spellOutWhiteSpace;
51+
public bool SpellOutWhiteSpace
52+
{
53+
get => _spellOutWhiteSpace;
54+
set
55+
{
56+
_spellOutWhiteSpace = value;
57+
RecalculateDescription();
58+
}
59+
}
60+
5061
private bool _globalFlag;
5162
private bool _ignoreCaseFlag;
5263
private string _pattern;
53-
64+
5465
private List<TreeViewItem> _resultItems;
5566
public List<TreeViewItem> ResultItems
5667
{
@@ -78,7 +89,7 @@ private void RecalculateDescription()
7889
ResultItems = results;
7990
return;
8091
}
81-
ResultItems = ToTreeViewItems(new Pattern(_pattern, _ignoreCaseFlag, _globalFlag));
92+
ResultItems = ToTreeViewItems(new Pattern(_pattern, _ignoreCaseFlag, _globalFlag, _spellOutWhiteSpace));
8293
}
8394

8495
private List<TreeViewItem> ToTreeViewItems(Pattern pattern)
@@ -92,7 +103,7 @@ private List<TreeViewItem> ToTreeViewItems(Pattern pattern)
92103
{
93104
resultItems.Add(TreeViewItemFromHeader(pattern.StartAnchorDescription));
94105
}
95-
resultItems.Add(AsTreeViewItem((dynamic)pattern.RootExpression));
106+
resultItems.Add(AsTreeViewItem((dynamic)pattern.RootExpression, _spellOutWhiteSpace));
96107
if (pattern.AnchoredAtEnd)
97108
{
98109
resultItems.Add(TreeViewItemFromHeader(pattern.EndAnchorDescription));
@@ -112,60 +123,60 @@ private TreeViewItem TreeViewItemFromHeader(string header)
112123

113124
public string DescriptionResults { get; private set; }
114125

115-
private static TreeViewItem AsTreeViewItem(IRegularExpression expression)
126+
private static TreeViewItem AsTreeViewItem(IRegularExpression expression, bool spellOutWhitespace)
116127
{
117-
throw new InvalidOperationException("Some unknown IRegularExpression subtype was in RegexAssistantViewModel");
128+
throw new InvalidOperationException($"Some unknown {typeof(IRegularExpression)} subtype was in RegexAssistantViewModel");
118129
}
119130

120-
private static TreeViewItem AsTreeViewItem(ErrorExpression expression)
131+
private static TreeViewItem AsTreeViewItem(ErrorExpression expression, bool spellOutWhitespace)
121132
{
122133
var result = new TreeViewItem
123134
{
124-
Header = expression.Description
135+
Header = expression.Description(spellOutWhitespace)
125136
};
126137

127138
return result;
128139
}
129140

130-
private static TreeViewItem AsTreeViewItem(ConcatenatedExpression expression)
141+
private static TreeViewItem AsTreeViewItem(ConcatenatedExpression expression, bool spellOutWhitespace)
131142
{
132143
var result = new TreeViewItem
133144
{
134-
Header = expression.Description
145+
Header = expression.Description(spellOutWhitespace)
135146
};
136147

137-
foreach (var subtree in expression.Subexpressions.Select(exp => AsTreeViewItem((dynamic)exp)))
148+
foreach (var subtree in expression.Subexpressions.Select(exp => AsTreeViewItem((dynamic)exp, spellOutWhitespace)))
138149
{
139150
result.Items.Add(subtree);
140151
}
141152
return result;
142153
}
143154

144-
private static TreeViewItem AsTreeViewItem(AlternativesExpression expression)
155+
private static TreeViewItem AsTreeViewItem(AlternativesExpression expression, bool spellOutWhitespace)
145156
{
146157
var result = new TreeViewItem
147158
{
148-
Header = expression.Description
159+
Header = expression.Description(spellOutWhitespace)
149160
};
150161

151-
foreach (var subtree in expression.Subexpressions.Select(exp => AsTreeViewItem((dynamic)exp)))
162+
foreach (var subtree in expression.Subexpressions.Select(exp => AsTreeViewItem((dynamic)exp, spellOutWhitespace)))
152163
{
153164
result.Items.Add(subtree);
154165
}
155166
return result;
156167
}
157168

158-
private static TreeViewItem AsTreeViewItem(SingleAtomExpression expression)
169+
private static TreeViewItem AsTreeViewItem(SingleAtomExpression expression, bool spellOutWhitespace)
159170
{
160171
var result = new TreeViewItem
161172
{
162-
Header = expression.Description
173+
Header = expression.Description(spellOutWhitespace)
163174
};
164175

165176
// no other Atom has Subexpressions we care about
166177
if (expression.Atom.GetType() == typeof(Group))
167178
{
168-
result.Items.Add(AsTreeViewItem((dynamic)(expression.Atom as Group).Subexpression));
179+
result.Items.Add(AsTreeViewItem((dynamic)(expression.Atom as Group).Subexpression, spellOutWhitespace));
169180
}
170181

171182
return result;

Rubberduck.RegexAssistant/Atoms/CharacterClass.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Text.RegularExpressions;
6+
using Rubberduck.Resources;
67

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

14-
public CharacterClass(string specifier, Quantifier quantifier)
15+
public CharacterClass(string specifier, Quantifier quantifier, bool spellOutWhiteSpace = false)
1516
{
1617
if (specifier == null || quantifier == null)
1718
{
@@ -27,15 +28,19 @@ public CharacterClass(string specifier, Quantifier quantifier)
2728
// trim leading and closing bracket
2829
var actualSpecifier = specifier.Substring(1, specifier.Length - 2);
2930
InverseMatching = actualSpecifier.StartsWith("^");
30-
CharacterSpecifiers= ExtractCharacterSpecifiers(InverseMatching ? actualSpecifier.Substring(1) : actualSpecifier);
31+
CharacterSpecifiers = ExtractCharacterSpecifiers(InverseMatching
32+
? actualSpecifier.Substring(1)
33+
: actualSpecifier
34+
, spellOutWhiteSpace);
3135
}
3236

3337
public string Specifier { get; }
3438

3539
public Quantifier Quantifier { get; }
3640

3741
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);
38-
private IList<string> ExtractCharacterSpecifiers(string characterClass)
42+
43+
private IList<string> ExtractCharacterSpecifiers(string characterClass, bool spellOutWhitespace)
3944
{
4045
var specifiers = CharacterRanges.Matches(characterClass);
4146
var result = new List<string>();
@@ -55,12 +60,15 @@ private IList<string> ExtractCharacterSpecifiers(string characterClass)
5560
continue;
5661
}
5762
}
58-
result.Add(specifier.Value);
63+
64+
result.Add(spellOutWhitespace && WhitespaceToString.IsFullySpellingOutApplicable(specifier.Value, out var spelledOutWhiteSpace)
65+
? spelledOutWhiteSpace
66+
: specifier.Value);
5967
}
6068
return result;
6169
}
6270

63-
public string Description => string.Format(InverseMatching
71+
public string Description(bool spellOutWhitespace) => string.Format(InverseMatching
6472
? AssistantResources.AtomDescription_CharacterClass_Inverted
6573
: AssistantResources.AtomDescription_CharacterClass
6674
, HumanReadableClass());

Rubberduck.RegexAssistant/Atoms/Group.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ public Group(IRegularExpression expression, string specifier, Quantifier quantif
2323

2424
public string Specifier { get; }
2525

26-
public string Description => string.Format(AssistantResources.AtomDescription_Group, Specifier);
26+
public string Description(bool spellOutWhitespace) => string.Format(AssistantResources.AtomDescription_Group,
27+
spellOutWhitespace && WhitespaceToString.IsFullySpellingOutApplicable(Specifier, out var spelledOutWhiteSpace)
28+
? spelledOutWhiteSpace
29+
: Specifier);
2730

2831

2932
public override string ToString() => Specifier;

Rubberduck.RegexAssistant/Atoms/Literal.cs

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public Literal(string specifier, Quantifier quantifier)
4545
{
4646
throw new ArgumentException("The given specifier does not denote a Literal");
4747
}
48+
4849
Specifier = specifier;
4950
}
5051

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

5556
private static readonly Dictionary<char, string> _escapeDescriptions = new Dictionary<char, string>();
56-
public string Description
57+
public string Description(bool spellOutWhitespace)
5758
{
58-
get
59+
// here be dragons!
60+
// keep track of:
61+
// - escaped chars
62+
// - escape sequences (each having a different description)
63+
// - codepoint escapes (belongs into above category but kept separate)
64+
// - and actually boring literal matches
65+
if (Specifier.Length > 1)
5966
{
60-
// here be dragons!
61-
// keep track of:
62-
// - escaped chars
63-
// - escape sequences (each having a different description)
64-
// - codepoint escapes (belongs into above category but kept separate)
65-
// - and actually boring literal matches
66-
if (Specifier.Length > 1)
67+
var relevant = Specifier.Substring(1); // skip the damn Backslash at the start
68+
if (relevant.Length > 1) // longer sequences
6769
{
68-
var relevant = Specifier.Substring(1); // skip the damn Backslash at the start
69-
if (relevant.Length > 1) // longer sequences
70-
{
71-
if (relevant.StartsWith("u"))
72-
{
73-
return string.Format(AssistantResources.AtomDescription_Literal_UnicodePoint, relevant.Substring(1)); //skip u
74-
}
75-
else if (relevant.StartsWith("x"))
76-
{
77-
return string.Format(AssistantResources.AtomDescription_Literal_HexCodepoint, relevant.Substring(1)); // skip x
78-
}
79-
else
80-
{
81-
return string.Format(AssistantResources.AtomDescription_Literal_OctalCodepoint, relevant); // no format specifier to skip
82-
}
83-
}
84-
else if (EscapeLiterals.Contains(relevant[0]))
85-
{
86-
return string.Format(AssistantResources.AtomDescription_Literal_EscapedLiteral, relevant);
87-
}
88-
else if (char.IsDigit(relevant[0]))
70+
if (relevant.StartsWith("u"))
8971
{
90-
return string.Format(AssistantResources.AtomDescription_Literal_Backreference, relevant);
72+
return string.Format(AssistantResources.AtomDescription_Literal_UnicodePoint, relevant.Substring(1)); //skip u
9173
}
92-
else
74+
75+
if (relevant.StartsWith("x"))
9376
{
94-
return _escapeDescriptions[relevant[0]];
77+
return string.Format(AssistantResources.AtomDescription_Literal_HexCodepoint, relevant.Substring(1)); // skip x
9578
}
79+
80+
return string.Format(AssistantResources.AtomDescription_Literal_OctalCodepoint, relevant); // no format specifier to skip
9681
}
9782

98-
return Specifier.Equals(".")
99-
? AssistantResources.AtomDescription_Dot
100-
: string.Format(AssistantResources.AtomDescription_Literal_ActualLiteral, Specifier);
83+
if (EscapeLiterals.Contains(relevant[0]))
84+
{
85+
return string.Format(AssistantResources.AtomDescription_Literal_EscapedLiteral, relevant);
86+
}
87+
88+
if (char.IsDigit(relevant[0]))
89+
{
90+
return string.Format(AssistantResources.AtomDescription_Literal_Backreference, relevant);
91+
}
92+
93+
return _escapeDescriptions[relevant[0]];
10194
}
95+
96+
if (Specifier.Equals("."))
97+
{
98+
return AssistantResources.AtomDescription_Dot;
99+
}
100+
101+
return string.Format(AssistantResources.AtomDescription_Literal_ActualLiteral,
102+
spellOutWhitespace && WhitespaceToString.IsFullySpellingOutApplicable(Specifier, out var spelledOutWhiteSpace)
103+
? spelledOutWhiteSpace
104+
: Specifier);
102105
}
103106

104107
public override string ToString() => Specifier;

Rubberduck.RegexAssistant/Expressions/AlternativesExpression.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ public AlternativesExpression(IList<IRegularExpression> subexpressions)
1212
Subexpressions = subexpressions ?? throw new ArgumentNullException();
1313
}
1414

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

1717
public IList<IRegularExpression> Subexpressions { get; }
1818

19-
public override string ToString() => $"Aternatives:{Subexpressions.ToString()}";
19+
public override string ToString() => $"Alternatives:{Subexpressions}";
2020
public override bool Equals(object obj) => obj is AlternativesExpression other && Subexpressions.Equals(other.Subexpressions);
2121
public override int GetHashCode() => HashCode.Compute(Subexpressions);
2222
}

Rubberduck.RegexAssistant/Expressions/ConcatenatedExpression.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public ConcatenatedExpression(IList<IRegularExpression> subexpressions)
1212
Subexpressions = subexpressions ?? throw new ArgumentNullException();
1313
}
1414

15-
public string Description => AssistantResources.ExpressionDescription_ConcatenatedExpression;
15+
public string Description(bool spellOutWhitespace) => AssistantResources.ExpressionDescription_ConcatenatedExpression;
1616

1717
public IList<IRegularExpression> Subexpressions { get; }
1818

Rubberduck.RegexAssistant/Expressions/ErrorExpression.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public ErrorExpression(string errorToken)
1313
_errorToken = errorToken ?? throw new ArgumentNullException();
1414
}
1515

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

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

Rubberduck.RegexAssistant/Expressions/SingleAtomExpression.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@ public SingleAtomExpression(IAtom atom)
1313
Atom = atom ?? throw new ArgumentNullException();
1414
}
1515

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

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

20-
2120
public override string ToString() => $"Atom: {Atom}";
2221
public override bool Equals(object obj) => obj is SingleAtomExpression other && other.Atom.Equals(Atom);
2322
public override int GetHashCode() => Atom.GetHashCode();

Rubberduck.RegexAssistant/IDescribable.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
{
33
public interface IDescribable
44
{
5-
string Description { get; }
5+
string Description(bool spellOutWhiteSpace = false);
66
}
77
}

0 commit comments

Comments
 (0)