|
|
@ -17,19 +17,19 @@ class ContentSearchService { |
|
|
|
final queries = _splitQuery(query); // 分割查询字符串 |
|
|
|
final queries = _splitQuery(query); // 分割查询字符串 |
|
|
|
|
|
|
|
|
|
|
|
for (final q in queries) { |
|
|
|
for (final q in queries) { |
|
|
|
final pattern = _buildSearchPattern( |
|
|
|
final pattern = _buildSearchPattern( |
|
|
|
query: q, |
|
|
|
query: q, |
|
|
|
caseSensitive: caseSensitive, |
|
|
|
caseSensitive: caseSensitive, |
|
|
|
wholeWord: wholeWord, |
|
|
|
wholeWord: wholeWord, |
|
|
|
useRegex: useRegex, |
|
|
|
useRegex: useRegex, |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
await for (final entity in dir.list(recursive: true)) { |
|
|
|
await for (final entity in dir.list(recursive: true)) { |
|
|
|
if (entity is File && _matchesFileType(entity.path, fileType)) { |
|
|
|
if (entity is File && _matchesFileType(entity.path, fileType)) { |
|
|
|
await _searchInFile(entity, pattern, results, q); // 传递当前查询项 |
|
|
|
await _searchInFile(entity, pattern, results, q); // 传递当前查询项 |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return results; |
|
|
|
return results; |
|
|
|
} |
|
|
|
} |
|
|
@ -48,19 +48,19 @@ class ContentSearchService { |
|
|
|
final queries = _splitQuery(query); // 分割查询字符串 |
|
|
|
final queries = _splitQuery(query); // 分割查询字符串 |
|
|
|
|
|
|
|
|
|
|
|
for (final q in queries) { |
|
|
|
for (final q in queries) { |
|
|
|
final pattern = _buildSearchPattern( |
|
|
|
final pattern = _buildSearchPattern( |
|
|
|
query: q, |
|
|
|
query: q, |
|
|
|
caseSensitive: caseSensitive, |
|
|
|
caseSensitive: caseSensitive, |
|
|
|
wholeWord: wholeWord, |
|
|
|
wholeWord: wholeWord, |
|
|
|
useRegex: useRegex, |
|
|
|
useRegex: useRegex, |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
await for (final entity in dir.list(recursive: true)) { |
|
|
|
await for (final entity in dir.list(recursive: true)) { |
|
|
|
if (entity is File && _matchesFileType(entity.path, fileType)) { |
|
|
|
if (entity is File && _matchesFileType(entity.path, fileType)) { |
|
|
|
await _countInFile(entity, pattern, counts, q); // 传递当前查询项 |
|
|
|
await _countInFile(entity, pattern, counts, q); // 传递当前查询项 |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return counts; |
|
|
|
return counts; |
|
|
|
} |
|
|
|
} |
|
|
@ -89,11 +89,76 @@ class ContentSearchService { |
|
|
|
return RegExp(pattern, caseSensitive: caseSensitive, multiLine: true); |
|
|
|
return RegExp(pattern, caseSensitive: caseSensitive, multiLine: true); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 检查文件类型(原逻辑不变) |
|
|
|
|
|
|
|
static bool _matchesFileType(String filePath, String fileType) { |
|
|
|
static bool _matchesFileType(String filePath, String fileType) { |
|
|
|
|
|
|
|
// 处理特殊情况 |
|
|
|
if (fileType == '*.*') return true; |
|
|
|
if (fileType == '*.*') return true; |
|
|
|
final ext = path.extension(filePath).toLowerCase(); |
|
|
|
if (fileType == '*') return true; |
|
|
|
return ext == fileType.toLowerCase(); |
|
|
|
|
|
|
|
|
|
|
|
// 分割通配符为文件名和扩展名部分 |
|
|
|
|
|
|
|
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 参数) |
|
|
|
/// 在文件中搜索匹配项(增加 queryTerm 参数) |
|
|
|