-
Notifications
You must be signed in to change notification settings - Fork 8.6k
Replace New Tab Menu Match Profiles functionality with regex support #18654
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bfee6f0
24850c8
ae3f960
adbe85b
50c4d4d
5962014
d128a09
691c58d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,41 +36,71 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation | |
auto entry = winrt::make_self<MatchProfilesEntry>(); | ||
|
||
JsonUtils::GetValueForKey(json, NameKey, entry->_Name); | ||
entry->_validateName(); | ||
|
||
JsonUtils::GetValueForKey(json, CommandlineKey, entry->_Commandline); | ||
entry->_validateCommandline(); | ||
|
||
JsonUtils::GetValueForKey(json, SourceKey, entry->_Source); | ||
entry->_validateSource(); | ||
|
||
return entry; | ||
} | ||
|
||
// Returns true if all regexes are valid, false otherwise | ||
bool MatchProfilesEntry::ValidateRegexes() const | ||
{ | ||
return !(_invalidName || _invalidCommandline || _invalidSource); | ||
} | ||
|
||
#define DEFINE_VALIDATE_FUNCTION(name) \ | ||
void MatchProfilesEntry::_validate##name() noexcept \ | ||
{ \ | ||
_invalid##name = false; \ | ||
if (_##name.empty()) \ | ||
{ \ | ||
/* empty field is valid*/ \ | ||
return; \ | ||
} \ | ||
UErrorCode status = U_ZERO_ERROR; \ | ||
_##name##Regex = til::ICU::CreateRegex(_##name, 0, &status); \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i wish "validate" didn't also mean "populate". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what i mean to say is, "i wish this was clearer about what it did" |
||
if (U_FAILURE(status)) \ | ||
{ \ | ||
_invalid##name = true; \ | ||
_##name##Regex.reset(); \ | ||
} \ | ||
} | ||
|
||
DEFINE_VALIDATE_FUNCTION(Name); | ||
DEFINE_VALIDATE_FUNCTION(Commandline); | ||
DEFINE_VALIDATE_FUNCTION(Source); | ||
|
||
bool MatchProfilesEntry::MatchesProfile(const Model::Profile& profile) | ||
{ | ||
// We use an optional here instead of a simple bool directly, since there is no | ||
// sensible default value for the desired semantics: the first property we want | ||
// to match on should always be applied (so one would set "true" as a default), | ||
// but if none of the properties are set, the default return value should be false | ||
// since this entry type is expected to behave like a positive match/whitelist. | ||
// | ||
// The easiest way to deal with this neatly is to use an optional, then for any | ||
// property to match we consider a null value to be "true", and for the return | ||
// value of the function we consider the null value to be "false". | ||
auto isMatching = std::optional<bool>{}; | ||
|
||
if (!_Name.empty()) | ||
auto isMatch = [](const til::ICU::unique_uregex& regex, std::wstring_view text) { | ||
if (text.empty()) | ||
{ | ||
return false; | ||
} | ||
UErrorCode status = U_ZERO_ERROR; | ||
uregex_setText(regex.get(), reinterpret_cast<const UChar*>(text.data()), static_cast<int32_t>(text.size()), &status); | ||
const auto match = uregex_matches(regex.get(), 0, &status); | ||
return status == U_ZERO_ERROR && match; | ||
Comment on lines
+85
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make it easier on ourselves, we could write our own Not sure if it's worth doing as part of this PR, but if you're feeling eager, I think it may be worth it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, if we do that, I think it's worth keeping the current approach of not throwing when the regex is invalid (and instead return a bool or |
||
}; | ||
|
||
if (!_Name.empty() && isMatch(_NameRegex, profile.Name())) | ||
{ | ||
isMatching = { isMatching.value_or(true) && _Name == profile.Name() }; | ||
return true; | ||
} | ||
|
||
if (!_Source.empty()) | ||
else if (!_Source.empty() && isMatch(_SourceRegex, profile.Source())) | ||
{ | ||
isMatching = { isMatching.value_or(true) && _Source == profile.Source() }; | ||
return true; | ||
} | ||
|
||
if (!_Commandline.empty()) | ||
else if (!_Commandline.empty() && isMatch(_CommandlineRegex, profile.Commandline())) | ||
{ | ||
isMatching = { isMatching.value_or(true) && _Commandline == profile.Commandline() }; | ||
return true; | ||
} | ||
|
||
return isMatching.value_or(false); | ||
return false; | ||
} | ||
|
||
Model::NewTabMenuEntry MatchProfilesEntry::Copy() const | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
#pragma once | ||
|
||
#include <icu.h> | ||
|
||
namespace til::ICU // Terminal Implementation Library. Also: "Today I Learned" | ||
{ | ||
using unique_uregex = wistd::unique_ptr<URegularExpression, wil::function_deleter<decltype(&uregex_close), &uregex_close>>; | ||
|
||
_TIL_INLINEPREFIX unique_uregex CreateRegex(const std::wstring_view& pattern, uint32_t flags, UErrorCode* status) noexcept | ||
{ | ||
#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1). | ||
const auto re = uregex_open(reinterpret_cast<const char16_t*>(pattern.data()), gsl::narrow_cast<int32_t>(pattern.size()), flags, nullptr, status); | ||
// ICU describes the time unit as being dependent on CPU performance and "typically [in] the order of milliseconds", | ||
// but this claim seems highly outdated already. On my CPU from 2021, a limit of 4096 equals roughly 600ms. | ||
uregex_setTimeLimit(re, 4096, status); | ||
uregex_setStackLimit(re, 4 * 1024 * 1024, status); | ||
return unique_uregex{ re }; | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.