MCPcopy Create free account
hub / github.com/codeaashu/claude-code / collectQuoteSpans

Function collectQuoteSpans

src/utils/bash/treeSitterAnalysis.ts:88–137  ·  view source on GitHub ↗

* Single-pass collection of all quote-related spans. * Previously this was 5 separate tree walks (one per type-set plus * allQuoteTypes plus heredoc); fusing cuts tree-traversal ~5x. * * Replicates the per-type walk semantics: each original walk stopped at * its own type. So the raw_string walk

(
  node: TreeSitterNode,
  out: QuoteSpans,
  inDouble: boolean,
)

Source from the content-addressed store, hash-verified

86 * in bash (no expansion), so no nested quote nodes exist — return early.
87 */
88function collectQuoteSpans(
89 node: TreeSitterNode,
90 out: QuoteSpans,
91 inDouble: boolean,
92): void {
93 switch (node.type) {
94 case 'raw_string':
95 out.raw.push([node.startIndex, node.endIndex])
96 return // literal body, no nested quotes possible
97 case 'ansi_c_string':
98 out.ansiC.push([node.startIndex, node.endIndex])
99 return // literal body
100 case 'string':
101 // Only collect the outermost string (matches old per-type walk
102 // which stops at first match). Recurse regardless — a nested
103 // $(cmd 'x') inside "..." has a real inner raw_string.
104 if (!inDouble) out.double.push([node.startIndex, node.endIndex])
105 for (const child of node.children) {
106 if (child) collectQuoteSpans(child, out, true)
107 }
108 return
109 case 'heredoc_redirect': {
110 // Quoted heredocs (<<'EOF', <<"EOF", <<\EOF): literal body.
111 // Unquoted (<<EOF) expands $()/${} — the body can contain
112 // $(cmd 'x') whose inner '...' IS a real raw_string node.
113 // Detection: heredoc_start text starts with '/"/\\
114 // Matches sync path's extractHeredocs({ quotedOnly: true }).
115 let isQuoted = false
116 for (const child of node.children) {
117 if (child && child.type === 'heredoc_start') {
118 const first = child.text[0]
119 isQuoted = first === "'" || first === '"' || first === '\\'
120 break
121 }
122 }
123 if (isQuoted) {
124 out.heredoc.push([node.startIndex, node.endIndex])
125 return // literal body, no nested quote nodes
126 }
127 // Unquoted: recurse into heredoc_body → command_substitution →
128 // inner quote nodes. The original per-type walks did NOT stop at
129 // heredoc_redirect (not in their type sets), so they recursed here.
130 break
131 }
132 }
133
134 for (const child of node.children) {
135 if (child) collectQuoteSpans(child, out, inDouble)
136 }
137}
138
139/**
140 * Builds a Set of all character positions covered by the given spans.

Callers 1

extractQuoteContextFunction · 0.85

Calls 1

pushMethod · 0.45

Tested by

no test coverage detected