@plebeiantech/lat.md 0.12.3-fork.16 → 0.12.3-fork.17

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 (121) hide show
  1. package/README.md +1 -1
  2. package/dist/src/cli/index.js +12 -6
  3. package/dist/src/cli/init.js +3 -4
  4. package/dist/src/cli/reindex.js +4 -8
  5. package/dist/src/cli/search.d.ts +3 -2
  6. package/dist/src/cli/search.js +69 -59
  7. package/dist/src/format.d.ts +5 -0
  8. package/dist/src/format.js +32 -4
  9. package/dist/src/lattice-model.d.ts +8 -1
  10. package/dist/src/lattice-model.js +4 -1
  11. package/dist/src/lattice.d.ts +2 -2
  12. package/dist/src/lattice.js +17 -34
  13. package/dist/src/markdown-analysis.d.ts +11 -0
  14. package/dist/src/markdown-analysis.js +31 -1
  15. package/dist/src/mcp/server.js +6 -6
  16. package/dist/src/parser-cache.d.ts +1 -1
  17. package/dist/src/parser-cache.js +1 -1
  18. package/dist/src/search/cache.d.ts +3 -0
  19. package/dist/src/search/cache.js +71 -0
  20. package/dist/src/search/chunks.d.ts +23 -0
  21. package/dist/src/search/chunks.js +197 -0
  22. package/dist/src/search/db.d.ts +27 -16
  23. package/dist/src/search/db.js +108 -38
  24. package/dist/src/search/index.d.ts +5 -2
  25. package/dist/src/search/index.js +173 -69
  26. package/dist/src/search/lexical.d.ts +6 -0
  27. package/dist/src/search/lexical.js +36 -0
  28. package/dist/src/search/lock.d.ts +2 -0
  29. package/dist/src/search/lock.js +23 -0
  30. package/dist/src/search/query.d.ts +2 -2
  31. package/dist/src/search/query.js +24 -9
  32. package/dist/src/search/search.d.ts +13 -8
  33. package/dist/src/search/search.js +155 -23
  34. package/dist/src/search/types.d.ts +31 -0
  35. package/dist/src/search/types.js +1 -0
  36. package/dist/src/source-formats.d.ts +1 -1
  37. package/dist/src/source-formats.js +2 -0
  38. package/dist/src/source-parser.js +197 -1
  39. package/dist/src/view/client/assets/{GraphView-BuZg_wrj.js → GraphView-BWlRdQM4.js} +2 -2
  40. package/dist/src/view/client/assets/{MarkdownEditor-BKqJtZYv.js → MarkdownEditor-MW-Ck1O0.js} +1 -1
  41. package/dist/src/view/client/assets/{abnfDiagram-VCTEODGH-CAu3KwfO.js → abnfDiagram-VCTEODGH-Bn3RshMV.js} +1 -1
  42. package/dist/src/view/client/assets/{arc-CFnPZhKM.js → arc-En9tdFQF.js} +1 -1
  43. package/dist/src/view/client/assets/{architectureDiagram-5GKGNRK7-Cf9Kd0OA.js → architectureDiagram-5GKGNRK7-B-9J1VAM.js} +1 -1
  44. package/dist/src/view/client/assets/{blockDiagram-I7D4REHJ-dPn7g_vm.js → blockDiagram-I7D4REHJ-QlznwalP.js} +1 -1
  45. package/dist/src/view/client/assets/{c4Diagram-7LVT6UL2-DdZrwaPd.js → c4Diagram-7LVT6UL2-Bp2y8B4_.js} +1 -1
  46. package/dist/src/view/client/assets/channel-uy0fFvcW.js +1 -0
  47. package/dist/src/view/client/assets/{chunk-2Q5K7J3B-C42JuteH.js → chunk-2Q5K7J3B-CnBKL-lm.js} +1 -1
  48. package/dist/src/view/client/assets/{chunk-5VM5RSS4-BKK3z8Zy.js → chunk-5VM5RSS4-LLYMi_n6.js} +1 -1
  49. package/dist/src/view/client/assets/{chunk-F27PBJKO-qZl_uKcW.js → chunk-F27PBJKO-VIeO7VF0.js} +1 -1
  50. package/dist/src/view/client/assets/{chunk-IMKFNOWR-Bs4Rnoj_.js → chunk-IMKFNOWR-D5i67DVD.js} +1 -1
  51. package/dist/src/view/client/assets/{chunk-JWPE2WC7-BpxgV2rA.js → chunk-JWPE2WC7-D4qAtoL3.js} +1 -1
  52. package/dist/src/view/client/assets/{chunk-POPQ4Y6H-hIynofdE.js → chunk-POPQ4Y6H-BMzOyiW_.js} +1 -1
  53. package/dist/src/view/client/assets/{chunk-SVP7TREG-t3gKMqeu.js → chunk-SVP7TREG-DWy5nbfp.js} +1 -1
  54. package/dist/src/view/client/assets/{chunk-TICWLB2K-B2x394mh.js → chunk-TICWLB2K-CVqCANFB.js} +1 -1
  55. package/dist/src/view/client/assets/{chunk-XXDRQBXY-CHaVKDsH.js → chunk-XXDRQBXY-Bp23h6Tf.js} +1 -1
  56. package/dist/src/view/client/assets/classDiagram-ZZMXUADV-DbSEqjA5.js +1 -0
  57. package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-DbSEqjA5.js +1 -0
  58. package/dist/src/view/client/assets/{cose-bilkent-JH36ORCC-DE2-Jgqu.js → cose-bilkent-JH36ORCC-BCw7fCC2.js} +1 -1
  59. package/dist/src/view/client/assets/{cynefin-OW5HDTMX-BsEtOvRy.js → cynefin-OW5HDTMX--lYN4N9m.js} +1 -1
  60. package/dist/src/view/client/assets/{cynefinDiagram-5FMLGOSQ-BKYG1Xs8.js → cynefinDiagram-5FMLGOSQ-9sK5EV7v.js} +1 -1
  61. package/dist/src/view/client/assets/{dagre-GXQ25YYZ-hgrdNjl7.js → dagre-GXQ25YYZ-CoMdEFCq.js} +1 -1
  62. package/dist/src/view/client/assets/{diagram-S7CK7UJ4-VIx8zAxf.js → diagram-S7CK7UJ4-D13dyWdd.js} +1 -1
  63. package/dist/src/view/client/assets/{diagram-UQ7AKVKN-yfloehRm.js → diagram-UQ7AKVKN-PdwLPPPA.js} +1 -1
  64. package/dist/src/view/client/assets/{diagram-VSXAHHWV-BT_dA4Q9.js → diagram-VSXAHHWV-CtWWa_A7.js} +1 -1
  65. package/dist/src/view/client/assets/{diagram-VX7I27RA-Gfmwxx6u.js → diagram-VX7I27RA-BeCYcSU-.js} +1 -1
  66. package/dist/src/view/client/assets/{diagram-Z3DM3KII-C2yM7EOo.js → diagram-Z3DM3KII-DiWabOBM.js} +1 -1
  67. package/dist/src/view/client/assets/{ebnfDiagram-PWID7BFC-Bkb_Lj59.js → ebnfDiagram-PWID7BFC-CZQ3MD_K.js} +1 -1
  68. package/dist/src/view/client/assets/{erDiagram-RLTQ6QDP-C7wQHPye.js → erDiagram-RLTQ6QDP-B1AhXIrP.js} +1 -1
  69. package/dist/src/view/client/assets/{flowDiagram-HODETNUW-CXoKr3oI.js → flowDiagram-HODETNUW-Bhr0JM6P.js} +1 -1
  70. package/dist/src/view/client/assets/{ganttDiagram-EL5Y4UJY-DqqXKgLa.js → ganttDiagram-EL5Y4UJY-CbP8iAkg.js} +1 -1
  71. package/dist/src/view/client/assets/{gitGraphDiagram-WWUBYQGX-D_DAhSgZ.js → gitGraphDiagram-WWUBYQGX-B9uuGdLF.js} +1 -1
  72. package/dist/src/view/client/assets/{index-DAxFk0q-.css → index-DG0Ez15D.css} +1 -1
  73. package/dist/src/view/client/assets/index-nBmbYfun.js +13 -0
  74. package/dist/src/view/client/assets/{infoDiagram-27XIBGKW-B50EOats.js → infoDiagram-27XIBGKW-BrfwQ_Cq.js} +1 -1
  75. package/dist/src/view/client/assets/{ishikawaDiagram-5VMMS53U-D1rny9gj.js → ishikawaDiagram-5VMMS53U-CHtJ36re.js} +1 -1
  76. package/dist/src/view/client/assets/{journeyDiagram-3NMN7TZE-B5UD2Xo4.js → journeyDiagram-3NMN7TZE-BChcgX1s.js} +1 -1
  77. package/dist/src/view/client/assets/{kanban-definition-UXKFOSKX-CTgey9zw.js → kanban-definition-UXKFOSKX-BnwFsKiB.js} +1 -1
  78. package/dist/src/view/client/assets/{layout-4X2n4os-.js → layout-Dnd0pLdb.js} +1 -1
  79. package/dist/src/view/client/assets/{linear-D0RIBXsp.js → linear-CFhW2ozn.js} +1 -1
  80. package/dist/src/view/client/assets/{mermaid.core-BAnH8y-D.js → mermaid.core--OP6lXtW.js} +5 -5
  81. package/dist/src/view/client/assets/{mindmap-definition-YA3MSWOX-WiFsh3Jg.js → mindmap-definition-YA3MSWOX-D51y2zbm.js} +1 -1
  82. package/dist/src/view/client/assets/{pegDiagram-XKGWAZYB-DCJOrckP.js → pegDiagram-XKGWAZYB--zyQJDvI.js} +1 -1
  83. package/dist/src/view/client/assets/{pieDiagram-E7YTZNPT-C78IkUE2.js → pieDiagram-E7YTZNPT-Cj8mvpJy.js} +1 -1
  84. package/dist/src/view/client/assets/{quadrantDiagram-AXDQQJYC-xZ6guleB.js → quadrantDiagram-AXDQQJYC-qvhJrnBf.js} +1 -1
  85. package/dist/src/view/client/assets/{railroadDiagram-O6MQD6OU-D3PwmgX0.js → railroadDiagram-O6MQD6OU-CtWvxpSo.js} +1 -1
  86. package/dist/src/view/client/assets/{requirementDiagram-BXWQKSXE-v-I8wLGH.js → requirementDiagram-BXWQKSXE-CBxen_KG.js} +1 -1
  87. package/dist/src/view/client/assets/{sankeyDiagram-P5KCCOFB-DBY-sdO7.js → sankeyDiagram-P5KCCOFB-B_fUkmfc.js} +1 -1
  88. package/dist/src/view/client/assets/{sequenceDiagram-WJ2MYXX4-D9HApBFi.js → sequenceDiagram-WJ2MYXX4-Dclv4p68.js} +1 -1
  89. package/dist/src/view/client/assets/{sizeCapture-INFHLROL-ERAytLnQ.js → sizeCapture-INFHLROL-GfG0sMTZ.js} +1 -1
  90. package/dist/src/view/client/assets/{stateDiagram-D77RDMKH-BKef_Qwv.js → stateDiagram-D77RDMKH-YQWPnzxU.js} +1 -1
  91. package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-gUj6Hzgl.js +1 -0
  92. package/dist/src/view/client/assets/{swimlanes-42K2YHIH-D7dS_tnF.js → swimlanes-42K2YHIH-5yiCveVS.js} +1 -1
  93. package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-DR7JhrxL.js +8 -0
  94. package/dist/src/view/client/assets/{timeline-definition-24CTP7MA-Di1UCseG.js → timeline-definition-24CTP7MA-BvAbB5Zs.js} +1 -1
  95. package/dist/src/view/client/assets/{vennDiagram-4TSXK5OY-g7Je3H2H.js → vennDiagram-4TSXK5OY-guC-r60n.js} +1 -1
  96. package/dist/src/view/client/assets/{wardleyDiagram-VM6X3IG4-C_diPEka.js → wardleyDiagram-VM6X3IG4-C0yXcU2x.js} +1 -1
  97. package/dist/src/view/client/assets/{xychartDiagram-S5SC5T6Z-C8uIQy_I.js → xychartDiagram-S5SC5T6Z-RPmkPNfK.js} +1 -1
  98. package/dist/src/view/client/index.html +2 -2
  99. package/dist/src/view/highlight.js +2 -0
  100. package/dist/src/view/markdown.js +15 -0
  101. package/dist/src/view/protocol.d.ts +7 -1
  102. package/dist/src/view/references.js +3 -3
  103. package/dist/src/view/repository.js +10 -2
  104. package/dist/src/view/search-result.js +7 -2
  105. package/dist/src/view/server-build.js +1 -1
  106. package/dist/src/view/server-deployment.js +2 -1
  107. package/dist/src/view/server-index-worker.js +1 -1
  108. package/dist/src/view/source-target.d.ts +2 -0
  109. package/dist/src/view/source-target.js +24 -0
  110. package/dist/src/view/store.js +3 -3
  111. package/dist/src/view/vercel-build.js +7 -0
  112. package/package.json +6 -3
  113. package/templates/AGENTS.md +1 -1
  114. package/templates/cursor-rules.md +1 -1
  115. package/templates/skill/SKILL.md +1 -1
  116. package/dist/src/view/client/assets/channel-C-byJRR7.js +0 -1
  117. package/dist/src/view/client/assets/classDiagram-ZZMXUADV-FlqGrEwv.js +0 -1
  118. package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-FlqGrEwv.js +0 -1
  119. package/dist/src/view/client/assets/index-DPuOWFj2.js +0 -10
  120. package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-BePfayWI.js +0 -1
  121. package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-DaqCEguT.js +0 -8
@@ -0,0 +1,31 @@
1
+ export type SourceSpan = {
2
+ start: number;
3
+ end: number;
4
+ startLine: number;
5
+ endLine: number;
6
+ };
7
+ export type SearchEvidence = {
8
+ chunkId: string;
9
+ text: string;
10
+ spans: SourceSpan[];
11
+ channel: 'lexical' | 'semantic';
12
+ };
13
+ export type SearchDiagnostics = {
14
+ lexicalCapped: boolean;
15
+ semanticCapped: boolean;
16
+ lexicalCandidates: number;
17
+ semanticCandidates: number;
18
+ };
19
+ export type SearchResult = {
20
+ id: string;
21
+ file: string;
22
+ heading: string;
23
+ content: string;
24
+ rankScore: number;
25
+ semanticSimilarity?: number;
26
+ semanticRank?: number;
27
+ lexicalScore?: number;
28
+ lexicalRank?: number;
29
+ evidence: SearchEvidence[];
30
+ diagnostics: SearchDiagnostics;
31
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,5 @@
1
1
  /** Source file extensions supported by Lat's parser and code-reference scan. */
2
- export declare const SOURCE_FILE_EXTENSIONS: readonly [".c", ".dart", ".go", ".h", ".java", ".js", ".jsx", ".py", ".rake", ".rb", ".rs", ".ts", ".tsx"];
2
+ export declare const SOURCE_FILE_EXTENSIONS: readonly [".c", ".dart", ".erb", ".go", ".h", ".java", ".js", ".jsx", ".php", ".py", ".rake", ".rb", ".rs", ".ts", ".tsx"];
3
3
  /** A source extension supported everywhere Lat accepts source files. */
4
4
  export type SourceFileExtension = (typeof SOURCE_FILE_EXTENSIONS)[number];
5
5
  export declare function isSourceFileExtension(extension: string): extension is SourceFileExtension;
@@ -3,11 +3,13 @@ import { extname } from 'node:path';
3
3
  export const SOURCE_FILE_EXTENSIONS = [
4
4
  '.c',
5
5
  '.dart',
6
+ '.erb',
6
7
  '.go',
7
8
  '.h',
8
9
  '.java',
9
10
  '.js',
10
11
  '.jsx',
12
+ '.php',
11
13
  '.py',
12
14
  '.rake',
13
15
  '.rb',
@@ -28,11 +28,13 @@ async function ensureParser() {
28
28
  const grammarMap = {
29
29
  '.c': 'tree-sitter-c.wasm',
30
30
  '.dart': 'tree-sitter-dart.wasm',
31
+ '.erb': 'tree-sitter-ruby.wasm',
31
32
  '.go': 'tree-sitter-go.wasm',
32
33
  '.h': 'tree-sitter-c.wasm',
33
34
  '.java': 'tree-sitter-java.wasm',
34
35
  '.js': 'tree-sitter-javascript.wasm',
35
36
  '.jsx': 'tree-sitter-javascript.wasm',
37
+ '.php': 'tree-sitter-php.wasm',
36
38
  '.py': 'tree-sitter-python.wasm',
37
39
  '.rake': 'tree-sitter-ruby.wasm',
38
40
  '.rb': 'tree-sitter-ruby.wasm',
@@ -752,6 +754,125 @@ function extractJavaSymbols(tree) {
752
754
  collectJavaScope(sourceLines, tree.rootNode, undefined, symbols);
753
755
  return symbols;
754
756
  }
757
+ function phpSignature(sourceLines, node) {
758
+ const declaration = node.namedChildren.find((child) => child.type !== 'attribute_list');
759
+ return sourceLines[(declaration ?? node).startPosition.row]?.trim() ?? '';
760
+ }
761
+ function phpName(node) {
762
+ const nameNode = node.childForFieldName('name');
763
+ if (nameNode?.type === 'by_ref') {
764
+ return (nameNode.namedChildren
765
+ .find((child) => child.type === 'variable_name')
766
+ ?.text.replace(/^\$/, '') ?? null);
767
+ }
768
+ const name = extractName(node) ??
769
+ node.namedChildren.find((child) => child.type === 'name')?.text ??
770
+ null;
771
+ return name?.replace(/^\$/, '') ?? null;
772
+ }
773
+ function pushPhpSymbol(sourceLines, symbols, node, name, kind, parent) {
774
+ symbols.push({
775
+ name,
776
+ kind,
777
+ ...(parent ? { parent } : {}),
778
+ startLine: node.startPosition.row + 1,
779
+ endLine: node.endPosition.row + 1,
780
+ signature: phpSignature(sourceLines, node),
781
+ });
782
+ }
783
+ function extractPhpVariables(sourceLines, symbols, node, parent) {
784
+ const kind = node.type === 'const_declaration' ? 'const' : 'variable';
785
+ for (const child of node.namedChildren) {
786
+ if (child.type !== 'const_element' && child.type !== 'property_element') {
787
+ continue;
788
+ }
789
+ const name = phpName(child);
790
+ if (name)
791
+ pushPhpSymbol(sourceLines, symbols, node, name, kind, parent);
792
+ }
793
+ }
794
+ const phpTypeKinds = {
795
+ class_declaration: 'class',
796
+ interface_declaration: 'interface',
797
+ trait_declaration: 'interface',
798
+ enum_declaration: 'class',
799
+ };
800
+ // Recurse through declaration containers, not arbitrary expressions: anonymous
801
+ // classes and closures must not leak their members into the enclosing scope.
802
+ const phpScopeContainers = new Set([
803
+ 'namespace_definition',
804
+ 'compound_statement',
805
+ 'declaration_list',
806
+ 'enum_declaration_list',
807
+ 'if_statement',
808
+ 'else_if_clause',
809
+ 'else_clause',
810
+ 'switch_statement',
811
+ 'switch_block',
812
+ 'case_statement',
813
+ 'default_statement',
814
+ 'while_statement',
815
+ 'do_statement',
816
+ 'for_statement',
817
+ 'foreach_statement',
818
+ 'try_statement',
819
+ 'catch_clause',
820
+ 'finally_clause',
821
+ 'declare_statement',
822
+ ]);
823
+ function collectPhpScope(sourceLines, scope, symbols, parent) {
824
+ for (const node of scope.namedChildren) {
825
+ const typeKind = phpTypeKinds[node.type];
826
+ if (typeKind) {
827
+ const name = phpName(node);
828
+ if (!name)
829
+ continue;
830
+ pushPhpSymbol(sourceLines, symbols, node, name, typeKind);
831
+ const body = node.childForFieldName('body');
832
+ if (body)
833
+ collectPhpScope(sourceLines, body, symbols, name);
834
+ continue;
835
+ }
836
+ if (node.type === 'function_definition' && !parent) {
837
+ const name = phpName(node);
838
+ if (name)
839
+ pushPhpSymbol(sourceLines, symbols, node, name, 'function');
840
+ }
841
+ else if (node.type === 'method_declaration' && parent) {
842
+ const name = phpName(node);
843
+ if (name)
844
+ pushPhpSymbol(sourceLines, symbols, node, name, 'method', parent);
845
+ if (name?.toLowerCase() === '__construct') {
846
+ const parameters = node.childForFieldName('parameters');
847
+ for (const parameter of parameters?.namedChildren ?? []) {
848
+ if (parameter.type !== 'property_promotion_parameter')
849
+ continue;
850
+ const propertyName = phpName(parameter);
851
+ if (!propertyName)
852
+ continue;
853
+ pushPhpSymbol(sourceLines, symbols, parameter, propertyName, 'variable', parent);
854
+ }
855
+ }
856
+ }
857
+ else if (node.type === 'const_declaration' ||
858
+ node.type === 'property_declaration') {
859
+ extractPhpVariables(sourceLines, symbols, node, parent);
860
+ }
861
+ else if (node.type === 'enum_case' && parent) {
862
+ const name = phpName(node);
863
+ if (name)
864
+ pushPhpSymbol(sourceLines, symbols, node, name, 'const', parent);
865
+ }
866
+ else if (phpScopeContainers.has(node.type)) {
867
+ collectPhpScope(sourceLines, node, symbols, parent);
868
+ }
869
+ }
870
+ }
871
+ function extractPhpSymbols(tree) {
872
+ const symbols = [];
873
+ collectPhpScope(tree.rootNode.text.split('\n'), tree.rootNode, symbols);
874
+ return symbols;
875
+ }
755
876
  function extractRustSymbols(tree) {
756
877
  const symbols = [];
757
878
  const root = tree.rootNode;
@@ -1294,11 +1415,13 @@ function firstLine(text) {
1294
1415
  const symbolExtractors = {
1295
1416
  '.c': extractCSymbols,
1296
1417
  '.dart': extractDartSymbols,
1418
+ '.erb': extractRubySymbols,
1297
1419
  '.go': extractGoSymbols,
1298
1420
  '.h': extractCSymbols,
1299
1421
  '.java': extractJavaSymbols,
1300
1422
  '.js': extractTsSymbols,
1301
1423
  '.jsx': extractTsSymbols,
1424
+ '.php': extractPhpSymbols,
1302
1425
  '.py': extractPySymbols,
1303
1426
  '.rake': extractRubySymbols,
1304
1427
  '.rb': extractRubySymbols,
@@ -1306,6 +1429,78 @@ const symbolExtractors = {
1306
1429
  '.ts': extractTsSymbols,
1307
1430
  '.tsx': extractTsSymbols,
1308
1431
  };
1432
+ function preprocessErb(content) {
1433
+ let result = '';
1434
+ let inRuby = false;
1435
+ let inComment = false;
1436
+ let i = 0;
1437
+ while (i < content.length) {
1438
+ if (!inRuby && !inComment) {
1439
+ if (content.startsWith('<%%', i)) {
1440
+ result += ' ';
1441
+ i += 3;
1442
+ }
1443
+ else if (content.startsWith('<%#', i)) {
1444
+ inComment = true;
1445
+ result += ' ';
1446
+ i += 3;
1447
+ }
1448
+ else if (content.startsWith('<%', i)) {
1449
+ inRuby = true;
1450
+ let tagLen = 2;
1451
+ while (i + tagLen < content.length &&
1452
+ ['=', '-', '~'].includes(content[i + tagLen])) {
1453
+ tagLen++;
1454
+ }
1455
+ result += ' '.repeat(tagLen);
1456
+ i += tagLen;
1457
+ }
1458
+ else {
1459
+ const ch = content[i];
1460
+ result += ch === '\n' ? '\n' : ' ';
1461
+ i++;
1462
+ }
1463
+ }
1464
+ else if (inComment) {
1465
+ if (content.startsWith('%>', i)) {
1466
+ inComment = false;
1467
+ result += ' ';
1468
+ i += 2;
1469
+ }
1470
+ else if (content.startsWith('-%>', i) || content.startsWith('~%>', i)) {
1471
+ inComment = false;
1472
+ result += ' ';
1473
+ i += 3;
1474
+ }
1475
+ else {
1476
+ const ch = content[i];
1477
+ result += ch === '\n' ? '\n' : ' ';
1478
+ i++;
1479
+ }
1480
+ }
1481
+ else {
1482
+ if (content.startsWith('%%>', i)) {
1483
+ result += ' ';
1484
+ i += 3;
1485
+ }
1486
+ else if (content.startsWith('%>', i)) {
1487
+ inRuby = false;
1488
+ result += ' ';
1489
+ i += 2;
1490
+ }
1491
+ else if (content.startsWith('-%>', i) || content.startsWith('~%>', i)) {
1492
+ inRuby = false;
1493
+ result += ' ';
1494
+ i += 3;
1495
+ }
1496
+ else {
1497
+ result += content[i];
1498
+ i++;
1499
+ }
1500
+ }
1501
+ }
1502
+ return result;
1503
+ }
1309
1504
  export async function parseSourceSymbols(filePath, content) {
1310
1505
  const ext = sourceFileExtension(filePath);
1311
1506
  if (!ext)
@@ -1313,7 +1508,8 @@ export async function parseSourceSymbols(filePath, content) {
1313
1508
  const lang = await getLanguage(ext);
1314
1509
  const p = await ensureParser();
1315
1510
  p.setLanguage(lang);
1316
- const tree = p.parse(content);
1511
+ const parsedContent = ext === '.erb' && content.includes('<%') ? preprocessErb(content) : content;
1512
+ const tree = p.parse(parsedContent);
1317
1513
  if (!tree)
1318
1514
  return [];
1319
1515
  try {