|
|
|
@ -9,6 +9,8 @@ import 'package:win_text_editor/modules/content_search/models/match_result.dart'
@@ -9,6 +9,8 @@ import 'package:win_text_editor/modules/content_search/models/match_result.dart'
|
|
|
|
|
import 'package:win_text_editor/modules/content_search/models/search_mode.dart'; |
|
|
|
|
import 'package:win_text_editor/modules/content_search/models/search_result.dart'; |
|
|
|
|
|
|
|
|
|
typedef ProgressCallback = void Function(double progress); |
|
|
|
|
|
|
|
|
|
class ContentSearchService { |
|
|
|
|
/// 执行定位搜索(返回所有匹配项) |
|
|
|
|
static Future<List<SearchResult>> performLocateSearch({ |
|
|
|
@ -18,29 +20,57 @@ class ContentSearchService {
@@ -18,29 +20,57 @@ class ContentSearchService {
|
|
|
|
|
required bool caseSensitive, |
|
|
|
|
required bool wholeWord, |
|
|
|
|
required bool useRegex, |
|
|
|
|
ProgressCallback? onProgress, |
|
|
|
|
bool Function()? shouldStop, |
|
|
|
|
}) async { |
|
|
|
|
final results = <SearchResult>[]; |
|
|
|
|
final dir = Directory(directory); |
|
|
|
|
final queries = _splitQuery(query); // 分割查询字符串 |
|
|
|
|
int speed = 0; |
|
|
|
|
final queries = _splitQuery(query); |
|
|
|
|
|
|
|
|
|
// 先统计文件总数 |
|
|
|
|
int totalFiles = 0; |
|
|
|
|
await for (final entity in dir.list(recursive: true)) { |
|
|
|
|
if (shouldStop?.call() == true) return results; |
|
|
|
|
if (entity is File && _matchesFileType(entity.path, fileType)) { |
|
|
|
|
totalFiles++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
onProgress?.call(1); |
|
|
|
|
|
|
|
|
|
int words = 0; |
|
|
|
|
int processedFiles = 0; |
|
|
|
|
int oldProgress = 1; |
|
|
|
|
for (final q in queries) { |
|
|
|
|
if (shouldStop?.call() == true) return results; |
|
|
|
|
|
|
|
|
|
final pattern = _buildSearchPattern( |
|
|
|
|
query: q, |
|
|
|
|
caseSensitive: caseSensitive, |
|
|
|
|
wholeWord: wholeWord, |
|
|
|
|
useRegex: useRegex, |
|
|
|
|
); |
|
|
|
|
Logger().info("搜索词 $q 开始[${++speed}/${queries.length}]"); |
|
|
|
|
int fileCount = 0; |
|
|
|
|
|
|
|
|
|
Logger().info("搜索词 $q 开始[${++words}/${queries.length}]"); |
|
|
|
|
|
|
|
|
|
await for (final entity in dir.list(recursive: true)) { |
|
|
|
|
if (shouldStop?.call() == true) return results; |
|
|
|
|
|
|
|
|
|
if (entity is File && _matchesFileType(entity.path, fileType)) { |
|
|
|
|
fileCount++; |
|
|
|
|
await _searchInFile(entity, pattern, results, q); // 传递当前查询项 |
|
|
|
|
processedFiles++; |
|
|
|
|
final progress = (processedFiles / (totalFiles * queries.length)) * 99 + 1; |
|
|
|
|
if (progress - oldProgress >= 1) { |
|
|
|
|
oldProgress = progress.floor(); |
|
|
|
|
onProgress?.call(progress); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
await _searchInFile(entity, pattern, results, q); |
|
|
|
|
} |
|
|
|
|
Logger().info("搜索词 $q 结束[$speed/${queries.length}],搜索文件 $fileCount 个,"); |
|
|
|
|
} |
|
|
|
|
Logger().info("搜索词 $q 结束[$words/${queries.length}],搜索文件 $totalFiles 个,"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
onProgress?.call(100); |
|
|
|
|
return results; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -52,12 +82,29 @@ class ContentSearchService {
@@ -52,12 +82,29 @@ class ContentSearchService {
|
|
|
|
|
required bool caseSensitive, |
|
|
|
|
required bool wholeWord, |
|
|
|
|
required bool useRegex, |
|
|
|
|
ProgressCallback? onProgress, |
|
|
|
|
bool Function()? shouldStop, |
|
|
|
|
}) async { |
|
|
|
|
final counts = <String, int>{}; |
|
|
|
|
final dir = Directory(directory); |
|
|
|
|
final queries = _splitQuery(query); // 分割查询字符串 |
|
|
|
|
int speed = 0; |
|
|
|
|
|
|
|
|
|
int totalFiles = 0; |
|
|
|
|
await for (final entity in dir.list(recursive: true)) { |
|
|
|
|
if (shouldStop?.call() == true) return counts; |
|
|
|
|
if (entity is File && _matchesFileType(entity.path, fileType)) { |
|
|
|
|
totalFiles++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
onProgress?.call(1); |
|
|
|
|
|
|
|
|
|
int words = 0; |
|
|
|
|
int processedFiles = 0; |
|
|
|
|
int oldProgress = 1; |
|
|
|
|
for (final q in queries) { |
|
|
|
|
if (shouldStop?.call() == true) return counts; |
|
|
|
|
|
|
|
|
|
final pattern = _buildSearchPattern( |
|
|
|
|
query: q, |
|
|
|
|
caseSensitive: caseSensitive, |
|
|
|
@ -65,17 +112,25 @@ class ContentSearchService {
@@ -65,17 +112,25 @@ class ContentSearchService {
|
|
|
|
|
useRegex: useRegex, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
Logger().info("搜索词 $q 开始[${++speed}/${queries.length}]"); |
|
|
|
|
Logger().info("搜索词 $q 开始[${++words}/${queries.length}]"); |
|
|
|
|
counts[q] = 0; |
|
|
|
|
int fileCount = 0; |
|
|
|
|
|
|
|
|
|
await for (final entity in dir.list(recursive: true)) { |
|
|
|
|
if (shouldStop?.call() == true) return counts; |
|
|
|
|
|
|
|
|
|
if (entity is File && _matchesFileType(entity.path, fileType)) { |
|
|
|
|
fileCount++; |
|
|
|
|
processedFiles++; |
|
|
|
|
final progress = (processedFiles / (totalFiles * queries.length)) * 99 + 1; |
|
|
|
|
if (progress - oldProgress >= 1) { |
|
|
|
|
oldProgress = progress.floor(); |
|
|
|
|
onProgress?.call(progress); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
await _countInFile(entity, pattern, counts, q); // 传递当前查询项 |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Logger().info("搜索词 $q 结束[$speed/${queries.length}],搜索文件 $fileCount 个,"); |
|
|
|
|
Logger().info("搜索词 $q 结束[$words/${queries.length}],搜索文件 $totalFiles 个,"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return counts; |
|
|
|
@ -87,6 +142,8 @@ class ContentSearchService {
@@ -87,6 +142,8 @@ class ContentSearchService {
|
|
|
|
|
required String fileType, |
|
|
|
|
required String jsFunction, |
|
|
|
|
required SearchMode searchMode, |
|
|
|
|
ProgressCallback? onProgress, |
|
|
|
|
bool Function()? shouldStop, |
|
|
|
|
}) async { |
|
|
|
|
final results = <SearchResult>[]; |
|
|
|
|
int count = 0; |
|
|
|
@ -99,11 +156,26 @@ class ContentSearchService {
@@ -99,11 +156,26 @@ class ContentSearchService {
|
|
|
|
|
|
|
|
|
|
jsRuntime.evaluate(jsCode); |
|
|
|
|
|
|
|
|
|
int totalFiles = 0; |
|
|
|
|
await for (final entity in dir.list(recursive: true)) { |
|
|
|
|
if (shouldStop?.call() == true) return results; |
|
|
|
|
if (entity is File && _matchesFileType(entity.path, fileType)) { |
|
|
|
|
totalFiles++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
onProgress?.call(1); |
|
|
|
|
|
|
|
|
|
int processedFiles = 0; |
|
|
|
|
int calledProcessedFiles = 1; |
|
|
|
|
|
|
|
|
|
await for (final entity in dir.list(recursive: true)) { |
|
|
|
|
if (shouldStop?.call() == true) return results; |
|
|
|
|
if (entity is File && _matchesFileType(entity.path, fileType)) { |
|
|
|
|
try { |
|
|
|
|
final lines = await entity.readAsLines(); |
|
|
|
|
for (int i = 0; i < lines.length; i++) { |
|
|
|
|
if (shouldStop?.call() == true) return results; |
|
|
|
|
final line = lines[i].trim(); |
|
|
|
|
if (line.length < 3) continue; // 跳过短行 |
|
|
|
|
|
|
|
|
@ -131,6 +203,13 @@ class ContentSearchService {
@@ -131,6 +203,13 @@ class ContentSearchService {
|
|
|
|
|
} catch (e) { |
|
|
|
|
Logger().error('Error in file ${entity.path}: $e'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
processedFiles++; |
|
|
|
|
final progress = (processedFiles / totalFiles) * 99 + 1; |
|
|
|
|
if (processedFiles - calledProcessedFiles >= 1) { |
|
|
|
|
calledProcessedFiles = processedFiles; |
|
|
|
|
onProgress?.call(progress); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -147,6 +226,7 @@ class ContentSearchService {
@@ -147,6 +226,7 @@ class ContentSearchService {
|
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} finally { |
|
|
|
|
onProgress?.call(100); |
|
|
|
|
jsRuntime.dispose(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|