autosnippet 3.0.11 → 3.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/bin/cli.js +64 -1
  2. package/config/default.json +9 -0
  3. package/dashboard/dist/assets/{index-I2ySoCmF.js → index-Bnm26ulL.js} +47 -47
  4. package/dashboard/dist/index.html +1 -1
  5. package/lib/cli/SetupService.js +92 -5
  6. package/lib/cli/UpgradeService.js +14 -5
  7. package/lib/core/discovery/GenericDiscoverer.js +4 -28
  8. package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +246 -0
  9. package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +80 -0
  10. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +275 -0
  11. package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +600 -0
  12. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +125 -342
  13. package/lib/external/mcp/handlers/bootstrap/refine.js +362 -0
  14. package/lib/external/mcp/handlers/bootstrap.js +6 -590
  15. package/lib/external/mcp/handlers/browse.js +119 -9
  16. package/lib/external/mcp/handlers/guard.js +25 -6
  17. package/lib/external/mcp/handlers/search.js +56 -24
  18. package/lib/http/routes/guardRules.js +9 -17
  19. package/lib/injection/ServiceContainer.js +12 -3
  20. package/lib/platform/ios/xcode/XcodeImportResolver.js +434 -0
  21. package/lib/platform/ios/xcode/XcodeIntegration.js +40 -659
  22. package/lib/platform/ios/xcode/XcodeWriteUtils.js +220 -0
  23. package/lib/service/chat/ChatAgent.js +39 -418
  24. package/lib/service/chat/ChatAgentPrompts.js +149 -0
  25. package/lib/service/chat/ChatAgentTasks.js +297 -0
  26. package/lib/service/chat/tools/_shared.js +61 -0
  27. package/lib/service/chat/tools/ai-analysis.js +284 -0
  28. package/lib/service/chat/tools/ast-graph.js +681 -0
  29. package/lib/service/chat/tools/composite.js +496 -0
  30. package/lib/service/chat/tools/guard.js +265 -0
  31. package/lib/service/chat/tools/index.js +250 -0
  32. package/lib/service/chat/tools/infrastructure.js +222 -0
  33. package/lib/service/chat/tools/knowledge-graph.js +234 -0
  34. package/lib/service/chat/tools/lifecycle.js +469 -0
  35. package/lib/service/chat/tools/project-access.js +923 -0
  36. package/lib/service/chat/tools/query.js +264 -0
  37. package/lib/service/chat/tools.js +14 -3994
  38. package/lib/service/cursor/AgentInstructionsGenerator.js +395 -0
  39. package/lib/service/cursor/CursorDeliveryPipeline.js +70 -11
  40. package/lib/service/cursor/FileProtection.js +116 -0
  41. package/lib/service/cursor/KnowledgeCompressor.js +61 -11
  42. package/lib/service/cursor/SkillsSyncer.js +5 -3
  43. package/lib/service/cursor/TopicClassifier.js +19 -3
  44. package/lib/service/guard/ExclusionManager.js +26 -2
  45. package/lib/service/guard/GuardCheckEngine.js +38 -370
  46. package/lib/service/guard/GuardCodeChecks.js +362 -0
  47. package/lib/service/guard/GuardCrossFileChecks.js +307 -0
  48. package/lib/service/guard/GuardPatternUtils.js +180 -0
  49. package/lib/service/guard/GuardService.js +80 -38
  50. package/lib/service/module/ModuleService.js +1 -0
  51. package/lib/service/search/SearchEngine.js +10 -2
  52. package/lib/service/wiki/WikiGenerator.js +226 -1532
  53. package/lib/service/wiki/WikiRenderers.js +1878 -0
  54. package/lib/service/wiki/WikiUtils.js +907 -0
  55. package/lib/shared/LanguageService.js +299 -0
  56. package/package.json +1 -1
@@ -8,6 +8,9 @@
8
8
  import * as AstAnalyzerModule from '../../core/AstAnalyzer.js';
9
9
  import Logger from '../../infrastructure/logging/Logger.js';
10
10
  import { LanguageService } from '../../shared/LanguageService.js';
11
+ import { compilePattern, clearPatternCache, buildTestBlockMask, buildCommentMask, detectLanguage } from './GuardPatternUtils.js';
12
+ import { runCodeLevelChecks } from './GuardCodeChecks.js';
13
+ import { runCrossFileChecks } from './GuardCrossFileChecks.js';
11
14
 
12
15
  /**
13
16
  * 内置默认规则集 — 多语言基础规则
@@ -454,17 +457,8 @@ const BUILT_IN_RULES = {
454
457
 
455
458
  };
456
459
 
457
- /**
458
- * 从文件扩展名推断语言
459
- */
460
- export function detectLanguage(filePath) {
461
- if (!filePath) {
462
- return 'unknown';
463
- }
464
- const lang = LanguageService.inferLang(filePath);
465
- // 向后兼容: objectivec → objc
466
- return lang === 'objectivec' ? 'objc' : lang;
467
- }
460
+ // 向后兼容: 从 GuardPatternUtils 重新导出 detectLanguage
461
+ export { detectLanguage } from './GuardPatternUtils.js';
468
462
 
469
463
  /**
470
464
  * GuardCheckEngine - 核心检查引擎
@@ -480,8 +474,13 @@ export class GuardCheckEngine {
480
474
  this._cacheTTL = options.cacheTTL || 60_000; // 1min
481
475
  /** @type {Map<string, object>} Enhancement Pack 注入的外部规则 */
482
476
  this._externalRules = new Map();
483
- /** @type {Map<string, RegExp>} 已编译的正则缓存 (pattern string → RegExp) */
484
- this._regexCache = new Map();
477
+ /** @type {boolean} EP 规则是否已注入(幂等标记,避免每次请求重复注入) */
478
+ this._epInjected = false;
479
+ /**
480
+ * Guard 配置 — 允许禁用特定规则或调整 Code-Level 检查阈值
481
+ * @type {{ disabledRules?: string[], codeLevelThresholds?: Record<string, number> }}
482
+ */
483
+ this._guardConfig = options.guardConfig || {};
485
484
  }
486
485
 
487
486
  /**
@@ -493,6 +492,8 @@ export class GuardCheckEngine {
493
492
  if (!Array.isArray(rules)) return;
494
493
  for (const rule of rules) {
495
494
  if (!rule.ruleId) continue;
495
+ // 已注入的 ruleId 跳过(幂等)
496
+ if (this._externalRules.has(rule.ruleId)) continue;
496
497
  // 跳过与 BUILT_IN_RULES 重复的模式(通过比较 pattern 源文本)
497
498
  const rulePatternStr = rule.pattern instanceof RegExp ? rule.pattern.source : String(rule.pattern || '');
498
499
  const isDuplicate = Object.entries(this._builtInRules).some(([, builtIn]) => {
@@ -519,6 +520,12 @@ export class GuardCheckEngine {
519
520
  this.logger.debug(`[GuardCheckEngine] External rules injected: ${this._externalRules.size} active`);
520
521
  }
521
522
 
523
+ /**
524
+ * EP 注入幂等标记 — 调用者可用此判断是否已完成注入,避免重复加载 EnhancementRegistry
525
+ */
526
+ isEpInjected() { return this._epInjected; }
527
+ markEpInjected() { this._epInjected = true; }
528
+
522
529
  /**
523
530
  * 获取所有启用的规则 (数据库 + 内置)
524
531
  */
@@ -622,6 +629,13 @@ export class GuardCheckEngine {
622
629
  rules = rules.filter((r) => !r.languages?.length || r.languages.includes(language));
623
630
  }
624
631
 
632
+ // 按 disabledRules 配置过滤
633
+ const disabledRules = this._guardConfig.disabledRules;
634
+ if (Array.isArray(disabledRules) && disabledRules.length > 0) {
635
+ const disabledSet = new Set(disabledRules);
636
+ rules = rules.filter((r) => !disabledSet.has(r.id || r.name));
637
+ }
638
+
625
639
  // 合并 AST 规则(供外部调用者使用,如 GuardFeedbackLoop.查找 fixSuggestion)
626
640
  if (this._astRulesCache?.length) {
627
641
  let astRules = this._astRulesCache;
@@ -673,11 +687,11 @@ export class GuardCheckEngine {
673
687
 
674
688
  // 预计算注释行掩码 — 供 skipComments 规则使用
675
689
  // 识别: // 行注释, /// doc, //! inner doc, /* block */, # Python/Shell 行注释
676
- const commentLines = this._buildCommentMask(lines, language);
690
+ const commentLines = buildCommentMask(lines, language);
677
691
 
678
692
  // 预计算测试块掩码 — 供 skipTestBlocks 规则使用
679
693
  // Rust: #[cfg(test)] mod tests { ... } 内联测试模块
680
- const testBlockLines = this._buildTestBlockMask(lines, language);
694
+ const testBlockLines = buildTestBlockMask(lines, language);
681
695
 
682
696
  for (const rule of rules) {
683
697
  // 跳过空模式或特殊标记 (?!) — 由 code-level 检查接管
@@ -687,7 +701,7 @@ export class GuardCheckEngine {
687
701
 
688
702
  let re;
689
703
  try {
690
- re = this._compilePattern(rule.pattern);
704
+ re = compilePattern(rule.pattern);
691
705
  } catch {
692
706
  this.logger.debug(`Invalid regex in rule ${rule.id}: ${rule.pattern}`);
693
707
  continue;
@@ -721,7 +735,10 @@ export class GuardCheckEngine {
721
735
  }
722
736
 
723
737
  // Code-level 检查(不依赖正则)
724
- violations.push(...this._runCodeLevelChecks(code, language, lines));
738
+ violations.push(...runCodeLevelChecks(code, language, lines, {
739
+ disabledRules: this._guardConfig.disabledRules,
740
+ codeLevelThresholds: this._guardConfig.codeLevelThresholds,
741
+ }));
725
742
 
726
743
  // AST 语义规则检查
727
744
  violations.push(...this._runAstRuleChecks(code, language));
@@ -927,135 +944,6 @@ export class GuardCheckEngine {
927
944
  }
928
945
  }
929
946
 
930
- /**
931
- * 代码级别检查 - 需要上下文理解的检查(跨行 / 配对检查)
932
- * 按语言分发到各自的检查逻辑
933
- */
934
- _runCodeLevelChecks(code, language, lines) {
935
- const violations = [];
936
-
937
- // ── ObjC ──
938
- if (language === 'objc') {
939
- // KVO 观察者未移除检查
940
- if (code.includes('addObserver') && !code.includes('removeObserver')) {
941
- const lineIdx = lines.findIndex((l) => /addObserver/.test(l));
942
- violations.push({
943
- ruleId: 'objc-kvo-missing-remove',
944
- message: '存在 addObserver 未发现配对 removeObserver,请在 dealloc 或合适时机移除',
945
- severity: 'warning',
946
- line: lineIdx >= 0 ? lineIdx + 1 : 1,
947
- snippet: lineIdx >= 0 ? lines[lineIdx].trim().slice(0, 120) : '',
948
- dimension: 'file',
949
- });
950
- }
951
-
952
- // ObjC Category 重名检查 (同文件)
953
- const categoryRegex = /@interface\s+(\w+)\s*\(\s*(\w+)\s*\)/g;
954
- const categories = {};
955
- for (let i = 0; i < lines.length; i++) {
956
- categoryRegex.lastIndex = 0;
957
- const m = categoryRegex.exec(lines[i]);
958
- if (!m) {
959
- continue;
960
- }
961
- const key = `${m[1]}(${m[2]})`;
962
- if (!categories[key]) {
963
- categories[key] = [];
964
- }
965
- categories[key].push({ line: i + 1, snippet: lines[i].trim().slice(0, 120) });
966
- }
967
- for (const [key, occs] of Object.entries(categories)) {
968
- if (occs.length <= 1) {
969
- continue;
970
- }
971
- for (let j = 1; j < occs.length; j++) {
972
- violations.push({
973
- ruleId: 'objc-duplicate-category',
974
- message: `同文件内 Category 重名:${key},首次在第 ${occs[0].line} 行`,
975
- severity: 'warning',
976
- line: occs[j].line,
977
- snippet: occs[j].snippet,
978
- dimension: 'file',
979
- });
980
- }
981
- }
982
- }
983
-
984
- // ── JavaScript / TypeScript ──
985
- if (language === 'javascript' || language === 'typescript') {
986
- // Promise 未处理 rejection 检查
987
- // 文件中存在 .then() 但没有对应的 .catch() 或 try-catch
988
- if (code.includes('.then(') && !code.includes('.catch(') && !code.includes('try')) {
989
- const thenLines = [];
990
- for (let i = 0; i < lines.length; i++) {
991
- if (/\.then\s*\(/.test(lines[i])) {
992
- thenLines.push(i);
993
- }
994
- }
995
- if (thenLines.length > 0) {
996
- violations.push({
997
- ruleId: 'js-unhandled-promise',
998
- message: 'Promise 链缺少 .catch() 错误处理,未捕获的 rejection 可能导致静默失败',
999
- severity: 'warning',
1000
- line: thenLines[0] + 1,
1001
- snippet: lines[thenLines[0]].trim().slice(0, 120),
1002
- dimension: 'file',
1003
- });
1004
- }
1005
- }
1006
- }
1007
-
1008
- // ── Go ──
1009
- if (language === 'go') {
1010
- // defer 在循环内检查 — defer 在函数结束时才执行,循环内 defer 可能资源泄露
1011
- let inLoop = false;
1012
- for (let i = 0; i < lines.length; i++) {
1013
- const trimmed = lines[i].trim();
1014
- if (/^for\s/.test(trimmed) || /^for\s*\{/.test(trimmed)) {
1015
- inLoop = true;
1016
- }
1017
- if (inLoop && /^\s*defer\s/.test(lines[i])) {
1018
- violations.push({
1019
- ruleId: 'go-defer-in-loop',
1020
- message: 'defer 在循环内会延迟到函数返回时才执行,可能导致资源泄露或大量堆积',
1021
- severity: 'warning',
1022
- line: i + 1,
1023
- snippet: lines[i].trim().slice(0, 120),
1024
- dimension: 'file',
1025
- fixSuggestion: '将循环体提取到独立函数中,或手动调用 Close()',
1026
- });
1027
- }
1028
- // 简化: 遇到 } 且缩进回到顶层,认为循环结束
1029
- if (inLoop && trimmed === '}' && (lines[i].match(/^\t/) || lines[i].match(/^}/))) {
1030
- inLoop = false;
1031
- }
1032
- }
1033
- }
1034
-
1035
- // ── Python ──
1036
- if (language === 'python') {
1037
- // 文件中同时存在 tab 和 space 缩进
1038
- let hasTab = false;
1039
- let hasSpace = false;
1040
- for (let i = 0; i < Math.min(lines.length, 200); i++) {
1041
- if (/^\t/.test(lines[i])) hasTab = true;
1042
- if (/^ {2,}/.test(lines[i]) && !/^\t/.test(lines[i])) hasSpace = true;
1043
- }
1044
- if (hasTab && hasSpace) {
1045
- violations.push({
1046
- ruleId: 'py-mixed-indentation',
1047
- message: '文件混用 tab 和 space 缩进,Python 对此敏感,请统一使用 space',
1048
- severity: 'warning',
1049
- line: 1,
1050
- snippet: '',
1051
- dimension: 'file',
1052
- });
1053
- }
1054
- }
1055
-
1056
- return violations;
1057
- }
1058
-
1059
947
  /**
1060
948
  * 文件审计 - 读取文件并检查
1061
949
  * @param {string} filePath - 绝对路径
@@ -1096,7 +984,9 @@ export class GuardCheckEngine {
1096
984
  }
1097
985
 
1098
986
  // ── 跨文件检查 ──
1099
- const crossFileViolations = this._runCrossFileChecks(files);
987
+ const crossFileViolations = runCrossFileChecks(files, {
988
+ disabledRules: this._guardConfig.disabledRules,
989
+ });
1100
990
  totalViolations += crossFileViolations.length;
1101
991
  totalErrors += crossFileViolations.filter((v) => v.severity === 'error').length;
1102
992
 
@@ -1112,235 +1002,13 @@ export class GuardCheckEngine {
1112
1002
  };
1113
1003
  }
1114
1004
 
1115
- /**
1116
- * 跨文件检查 — 需要多文件上下文才能发现的问题
1117
- * @param {Array<{path: string, content: string}>} files
1118
- * @returns {Array<{ruleId, message, severity, locations}>}
1119
- */
1120
- _runCrossFileChecks(files) {
1121
- const violations = [];
1122
-
1123
- // ── ObjC Category 跨文件重名检查 ──
1124
- // 收集所有文件中的 @interface ClassName(CategoryName) 声明
1125
- const categoryMap = new Map(); // key: "ClassName(CategoryName)" → [{filePath, line, snippet}]
1126
- const categoryRegex = /@interface\s+(\w+)\s*\(\s*(\w+)\s*\)/g;
1127
-
1128
- for (const { path: filePath, content } of files) {
1129
- const ext = filePath.split('.').pop()?.toLowerCase();
1130
- if (ext !== 'm' && ext !== 'mm' && ext !== 'h') {
1131
- continue;
1132
- }
1133
-
1134
- const lines = content.split(/\r?\n/);
1135
- for (let i = 0; i < lines.length; i++) {
1136
- categoryRegex.lastIndex = 0;
1137
- let m;
1138
- while ((m = categoryRegex.exec(lines[i])) !== null) {
1139
- const key = `${m[1]}(${m[2]})`;
1140
- if (!categoryMap.has(key)) {
1141
- categoryMap.set(key, []);
1142
- }
1143
- categoryMap.get(key).push({
1144
- filePath,
1145
- line: i + 1,
1146
- snippet: lines[i].trim().slice(0, 120),
1147
- });
1148
- }
1149
- }
1150
- }
1151
-
1152
- // .h 和 .m 成对出现是正常的(声明 + 实现),只有同类型文件重名才是问题
1153
- // 或者超过 2 处声明就一定有问题
1154
- for (const [key, locations] of categoryMap) {
1155
- if (locations.length <= 1) {
1156
- continue;
1157
- }
1158
-
1159
- // 按文件扩展名分组: .h 和 .m/.mm 各一个是合法的
1160
- const hFiles = locations.filter((l) => l.filePath.endsWith('.h'));
1161
- const mFiles = locations.filter((l) => !l.filePath.endsWith('.h'));
1162
-
1163
- // 同类型文件中有多个声明 → 重名冲突
1164
- const hasDuplicateH = hFiles.length > 1;
1165
- const hasDuplicateM = mFiles.length > 1;
1166
- // 超过 2 处总声明(如 3 个文件都声明了同一个 Category)→ 一定有问题
1167
- const tooMany = locations.length > 2;
1168
-
1169
- if (hasDuplicateH || hasDuplicateM || tooMany) {
1170
- // 收集冲突的那些位置
1171
- const conflictLocations = tooMany
1172
- ? locations
1173
- : hasDuplicateH && hasDuplicateM
1174
- ? locations
1175
- : hasDuplicateH
1176
- ? hFiles
1177
- : mFiles;
1178
-
1179
- violations.push({
1180
- ruleId: 'objc-cross-file-duplicate-category',
1181
- message: `Category ${key} 在 ${conflictLocations.length} 个文件中重复声明,可能导致方法覆盖或未定义行为`,
1182
- severity: 'warning',
1183
- locations: conflictLocations,
1184
- });
1185
- }
1186
- }
1187
-
1188
- return violations;
1189
- }
1190
-
1191
1005
  /**
1192
1006
  * 清除规则缓存
1193
1007
  */
1194
1008
  clearCache() {
1195
1009
  this._customRulesCache = null;
1196
1010
  this._cacheTime = 0;
1197
- this._regexCache.clear();
1198
- }
1199
-
1200
- /**
1201
- * 编译正则模式(支持 RegExp 对象和 string,带缓存)
1202
- * @param {RegExp|string} pattern
1203
- * @returns {RegExp}
1204
- */
1205
- _compilePattern(pattern) {
1206
- if (pattern instanceof RegExp) {
1207
- return pattern;
1208
- }
1209
- const key = String(pattern);
1210
- let cached = this._regexCache.get(key);
1211
- if (!cached) {
1212
- cached = new RegExp(key);
1213
- this._regexCache.set(key, cached);
1214
- }
1215
- return cached;
1216
- }
1217
-
1218
- /**
1219
- * 构建内联测试块掩码
1220
- * 目前支持 Rust #[cfg(test)] mod xxx { ... } 块
1221
- * @param {string[]} lines
1222
- * @param {string} language
1223
- * @returns {boolean[]} 每行是否在测试块内
1224
- */
1225
- _buildTestBlockMask(lines, language) {
1226
- const mask = new Array(lines.length).fill(false);
1227
-
1228
- // 目前仅 Rust 需要 — #[cfg(test)] 内联测试模块
1229
- if (language !== 'rust') return mask;
1230
-
1231
- let inTestBlock = false;
1232
- let braceDepth = 0;
1233
-
1234
- for (let i = 0; i < lines.length; i++) {
1235
- const trimmed = lines[i].trimStart();
1236
-
1237
- if (!inTestBlock) {
1238
- // 检测 #[cfg(test)] 属性行
1239
- if (/^#\[cfg\(test\)\]/.test(trimmed)) {
1240
- // 向后找 mod xxx { — 标记为测试块起始
1241
- // 可能在同一行: #[cfg(test)] mod tests {
1242
- // 也可能在下一行: mod tests {
1243
- const restOfLine = trimmed.slice('#[cfg(test)]'.length).trim();
1244
- if (/^mod\s+\w+/.test(restOfLine)) {
1245
- // 同一行有 mod 声明
1246
- inTestBlock = true;
1247
- braceDepth = 0;
1248
- // 计算本行的花括号
1249
- for (const ch of lines[i]) {
1250
- if (ch === '{') braceDepth++;
1251
- else if (ch === '}') braceDepth--;
1252
- }
1253
- mask[i] = true;
1254
- if (braceDepth <= 0) inTestBlock = false; // 单行 mod 声明 (mod tests;)
1255
- continue;
1256
- }
1257
- // 检查下一行是否是 mod xxx {
1258
- if (i + 1 < lines.length && /^\s*mod\s+\w+/.test(lines[i + 1])) {
1259
- mask[i] = true; // #[cfg(test)] 行本身也标记
1260
- inTestBlock = true;
1261
- braceDepth = 0;
1262
- // 下一行会在循环中处理
1263
- continue;
1264
- }
1265
- // 单行 #[cfg(test)] 但后面不是 mod — 不处理
1266
- }
1267
- } else {
1268
- // 正在测试块内 — 追踪花括号深度
1269
- mask[i] = true;
1270
- for (const ch of lines[i]) {
1271
- if (ch === '{') braceDepth++;
1272
- else if (ch === '}') braceDepth--;
1273
- }
1274
- if (braceDepth <= 0) {
1275
- inTestBlock = false; // 测试块结束
1276
- }
1277
- }
1278
- }
1279
-
1280
- return mask;
1281
- }
1282
-
1283
- /**
1284
- * 构建注释行掩码 — 识别行注释和块注释内部行
1285
- *
1286
- * 支持的注释形式:
1287
- * // 行注释, /// 文档注释, //! 内部文档注释 (C/Java/JS/TS/Go/Rust/Swift/Kotlin/Dart)
1288
- * # 行注释 (Python)
1289
- * /* ... * / 块注释 (C/Java/JS/TS/Go/Rust/Swift/Kotlin)
1290
- * \"\"\" ... \"\"\" (Python doc-string — 简化: 整行以 \"\"\" 开头的行)
1291
- *
1292
- * @param {string[]} lines
1293
- * @param {string} language
1294
- * @returns {boolean[]} 每行是否为注释行
1295
- */
1296
- _buildCommentMask(lines, language) {
1297
- const mask = new Array(lines.length).fill(false);
1298
- let inBlock = false; // 是否在 /* ... */ 块内
1299
-
1300
- const usesHash = language === 'python'; // Python 用 # 注释
1301
- const usesSlash = !usesHash; // 其他语言用 //
1302
-
1303
- for (let i = 0; i < lines.length; i++) {
1304
- const trimmed = lines[i].trimStart();
1305
-
1306
- // 块注释延续
1307
- if (inBlock) {
1308
- mask[i] = true;
1309
- if (trimmed.includes('*/')) {
1310
- inBlock = false;
1311
- }
1312
- continue;
1313
- }
1314
-
1315
- // 块注释开始(同行不闭合)
1316
- if (usesSlash && /^\s*\/\*/.test(lines[i])) {
1317
- mask[i] = true;
1318
- if (!trimmed.includes('*/')) {
1319
- inBlock = true;
1320
- }
1321
- continue;
1322
- }
1323
-
1324
- // 行注释: // 或 /// 或 //!
1325
- if (usesSlash && /^\s*\/\//.test(lines[i])) {
1326
- mask[i] = true;
1327
- continue;
1328
- }
1329
-
1330
- // Python 行注释: #
1331
- if (usesHash && /^\s*#/.test(lines[i])) {
1332
- mask[i] = true;
1333
- continue;
1334
- }
1335
-
1336
- // Python docstring 行 (简化: 整行以 """ 或 ''' 开头)
1337
- if (usesHash && /^\s*("""|''')/.test(lines[i])) {
1338
- mask[i] = true;
1339
- continue;
1340
- }
1341
- }
1342
-
1343
- return mask;
1011
+ clearPatternCache();
1344
1012
  }
1345
1013
 
1346
1014
  /**