From f8a70811494beb0c998eaaf794fee8a80e4abb04 Mon Sep 17 00:00:00 2001 From: hejl Date: Fri, 16 May 2025 15:51:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=AD=E6=96=87=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/app/components/text_editor.dart | 33 ++++-- .../content_search_service.dart | 103 ++++++++++++++---- 2 files changed, 108 insertions(+), 28 deletions(-) diff --git a/win_text_editor/lib/app/components/text_editor.dart b/win_text_editor/lib/app/components/text_editor.dart index 7a046c4..eed6cbd 100644 --- a/win_text_editor/lib/app/components/text_editor.dart +++ b/win_text_editor/lib/app/components/text_editor.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -29,6 +30,8 @@ class TextEditorState extends State with AutomaticKeepAliveClientMix final TextEditingController _textController = TextEditingController(); final FocusNode _focusNode = FocusNode(); bool _isLoading = false; + String _lastSyncedText = ''; + Timer? _debounceTimer; @override bool get wantKeepAlive => true; @@ -36,26 +39,38 @@ class TextEditorState extends State with AutomaticKeepAliveClientMix @override void initState() { super.initState(); - _textController.text = widget.initialContent ?? ''; + _lastSyncedText = widget.initialContent ?? ''; + _textController.text = _lastSyncedText; _textController.addListener(_handleTextChanged); } - @override - void didUpdateWidget(TextEditor oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.initialContent != widget.initialContent) { - _textController.text = widget.initialContent ?? ''; + void _handleTextChanged() { + if (_debounceTimer?.isActive ?? false) { + _debounceTimer?.cancel(); } + + _debounceTimer = Timer(const Duration(milliseconds: 300), () { + final currentText = _textController.text; + if (widget.onContentChanged != null && currentText != _lastSyncedText) { + _lastSyncedText = currentText; + widget.onContentChanged!(currentText); + } + }); } - void _handleTextChanged() { - if (widget.onContentChanged != null) { - widget.onContentChanged!(_textController.text); + @override + void didUpdateWidget(TextEditor oldWidget) { + super.didUpdateWidget(oldWidget); + final newText = widget.initialContent ?? ''; + if (newText != _textController.text && newText != _lastSyncedText) { + _lastSyncedText = newText; + _textController.text = newText; } } @override void dispose() { + _debounceTimer?.cancel(); _textController.removeListener(_handleTextChanged); _textController.dispose(); _focusNode.dispose(); diff --git a/win_text_editor/lib/app/modules/content_search/content_search_service.dart b/win_text_editor/lib/app/modules/content_search/content_search_service.dart index 1a23c2c..cfef201 100644 --- a/win_text_editor/lib/app/modules/content_search/content_search_service.dart +++ b/win_text_editor/lib/app/modules/content_search/content_search_service.dart @@ -17,19 +17,19 @@ class ContentSearchService { final queries = _splitQuery(query); // 分割查询字符串 for (final q in queries) { - final pattern = _buildSearchPattern( + final pattern = _buildSearchPattern( query: q, - caseSensitive: caseSensitive, - wholeWord: wholeWord, - useRegex: useRegex, - ); + caseSensitive: caseSensitive, + wholeWord: wholeWord, + useRegex: useRegex, + ); - await for (final entity in dir.list(recursive: true)) { - if (entity is File && _matchesFileType(entity.path, fileType)) { + await for (final entity in dir.list(recursive: true)) { + if (entity is File && _matchesFileType(entity.path, fileType)) { await _searchInFile(entity, pattern, results, q); // 传递当前查询项 + } } } - } return results; } @@ -48,19 +48,19 @@ class ContentSearchService { final queries = _splitQuery(query); // 分割查询字符串 for (final q in queries) { - final pattern = _buildSearchPattern( + final pattern = _buildSearchPattern( query: q, - caseSensitive: caseSensitive, - wholeWord: wholeWord, - useRegex: useRegex, - ); + caseSensitive: caseSensitive, + wholeWord: wholeWord, + useRegex: useRegex, + ); - await for (final entity in dir.list(recursive: true)) { - if (entity is File && _matchesFileType(entity.path, fileType)) { + await for (final entity in dir.list(recursive: true)) { + if (entity is File && _matchesFileType(entity.path, fileType)) { await _countInFile(entity, pattern, counts, q); // 传递当前查询项 + } } } - } return counts; } @@ -89,11 +89,76 @@ class ContentSearchService { return RegExp(pattern, caseSensitive: caseSensitive, multiLine: true); } - /// 检查文件类型(原逻辑不变) static bool _matchesFileType(String filePath, String fileType) { + // 处理特殊情况 if (fileType == '*.*') return true; - final ext = path.extension(filePath).toLowerCase(); - return ext == fileType.toLowerCase(); + if (fileType == '*') return true; + + // 分割通配符为文件名和扩展名部分 + final parts = fileType.split('.'); + final patternName = parts.length > 0 ? parts[0] : ''; + final patternExt = parts.length > 1 ? parts.sublist(1).join('.') : ''; + + // 获取文件的实际名称和扩展名 + final fileName = path.basename(filePath); + final fileExt = path.extension(fileName).toLowerCase().replaceFirst('.', ''); + + // 匹配文件名部分(如果有指定) + bool nameMatches = true; + if (patternName.isNotEmpty && patternName != '*') { + nameMatches = _matchesWildcard(fileName, patternName); + } + + // 匹配扩展名部分(如果有指定) + bool extMatches = true; + if (patternExt.isNotEmpty) { + if (patternExt == '*') { + extMatches = true; + } else { + extMatches = _matchesWildcard(fileExt, patternExt); + } + } + + return nameMatches && extMatches; + } + + // 辅助函数:判断字符串是否匹配通配符模式 + static bool _matchesWildcard(String input, String pattern) { + // 处理特殊情况 + if (pattern == '*') return true; + + // 动态规划实现通配符匹配 + final m = input.length; + final n = pattern.length; + final dp = List.generate(m + 1, (_) => List.filled(n + 1, false)); + + // 空字符串匹配空模式 + dp[0][0] = true; + + // 处理模式以 * 开头的情况 + for (int j = 1; j <= n; j++) { + if (pattern[j - 1] == '*') { + dp[0][j] = dp[0][j - 1]; + } + } + + // 填充动态规划表 + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (pattern[j - 1] == '*') { + // * 可以匹配任意字符或空字符 + dp[i][j] = dp[i][j - 1] || dp[i - 1][j]; + } else if (pattern[j - 1] == '?' || input[i - 1] == pattern[j - 1]) { + // ? 匹配单个字符,或者字符直接相等 + dp[i][j] = dp[i - 1][j - 1]; + } else { + // 不匹配 + dp[i][j] = false; + } + } + } + + return dp[m][n]; } /// 在文件中搜索匹配项(增加 queryTerm 参数)