fix: catch regex FormatException and show a corresponding error message (#573)

* add error message

* restore and reformat

* update localization and comments

* chore: reformat files
This commit is contained in:
Sun Jiao 2023-11-06 15:19:23 +08:00 • committed by GitHub
commit 77e5d8713b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 97 additions and 15 deletions

View file

@ -128,6 +128,9 @@
"replaceAll": "Replace all", "replaceAll": "Replace all",
"regex": "Regex", "regex": "Regex",
"caseSensitive": "Case sensitive", "caseSensitive": "Case sensitive",
"regexError": "Regex Error",
"noFindResult": "No result",
"emptySearchBoxHint": "Enter a pattern",
"uploadImage": "Upload Image", "uploadImage": "Upload Image",
"urlImage": "URL Image", "urlImage": "URL Image",
"incorrectLink": "Incorrect Link", "incorrectLink": "Incorrect Link",

View file

@ -128,6 +128,9 @@
"replaceAll": "替换全部", "replaceAll": "替换全部",
"regex": "正则表达式", "regex": "正则表达式",
"caseSensitive": "区分大小写", "caseSensitive": "区分大小写",
"regexError": "正则错误",
"noFindResult": "无匹配项",
"emptySearchBoxHint": "输入查找内容",
"uploadImage": "上传图片", "uploadImage": "上传图片",
"urlImage": "网络图片", "urlImage": "网络图片",
"incorrectLink": "链接错误", "incorrectLink": "链接错误",

View file

@ -128,6 +128,9 @@
"replaceAll": "取代全部", "replaceAll": "取代全部",
"regex": "正規表示式", "regex": "正規表示式",
"caseSensitive": "區分大小寫", "caseSensitive": "區分大小寫",
"regexError": "正規錯誤",
"noFindResult": "無相符項",
"emptySearchBoxHint": "鍵入尋找内容",
"uploadImage": "上載圖片", "uploadImage": "上載圖片",
"urlImage": "網路圖片", "urlImage": "網路圖片",
"incorrectLink": "連結錯誤", "incorrectLink": "連結錯誤",

View file

@ -161,7 +161,7 @@ class _FindMenuState extends State<FindMenu> {
final findTextEditingController = TextEditingController(); final findTextEditingController = TextEditingController();
String queriedPattern = ''; String message = AppFlowyEditorLocalizations.current.emptySearchBoxHint;
bool showReplaceMenu = false; bool showReplaceMenu = false;
bool caseSensitive = false; bool caseSensitive = false;
@ -250,9 +250,7 @@ class _FindMenuState extends State<FindMenu> {
padding: const EdgeInsets.symmetric(horizontal: 8.0), padding: const EdgeInsets.symmetric(horizontal: 8.0),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
matches.isEmpty matches.isEmpty ? message : '$selectedIndex of ${matches.length}',
? widget.localizations?.noResult ?? 'No Result'
: '$selectedIndex of ${matches.length}',
), ),
), ),
// previous match button // previous match button
@ -322,11 +320,25 @@ class _FindMenuState extends State<FindMenu> {
} }
void _searchPattern() { void _searchPattern() {
if (findTextEditingController.text.isEmpty) { String error;
return;
// the following line needs to be executed even if
// findTextEditingController.text.isEmpty, otherwise the previous
// matches will persist
error =
widget.searchService.findAndHighlight(findTextEditingController.text);
switch (error) {
case 'Regex':
message = AppFlowyEditorLocalizations.current.regexError;
case 'Empty':
message = AppFlowyEditorLocalizations.current.emptySearchBoxHint;
default:
message = widget.localizations?.noResult ??
AppFlowyEditorLocalizations.current.noFindResult;
} }
widget.searchService.findAndHighlight(findTextEditingController.text);
setState(() => queriedPattern = findTextEditingController.text); _setState();
} }
void _setState() { void _setState() {

View file

@ -11,8 +11,8 @@ class SearchServiceV3 {
final EditorState editorState; final EditorState editorState;
//matchedPositions.value will contain a list of positions of the matched patterns //matchWrappers.value will contain a list of matchWrappers of the matched patterns
//the position here consists of the node and the starting offset of the //the position here consists of the match and the node path of
//matched pattern. We will use this to traverse between the matched patterns. //matched pattern. We will use this to traverse between the matched patterns.
ValueNotifier<List<MatchWrapper>> matchWrappers = ValueNotifier([]); ValueNotifier<List<MatchWrapper>> matchWrappers = ValueNotifier([]);
SearchAlgorithm searchAlgorithm = DartBuiltIn(); SearchAlgorithm searchAlgorithm = DartBuiltIn();
@ -75,8 +75,15 @@ class SearchServiceV3 {
// Public entry method for _findAndHighlight, do necessary checks // Public entry method for _findAndHighlight, do necessary checks
// and clear previous highlights before calling the private method // and clear previous highlights before calling the private method
void findAndHighlight(String target, {bool unHighlight = false}) { String findAndHighlight(String target, {bool unHighlight = false}) {
Pattern pattern = _getPattern(target); Pattern pattern;
try {
pattern = _getPattern(target);
} on FormatException {
matchWrappers.value.clear();
return 'Regex';
}
if (queriedPattern != pattern) { if (queriedPattern != pattern) {
// this means we have a new pattern, but before we highlight the new matches, // this means we have a new pattern, but before we highlight the new matches,
@ -87,9 +94,11 @@ class SearchServiceV3 {
targetString = target; targetString = target;
} }
if (target.isEmpty) return; if (target.isEmpty) return 'Empty';
_findAndHighlight(pattern, unHighlight: unHighlight); _findAndHighlight(pattern, unHighlight: unHighlight);
return '';
} }
/// Finds the pattern in editorState.document and stores it in matchedPositions. /// Finds the pattern in editorState.document and stores it in matchedPositions.
@ -186,7 +195,7 @@ class SearchServiceV3 {
/// matched word if that exists. /// matched word if that exists.
Future<void> replaceSelectedWord(String replaceText) async { Future<void> replaceSelectedWord(String replaceText) async {
if (replaceText.isEmpty || if (replaceText.isEmpty ||
queriedPattern.toString().isEmpty || queriedPattern.isEmpty ||
matchWrappers.value.isEmpty) { matchWrappers.value.isEmpty) {
return; return;
} }
@ -217,7 +226,7 @@ class SearchServiceV3 {
/// Replaces all the found occurrences of pattern with replaceText /// Replaces all the found occurrences of pattern with replaceText
void replaceAllMatches(String replaceText) { void replaceAllMatches(String replaceText) {
if (replaceText.isEmpty || if (replaceText.isEmpty ||
queriedPattern.toString().isEmpty || queriedPattern.isEmpty ||
matchWrappers.value.isEmpty) { matchWrappers.value.isEmpty) {
return; return;
} }
@ -257,3 +266,15 @@ class MatchWrapper {
end: Position(path: path, offset: match.end), end: Position(path: path, offset: match.end),
); );
} }
extension on Pattern {
bool get isEmpty {
if (this is String) {
return (this as String).isEmpty;
} else if (this is RegExp) {
return (this as RegExp).pattern.isEmpty;
} else {
return toString().isEmpty;
}
}
}

View file

@ -69,6 +69,8 @@ class MessageLookup extends MessageLookupByLibrary {
"done": MessageLookupByLibrary.simpleMessage("Done"), "done": MessageLookupByLibrary.simpleMessage("Done"),
"editLink": MessageLookupByLibrary.simpleMessage("Edit link"), "editLink": MessageLookupByLibrary.simpleMessage("Edit link"),
"embedCode": MessageLookupByLibrary.simpleMessage("Embed Code"), "embedCode": MessageLookupByLibrary.simpleMessage("Embed Code"),
"emptySearchBoxHint":
MessageLookupByLibrary.simpleMessage("Enter a pattern"),
"find": MessageLookupByLibrary.simpleMessage("Find"), "find": MessageLookupByLibrary.simpleMessage("Find"),
"fontColorBlue": MessageLookupByLibrary.simpleMessage("Blue"), "fontColorBlue": MessageLookupByLibrary.simpleMessage("Blue"),
"fontColorBrown": MessageLookupByLibrary.simpleMessage("Brown"), "fontColorBrown": MessageLookupByLibrary.simpleMessage("Brown"),
@ -113,6 +115,7 @@ class MessageLookup extends MessageLookupByLibrary {
"mobileHeading2": MessageLookupByLibrary.simpleMessage("Heading 2"), "mobileHeading2": MessageLookupByLibrary.simpleMessage("Heading 2"),
"mobileHeading3": MessageLookupByLibrary.simpleMessage("Heading 3"), "mobileHeading3": MessageLookupByLibrary.simpleMessage("Heading 3"),
"nextMatch": MessageLookupByLibrary.simpleMessage("Next match"), "nextMatch": MessageLookupByLibrary.simpleMessage("Next match"),
"noFindResult": MessageLookupByLibrary.simpleMessage("No result"),
"numberedList": MessageLookupByLibrary.simpleMessage("Numbered List"), "numberedList": MessageLookupByLibrary.simpleMessage("Numbered List"),
"opacity": MessageLookupByLibrary.simpleMessage("Opacity"), "opacity": MessageLookupByLibrary.simpleMessage("Opacity"),
"openLink": MessageLookupByLibrary.simpleMessage("Open link"), "openLink": MessageLookupByLibrary.simpleMessage("Open link"),
@ -120,6 +123,7 @@ class MessageLookup extends MessageLookupByLibrary {
"previousMatch": MessageLookupByLibrary.simpleMessage("Previous match"), "previousMatch": MessageLookupByLibrary.simpleMessage("Previous match"),
"quote": MessageLookupByLibrary.simpleMessage("Quote"), "quote": MessageLookupByLibrary.simpleMessage("Quote"),
"regex": MessageLookupByLibrary.simpleMessage("Regex"), "regex": MessageLookupByLibrary.simpleMessage("Regex"),
"regexError": MessageLookupByLibrary.simpleMessage("Regex Error"),
"removeLink": MessageLookupByLibrary.simpleMessage("Remove link"), "removeLink": MessageLookupByLibrary.simpleMessage("Remove link"),
"replace": MessageLookupByLibrary.simpleMessage("Replace"), "replace": MessageLookupByLibrary.simpleMessage("Replace"),
"replaceAll": MessageLookupByLibrary.simpleMessage("Replace all"), "replaceAll": MessageLookupByLibrary.simpleMessage("Replace all"),

View file

@ -57,6 +57,7 @@ class MessageLookup extends MessageLookupByLibrary {
"done": MessageLookupByLibrary.simpleMessage("完成"), "done": MessageLookupByLibrary.simpleMessage("完成"),
"editLink": MessageLookupByLibrary.simpleMessage("修改链接"), "editLink": MessageLookupByLibrary.simpleMessage("修改链接"),
"embedCode": MessageLookupByLibrary.simpleMessage("代码块"), "embedCode": MessageLookupByLibrary.simpleMessage("代码块"),
"emptySearchBoxHint": MessageLookupByLibrary.simpleMessage("输入查找内容"),
"find": MessageLookupByLibrary.simpleMessage("查找"), "find": MessageLookupByLibrary.simpleMessage("查找"),
"fontColorBlue": MessageLookupByLibrary.simpleMessage("蓝色"), "fontColorBlue": MessageLookupByLibrary.simpleMessage("蓝色"),
"fontColorBrown": MessageLookupByLibrary.simpleMessage("棕色"), "fontColorBrown": MessageLookupByLibrary.simpleMessage("棕色"),
@ -97,6 +98,7 @@ class MessageLookup extends MessageLookupByLibrary {
"mobileHeading2": MessageLookupByLibrary.simpleMessage("二级标题"), "mobileHeading2": MessageLookupByLibrary.simpleMessage("二级标题"),
"mobileHeading3": MessageLookupByLibrary.simpleMessage("三级标题"), "mobileHeading3": MessageLookupByLibrary.simpleMessage("三级标题"),
"nextMatch": MessageLookupByLibrary.simpleMessage("下一匹配项"), "nextMatch": MessageLookupByLibrary.simpleMessage("下一匹配项"),
"noFindResult": MessageLookupByLibrary.simpleMessage("无匹配项"),
"numberedList": MessageLookupByLibrary.simpleMessage("有序列表"), "numberedList": MessageLookupByLibrary.simpleMessage("有序列表"),
"opacity": MessageLookupByLibrary.simpleMessage("透明度"), "opacity": MessageLookupByLibrary.simpleMessage("透明度"),
"openLink": MessageLookupByLibrary.simpleMessage("打开链接"), "openLink": MessageLookupByLibrary.simpleMessage("打开链接"),
@ -104,6 +106,7 @@ class MessageLookup extends MessageLookupByLibrary {
"previousMatch": MessageLookupByLibrary.simpleMessage("上一匹配项"), "previousMatch": MessageLookupByLibrary.simpleMessage("上一匹配项"),
"quote": MessageLookupByLibrary.simpleMessage("引文"), "quote": MessageLookupByLibrary.simpleMessage("引文"),
"regex": MessageLookupByLibrary.simpleMessage("正则表达式"), "regex": MessageLookupByLibrary.simpleMessage("正则表达式"),
"regexError": MessageLookupByLibrary.simpleMessage("正则错误"),
"removeLink": MessageLookupByLibrary.simpleMessage("移除链接"), "removeLink": MessageLookupByLibrary.simpleMessage("移除链接"),
"replace": MessageLookupByLibrary.simpleMessage("替换"), "replace": MessageLookupByLibrary.simpleMessage("替换"),
"replaceAll": MessageLookupByLibrary.simpleMessage("替换全部"), "replaceAll": MessageLookupByLibrary.simpleMessage("替换全部"),

View file

@ -57,6 +57,7 @@ class MessageLookup extends MessageLookupByLibrary {
"done": MessageLookupByLibrary.simpleMessage("完成"), "done": MessageLookupByLibrary.simpleMessage("完成"),
"editLink": MessageLookupByLibrary.simpleMessage("修改連結"), "editLink": MessageLookupByLibrary.simpleMessage("修改連結"),
"embedCode": MessageLookupByLibrary.simpleMessage("代碼塊"), "embedCode": MessageLookupByLibrary.simpleMessage("代碼塊"),
"emptySearchBoxHint": MessageLookupByLibrary.simpleMessage("鍵入尋找内容"),
"find": MessageLookupByLibrary.simpleMessage("尋找"), "find": MessageLookupByLibrary.simpleMessage("尋找"),
"fontColorBlue": MessageLookupByLibrary.simpleMessage("藍色"), "fontColorBlue": MessageLookupByLibrary.simpleMessage("藍色"),
"fontColorBrown": MessageLookupByLibrary.simpleMessage("棕色"), "fontColorBrown": MessageLookupByLibrary.simpleMessage("棕色"),
@ -97,6 +98,7 @@ class MessageLookup extends MessageLookupByLibrary {
"mobileHeading2": MessageLookupByLibrary.simpleMessage("二級標題"), "mobileHeading2": MessageLookupByLibrary.simpleMessage("二級標題"),
"mobileHeading3": MessageLookupByLibrary.simpleMessage("三級標題"), "mobileHeading3": MessageLookupByLibrary.simpleMessage("三級標題"),
"nextMatch": MessageLookupByLibrary.simpleMessage("下一相符項"), "nextMatch": MessageLookupByLibrary.simpleMessage("下一相符項"),
"noFindResult": MessageLookupByLibrary.simpleMessage("無相符項"),
"numberedList": MessageLookupByLibrary.simpleMessage("有序列表"), "numberedList": MessageLookupByLibrary.simpleMessage("有序列表"),
"opacity": MessageLookupByLibrary.simpleMessage("透明度"), "opacity": MessageLookupByLibrary.simpleMessage("透明度"),
"openLink": MessageLookupByLibrary.simpleMessage("打開連結"), "openLink": MessageLookupByLibrary.simpleMessage("打開連結"),
@ -104,6 +106,7 @@ class MessageLookup extends MessageLookupByLibrary {
"previousMatch": MessageLookupByLibrary.simpleMessage("上一相符項"), "previousMatch": MessageLookupByLibrary.simpleMessage("上一相符項"),
"quote": MessageLookupByLibrary.simpleMessage("引文"), "quote": MessageLookupByLibrary.simpleMessage("引文"),
"regex": MessageLookupByLibrary.simpleMessage("正規表示式"), "regex": MessageLookupByLibrary.simpleMessage("正規表示式"),
"regexError": MessageLookupByLibrary.simpleMessage("正規錯誤"),
"removeLink": MessageLookupByLibrary.simpleMessage("移除連結"), "removeLink": MessageLookupByLibrary.simpleMessage("移除連結"),
"replace": MessageLookupByLibrary.simpleMessage("取代"), "replace": MessageLookupByLibrary.simpleMessage("取代"),
"replaceAll": MessageLookupByLibrary.simpleMessage("取代全部"), "replaceAll": MessageLookupByLibrary.simpleMessage("取代全部"),

View file

@ -961,6 +961,36 @@ class AppFlowyEditorLocalizations {
); );
} }
/// `Regex Error`
String get regexError {
return Intl.message(
'Regex Error',
name: 'regexError',
desc: '',
args: [],
);
}
/// `No result`
String get noFindResult {
return Intl.message(
'No result',
name: 'noFindResult',
desc: '',
args: [],
);
}
/// `Enter a pattern`
String get emptySearchBoxHint {
return Intl.message(
'Enter a pattern',
name: 'emptySearchBoxHint',
desc: '',
args: [],
);
}
/// `Upload Image` /// `Upload Image`
String get uploadImage { String get uploadImage {
return Intl.message( return Intl.message(