lat.md 0.10.2 → 0.10.4

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.
@@ -9,6 +9,7 @@ export type SourceRef = {
9
9
  target: string;
10
10
  file: string;
11
11
  line: number;
12
+ endLine: number;
12
13
  snippet: string;
13
14
  };
14
15
  export type SectionFound = {
@@ -52,6 +52,7 @@ export async function getSection(ctx, query) {
52
52
  seen.add(targetLower);
53
53
  const symbolPart = hashIdx === -1 ? '' : ref.target.slice(hashIdx + 1);
54
54
  let line = 0;
55
+ let endLine = 0;
55
56
  let snippet = '';
56
57
  if (symbolPart) {
57
58
  const { found, symbols } = await resolveSourceSymbol(filePart, symbolPart, ctx.projectRoot);
@@ -62,6 +63,7 @@ export async function getSection(ctx, query) {
62
63
  : s.name === parts[1] && s.parent === parts[0]);
63
64
  if (sym) {
64
65
  line = sym.startLine;
66
+ endLine = sym.endLine;
65
67
  try {
66
68
  const src = await readFile(join(ctx.projectRoot, filePart), 'utf-8');
67
69
  const srcLines = src.split('\n');
@@ -79,6 +81,7 @@ export async function getSection(ctx, query) {
79
81
  target: ref.target,
80
82
  file: filePart,
81
83
  line,
84
+ endLine,
82
85
  snippet,
83
86
  });
84
87
  }
@@ -181,7 +184,9 @@ export function formatSectionOutput(ctx, result) {
181
184
  }
182
185
  for (const ref of outgoingSourceRefs) {
183
186
  const loc = ref.line
184
- ? `${s.dim(` (${ref.file}:${ref.line})`)}`
187
+ ? ref.endLine && ref.endLine !== ref.line
188
+ ? `${s.dim(` (${ref.file}:${ref.line}-${ref.endLine})`)}`
189
+ : `${s.dim(` (${ref.file}:${ref.line})`)}`
185
190
  : `${s.dim(` (${ref.file})`)}`;
186
191
  parts.push(`${s.dim('*')} [[${s.cyan(ref.target)}]]${loc}`);
187
192
  if (ref.snippet) {
@@ -546,8 +546,13 @@ function extractCSymbols(tree) {
546
546
  return symbols;
547
547
  }
548
548
  /**
549
- * Walk C AST nodes, collecting symbols. Recurses into preproc_ifdef /
550
- * preproc_ifndef blocks so header include guards don't hide declarations.
549
+ * Walk C AST nodes, collecting symbols. Recurses into preprocessor
550
+ * conditional blocks (ifdef/ifndef/if), linkage specifications
551
+ * (extern "C" { ... }), and declaration lists so that include guards
552
+ * and conditional compilation don't hide declarations.
553
+ *
554
+ * For #if/#ifdef/#ifndef, only the "then" branch is traversed —
555
+ * preproc_else and preproc_elif children are skipped.
551
556
  */
552
557
  function collectCNodes(parent, symbols) {
553
558
  for (let i = 0; i < parent.childCount; i++) {
@@ -592,7 +597,12 @@ function collectCNodes(parent, symbols) {
592
597
  }
593
598
  }
594
599
  else if (node.type === 'type_definition') {
595
- const declarator = node.childForFieldName('declarator');
600
+ let declarator = node.childForFieldName('declarator');
601
+ // Unwrap pointer_declarator for pointer typedefs
602
+ // e.g. `typedef struct __JSValue *JSValue;`
603
+ while (declarator?.type === 'pointer_declarator') {
604
+ declarator = declarator.childForFieldName('declarator') ?? null;
605
+ }
596
606
  const name = declarator?.type === 'type_identifier' ? declarator.text : null;
597
607
  if (name) {
598
608
  symbols.push({
@@ -606,16 +616,30 @@ function collectCNodes(parent, symbols) {
606
616
  }
607
617
  else if (node.type === 'declaration') {
608
618
  const declarator = node.childForFieldName('declarator');
609
- const name = declarator ? cVarName(declarator) : null;
610
- if (name) {
619
+ // Try as function declaration first (e.g. `void greet(const char *name);`
620
+ // in headers), then fall back to variable.
621
+ const funcName = declarator ? cFuncName(declarator) : null;
622
+ if (funcName) {
611
623
  symbols.push({
612
- name,
613
- kind: 'variable',
624
+ name: funcName,
625
+ kind: 'function',
614
626
  startLine,
615
627
  endLine,
616
628
  signature: firstLine(node.text),
617
629
  });
618
630
  }
631
+ else {
632
+ const name = declarator ? cVarName(declarator) : null;
633
+ if (name) {
634
+ symbols.push({
635
+ name,
636
+ kind: 'variable',
637
+ startLine,
638
+ endLine,
639
+ signature: firstLine(node.text),
640
+ });
641
+ }
642
+ }
619
643
  }
620
644
  else if (node.type === 'preproc_def' ||
621
645
  node.type === 'preproc_function_def') {
@@ -631,10 +655,21 @@ function collectCNodes(parent, symbols) {
631
655
  }
632
656
  }
633
657
  else if (node.type === 'preproc_ifdef' ||
634
- node.type === 'preproc_ifndef') {
635
- // Recurse into include guard / conditional blocks
658
+ node.type === 'preproc_ifndef' ||
659
+ node.type === 'preproc_if') {
660
+ // Recurse into conditional blocks (then-branch only).
661
+ // preproc_else / preproc_elif children are skipped.
662
+ collectCNodes(node, symbols);
663
+ }
664
+ else if (node.type === 'linkage_specification' ||
665
+ node.type === 'declaration_list') {
666
+ // extern "C" { ... } wraps declarations in linkage_specification
667
+ // containing a declaration_list — recurse through both.
636
668
  collectCNodes(node, symbols);
637
669
  }
670
+ else if (node.type === 'preproc_else' || node.type === 'preproc_elif') {
671
+ // Skip else/elif branches of preprocessor conditionals.
672
+ }
638
673
  }
639
674
  }
640
675
  function firstLine(text) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lat.md",
3
- "version": "0.10.2",
3
+ "version": "0.10.4",
4
4
  "description": "A knowledge graph for your codebase, written in markdown",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.30.2",