(lang: string, source: string)
| 205 | }; |
| 206 | |
| 207 | function extractWithRegex(lang: string, source: string): ExtractedSymbol[] { |
| 208 | const patterns = REGEX_PATTERNS[lang]; |
| 209 | if (!patterns) return []; |
| 210 | |
| 211 | const lines = source.split('\n'); |
| 212 | const lineOffsets: number[] = [0]; |
| 213 | for (let i = 0; i < lines.length; i++) { |
| 214 | lineOffsets.push(lineOffsets[i]! + lines[i]!.length + 1); |
| 215 | } |
| 216 | const positionToLine = (pos: number): { line: number; col: number } => { |
| 217 | // Binary search |
| 218 | let lo = 0, hi = lineOffsets.length - 1; |
| 219 | while (lo < hi) { |
| 220 | const mid = (lo + hi + 1) >>> 1; |
| 221 | if (lineOffsets[mid]! <= pos) lo = mid; |
| 222 | else hi = mid - 1; |
| 223 | } |
| 224 | return { line: lo + 1, col: pos - lineOffsets[lo]! }; |
| 225 | }; |
| 226 | |
| 227 | const symbols: ExtractedSymbol[] = []; |
| 228 | for (const { pattern, kind } of patterns) { |
| 229 | const re = new RegExp(pattern.source, pattern.flags); |
| 230 | let m: RegExpExecArray | null; |
| 231 | while ((m = re.exec(source)) !== null) { |
| 232 | const name = m[1]; |
| 233 | if (!name) continue; |
| 234 | const { line, col } = positionToLine(m.index); |
| 235 | const lineText = (lines[line - 1] ?? '').slice(0, 200); |
| 236 | symbols.push({ |
| 237 | name, |
| 238 | kind, |
| 239 | startLine: line, |
| 240 | endLine: line, // regex doesn't know body bounds |
| 241 | startColumn: col, |
| 242 | signature: lineText, |
| 243 | }); |
| 244 | } |
| 245 | } |
| 246 | return symbols; |
| 247 | } |
no test coverage detected