| 1 |
// `#N` and `@user` in text the user is typing (spec §6.2): what is being |
| 2 |
// completed at the cursor, and which reference a position sits on. No |
| 3 |
// vscode imports, so it is tested with plain Node. |
| 4 |
|
| 5 |
/** A reference being typed: `#12` (issue or proposal) or `@an` (a user). */ |
| 6 |
export type Typing = { kind: "number"; prefix: string; start: number } | { kind: "user"; prefix: string; start: number }; |
| 7 |
|
| 8 |
/** |
| 9 |
* What the text before the cursor is in the middle of typing, if anything. |
| 10 |
* A reference starts a word: after the line start, whitespace or an |
| 11 |
* opening bracket, so `a#1` and `mail@host` are not references. |
| 12 |
*/ |
| 13 |
export function typingAt(before: string): Typing | undefined { |
| 14 |
const n = /(^|[\s([{])#(\d*)$/.exec(before); |
| 15 |
if (n) { |
| 16 |
return { kind: "number", prefix: n[2], start: before.length - n[2].length - 1 }; |
| 17 |
} |
| 18 |
const u = /(^|[\s([{])@([A-Za-z0-9][A-Za-z0-9_-]*|)$/.exec(before); |
| 19 |
if (u) { |
| 20 |
return { kind: "user", prefix: u[2], start: before.length - u[2].length - 1 }; |
| 21 |
} |
| 22 |
return undefined; |
| 23 |
} |
| 24 |
|
| 25 |
/** The `#N` reference covering `column` of `line`, with its span. */ |
| 26 |
export function numberAt(line: string, column: number): { number: number; start: number; end: number } | undefined { |
| 27 |
const re = /(^|[\s([{])#(\d+)\b/g; |
| 28 |
for (let m = re.exec(line); m; m = re.exec(line)) { |
| 29 |
const start = m.index + m[1].length; |
| 30 |
const end = start + 1 + m[2].length; |
| 31 |
if (column >= start && column <= end) { |
| 32 |
return { number: Number(m[2]), start, end }; |
| 33 |
} |
| 34 |
} |
| 35 |
return undefined; |
| 36 |
} |
| 37 |
|
| 38 |
/** A repository root named by an SCM input box's URI query (`rootUri=file:///…`). */ |
| 39 |
export function rootFromScmQuery(query: string): string | undefined { |
| 40 |
const raw = new URLSearchParams(query).get("rootUri"); |
| 41 |
if (!raw) { |
| 42 |
return undefined; |
| 43 |
} |
| 44 |
try { |
| 45 |
return decodeURIComponent(new URL(raw).pathname); |
| 46 |
} catch { |
| 47 |
return undefined; |
| 48 |
} |
| 49 |
} |