* Strip prose punctuation wrapped around a token without eating punctuation * that is PART of the path: quotes/backticks always strip; a trailing `)`/`]` * strips only when the token has no matching opener (so `(protected)` and * `[id]` segments survive, while "…(see src/foo.ts)" loses its parent
(token: string)
| 111 | * so "src/foo.ts." resolves. |
| 112 | */ |
| 113 | function stripWrapping(token: string): string { |
| 114 | let s = token; |
| 115 | for (;;) { |
| 116 | const first = s[0]; |
| 117 | if (!first) break; |
| 118 | if ('\'"`<'.includes(first)) { s = s.slice(1); continue; } |
| 119 | if (first === '(' && !s.includes(')')) { s = s.slice(1); continue; } |
| 120 | if (first === '[' && !s.includes(']')) { s = s.slice(1); continue; } |
| 121 | if (first === '{' && !s.includes('}')) { s = s.slice(1); continue; } |
| 122 | break; |
| 123 | } |
| 124 | for (;;) { |
| 125 | const last = s[s.length - 1]; |
| 126 | if (!last) break; |
| 127 | if ('\'"`>.,;!?'.includes(last)) { s = s.slice(0, -1); continue; } |
| 128 | if (last === ')' && !s.includes('(')) { s = s.slice(0, -1); continue; } |
| 129 | if (last === ']' && !s.includes('[')) { s = s.slice(0, -1); continue; } |
| 130 | if (last === '}' && !s.includes('{')) { s = s.slice(0, -1); continue; } |
| 131 | break; |
| 132 | } |
| 133 | // Line references ride along in agent-written paths: `foo.ts:123`, |
| 134 | // `foo.ts:12-40`, `foo.ts#L88`. The file is what gets pinned. |
| 135 | s = s.replace(/(?::\d+(?:-\d+)?|#L\d+(?:-L?\d+)?)$/, ''); |
| 136 | return s; |
| 137 | } |
| 138 | |
| 139 | /** Normalize a span into the repo-relative shape the files table stores. */ |
| 140 | function normalizeSpan(span: string): string { |