|
|
|
@ -1,9 +1,14 @@
@@ -1,9 +1,14 @@
|
|
|
|
|
// lib/app/modules/content_search/content_search_service.dart |
|
|
|
|
|
|
|
|
|
import 'dart:convert'; |
|
|
|
|
import 'dart:io'; |
|
|
|
|
import 'package:flutter_js/flutter_js.dart'; |
|
|
|
|
import 'package:path/path.dart' as path; |
|
|
|
|
import 'package:win_text_editor/app/models/search_model.dart'; |
|
|
|
|
import 'package:win_text_editor/app/providers/logger.dart'; |
|
|
|
|
|
|
|
|
|
class ContentSearchService { |
|
|
|
|
/// 执行定位搜索(返回所有匹配项) |
|
|
|
|
static Future<List<SearchResult>> performLocateSearch({ |
|
|
|
|
required String directory, |
|
|
|
|
required String query, |
|
|
|
@ -65,6 +70,78 @@ class ContentSearchService {
@@ -65,6 +70,78 @@ class ContentSearchService {
|
|
|
|
|
return counts; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// 新增方法:执行自定义 JavaScript 规则搜索 |
|
|
|
|
static Future<List<SearchResult>> performCustomSearch({ |
|
|
|
|
required String directory, |
|
|
|
|
required String fileType, |
|
|
|
|
required String jsFunction, |
|
|
|
|
required SearchMode searchMode, |
|
|
|
|
}) async { |
|
|
|
|
final results = <SearchResult>[]; |
|
|
|
|
int count = 0; |
|
|
|
|
final dir = Directory(directory); |
|
|
|
|
final jsRuntime = getJavascriptRuntime(); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
// 定义 JavaScript 函数 |
|
|
|
|
final jsCode = 'function match(content){$jsFunction};'; |
|
|
|
|
|
|
|
|
|
jsRuntime.evaluate(jsCode); |
|
|
|
|
|
|
|
|
|
await for (final entity in dir.list(recursive: true)) { |
|
|
|
|
if (entity is File && _matchesFileType(entity.path, fileType)) { |
|
|
|
|
try { |
|
|
|
|
final lines = await entity.readAsLines(); |
|
|
|
|
for (int i = 0; i < lines.length; i++) { |
|
|
|
|
final line = lines[i].trim(); |
|
|
|
|
if (line.length < 3) continue; // 跳过短行 |
|
|
|
|
|
|
|
|
|
final result = jsRuntime.evaluate('match(${jsonEncode(line)});'); |
|
|
|
|
if (result.isError) { |
|
|
|
|
throw Exception('JS Error: ${result.stringResult}'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (result.stringResult == 'true') { |
|
|
|
|
if (searchMode == SearchMode.locate) { |
|
|
|
|
results.add( |
|
|
|
|
SearchResult( |
|
|
|
|
filePath: entity.path, |
|
|
|
|
lineNumber: i + 1, |
|
|
|
|
lineContent: line, |
|
|
|
|
matches: [], |
|
|
|
|
queryTerm: "Custom Rule", |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
|
} else { |
|
|
|
|
count++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} catch (e) { |
|
|
|
|
Logger().error('Error in file ${entity.path}: $e'); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 处理计数模式结果 |
|
|
|
|
if (searchMode == SearchMode.count) { |
|
|
|
|
results.add( |
|
|
|
|
SearchResult( |
|
|
|
|
filePath: "Custom Rule", |
|
|
|
|
lineNumber: count, |
|
|
|
|
lineContent: '', |
|
|
|
|
matches: [], |
|
|
|
|
queryTerm: "Custom Rule", |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} finally { |
|
|
|
|
jsRuntime.dispose(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return results; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// 分割查询字符串(按半角逗号分隔,并去除空格) |
|
|
|
|
static List<String> _splitQuery(String query) { |
|
|
|
|
return query.split(',').map((q) => q.trim()).where((q) => q.isNotEmpty).toList(); |
|
|
|
@ -206,31 +283,7 @@ class ContentSearchService {
@@ -206,31 +283,7 @@ class ContentSearchService {
|
|
|
|
|
counts[queryTerm] = (counts[queryTerm] ?? 0) + matches.length; |
|
|
|
|
} |
|
|
|
|
} catch (e) { |
|
|
|
|
print('Error reading file ${file.path}: $e'); |
|
|
|
|
Logger().error('Error reading file ${file.path}: $e'); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// 搜索结果类(新增 queryTerm 字段) |
|
|
|
|
class SearchResult { |
|
|
|
|
final String filePath; |
|
|
|
|
final int lineNumber; |
|
|
|
|
final String lineContent; |
|
|
|
|
final List<MatchResult> matches; |
|
|
|
|
final String queryTerm; // 记录匹配的查询项 |
|
|
|
|
|
|
|
|
|
SearchResult({ |
|
|
|
|
required this.filePath, |
|
|
|
|
required this.lineNumber, |
|
|
|
|
required this.lineContent, |
|
|
|
|
required this.matches, |
|
|
|
|
required this.queryTerm, |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class MatchResult { |
|
|
|
|
final int start; |
|
|
|
|
final int end; |
|
|
|
|
|
|
|
|
|
const MatchResult({required this.start, required this.end}); |
|
|
|
|
} |
|
|
|
|