Skip to content

Commit 24850c8

Browse files
committed
cache regex; rework validation system
1 parent bfee6f0 commit 24850c8

File tree

3 files changed

+72
-63
lines changed

3 files changed

+72
-63
lines changed

Diff for: src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp

+14-25
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "pch.h"
55
#include "CascadiaSettings.h"
66
#include "CascadiaSettings.g.cpp"
7+
#include "MatchProfilesEntry.h"
78

89
#include "DefaultTerminal.h"
910
#include "FileUtils.h"
@@ -584,47 +585,35 @@ void CascadiaSettings::_validateProfileEnvironmentVariables()
584585
}
585586
}
586587

587-
static void _validateRegex(const winrt::hstring& regex, IVector<Model::SettingsLoadWarnings>& warnings)
588-
{
589-
try
590-
{
591-
std::wregex{ regex.cbegin(), regex.cend() };
592-
}
593-
catch (std::regex_error)
594-
{
595-
warnings.Append(Model::SettingsLoadWarnings::InvalidRegex);
596-
}
597-
}
598-
599-
static void _validateNTMEntries(const IVector<Model::NewTabMenuEntry>& entries, IVector<Model::SettingsLoadWarnings>& warnings)
588+
// Returns true if all regexes in the new tab menu are valid, false otherwise
589+
static bool _validateNTMEntries(const IVector<Model::NewTabMenuEntry>& entries)
600590
{
601591
for (const auto& ntmEntry : entries)
602592
{
603593
if (const auto& folderEntry = ntmEntry.try_as<Model::FolderEntry>())
604594
{
605-
_validateNTMEntries(folderEntry.RawEntries(), warnings);
595+
if (!_validateNTMEntries(folderEntry.RawEntries()))
596+
{
597+
return false;
598+
}
606599
}
607600
if (const auto& matchProfilesEntry = ntmEntry.try_as<Model::MatchProfilesEntry>())
608601
{
609-
if (const auto nameRegex = matchProfilesEntry.Name(); !nameRegex.empty())
610-
{
611-
_validateRegex(nameRegex, warnings);
612-
}
613-
if (const auto commandlineRegex = matchProfilesEntry.Commandline(); !commandlineRegex.empty())
602+
if (!winrt::get_self<Model::implementation::MatchProfilesEntry>(matchProfilesEntry)->ValidateRegexes())
614603
{
615-
_validateRegex(commandlineRegex, warnings);
616-
}
617-
if (const auto sourceRegex = matchProfilesEntry.Source(); !sourceRegex.empty())
618-
{
619-
_validateRegex(sourceRegex, warnings);
604+
return false;
620605
}
621606
}
622607
}
608+
return true;
623609
}
624610

625611
void CascadiaSettings::_validateRegexes()
626612
{
627-
_validateNTMEntries(_globals->NewTabMenu(), _warnings);
613+
if (!_validateNTMEntries(_globals->NewTabMenu()))
614+
{
615+
_warnings.Append(SettingsLoadWarnings::InvalidRegex);
616+
}
628617
}
629618

630619
// Method Description:

Diff for: src/cascadia/TerminalSettingsModel/MatchProfilesEntry.cpp

+20-35
Original file line numberDiff line numberDiff line change
@@ -36,61 +36,46 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
3636
auto entry = winrt::make_self<MatchProfilesEntry>();
3737

3838
JsonUtils::GetValueForKey(json, NameKey, entry->_Name);
39+
entry->_validateName();
40+
3941
JsonUtils::GetValueForKey(json, CommandlineKey, entry->_Commandline);
42+
entry->_validateCommandline();
43+
4044
JsonUtils::GetValueForKey(json, SourceKey, entry->_Source);
45+
entry->_validateSource();
4146

4247
return entry;
4348
}
4449

50+
// Returns true if all regexes are valid, false otherwise
51+
bool MatchProfilesEntry::ValidateRegexes() const
52+
{
53+
return !(_invalidName || _invalidCommandline || _invalidSource);
54+
}
55+
4556
bool MatchProfilesEntry::MatchesProfile(const Model::Profile& profile)
4657
{
47-
// We use an optional here instead of a simple bool directly, since there is no
48-
// sensible default value for the desired semantics: the first property we want
49-
// to match on should always be applied (so one would set "true" as a default),
50-
// but if none of the properties are set, the default return value should be false
51-
// since this entry type is expected to behave like a positive match/whitelist.
52-
//
53-
// The easiest way to deal with this neatly is to use an optional, then for any
54-
// property to match we consider a null value to be "true", and for the return
55-
// value of the function we consider the null value to be "false".
56-
auto isMatching = std::optional<bool>{};
57-
58-
auto isMatch = [](std::wstring_view regex, std::wstring_view text) {
58+
auto isMatch = [](const std::wregex& regex, std::wstring_view text) {
5959
if (text.empty())
6060
{
6161
return false;
6262
}
63-
64-
std::wregex re;
65-
try
66-
{
67-
re = { regex.cbegin(), regex.cend() };
68-
}
69-
catch (std::regex_error)
70-
{
71-
// invalid regex
72-
return false;
73-
}
74-
75-
return std::regex_match(text.cbegin(), text.cend(), re);
63+
return std::regex_match(text.cbegin(), text.cend(), regex);
7664
};
7765

78-
if (!_Name.empty())
66+
if (!_Name.empty() && isMatch(_NameRegex, profile.Name()))
7967
{
80-
isMatching = { isMatching.value_or(true) && isMatch(_Name, profile.Name()) };
68+
return true;
8169
}
82-
83-
if (!_Source.empty())
70+
else if (!_Source.empty() && isMatch(_SourceRegex, profile.Source()))
8471
{
85-
isMatching = { isMatching.value_or(true) && isMatch(_Source, profile.Source()) };
72+
return true;
8673
}
87-
88-
if (!_Commandline.empty())
74+
else if (!_Commandline.empty() && isMatch(_CommandlineRegex, profile.Commandline()))
8975
{
90-
isMatching = { isMatching.value_or(true) && isMatch(_Commandline, profile.Commandline()) };
76+
return true;
9177
}
92-
93-
return isMatching.value_or(false);
78+
return false;
9479
}
9580

9681
Model::NewTabMenuEntry MatchProfilesEntry::Copy() const

Diff for: src/cascadia/TerminalSettingsModel/MatchProfilesEntry.h

+38-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,40 @@ Author(s):
1818
#include "ProfileCollectionEntry.h"
1919
#include "MatchProfilesEntry.g.h"
2020

21+
// This macro defines the getter and setter for a regex property.
22+
// The setter tries to instantiate the regex immediately and caches
23+
// it if successful. If it fails, it sets a boolean flag to track that
24+
// it failed.
25+
#define MATCH_PROFILE_REGEX_PROPERTY(name) \
26+
public: \
27+
hstring name() const noexcept \
28+
{ \
29+
return _##name; \
30+
} \
31+
void name(const hstring& value) noexcept \
32+
{ \
33+
_##name = value; \
34+
_validate##name(); \
35+
} \
36+
\
37+
private: \
38+
void _validate##name() noexcept \
39+
{ \
40+
_invalid##name = false; \
41+
try \
42+
{ \
43+
_##name##Regex = { _##name.cbegin(), _##name.cend() }; \
44+
} \
45+
catch (std::regex_error) \
46+
{ \
47+
_invalid##name = true; \
48+
} \
49+
} \
50+
\
51+
hstring _##name; \
52+
std::wregex _##name##Regex; \
53+
bool _invalid##name{ false };
54+
2155
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
2256
{
2357
struct MatchProfilesEntry : MatchProfilesEntryT<MatchProfilesEntry, ProfileCollectionEntry>
@@ -30,11 +64,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
3064
Json::Value ToJson() const override;
3165
static com_ptr<NewTabMenuEntry> FromJson(const Json::Value& json);
3266

67+
bool ValidateRegexes() const;
3368
bool MatchesProfile(const Model::Profile& profile);
3469

35-
WINRT_PROPERTY(winrt::hstring, Name);
36-
WINRT_PROPERTY(winrt::hstring, Commandline);
37-
WINRT_PROPERTY(winrt::hstring, Source);
70+
MATCH_PROFILE_REGEX_PROPERTY(Name);
71+
MATCH_PROFILE_REGEX_PROPERTY(Commandline);
72+
MATCH_PROFILE_REGEX_PROPERTY(Source);
3873
};
3974
}
4075

0 commit comments

Comments
 (0)