/** * Browser-safe `@file` token grammar shared by terminal and web clients. * * @module @deepseek-ai/dsh-file-reference/grammar */ import type { FileReferenceCandidate } from './types.ts' /** Active `@` token ending at the editor cursor. */ export interface ActiveAtToken { /** Complete token replaced when the user accepts a completion. */ prefix: string /** Path query after `@` or `@"`. */ query: string /** Whether the user opened a quoted path. */ quoted: boolean } /** * Extract an `@path` or `@"path with spaces` token at the cursor. An `@` * inside another token, such as an email address, is not a completion trigger. * @param line - current editor line. * @param cursorCol - cursor column within that line. * @returns the active token, or `undefined` outside an `@` token. */ export function activeAtToken(line: string, cursorCol: number): ActiveAtToken | undefined { const beforeCursor = line.slice(0, cursorCol) const quoted = /(?:^|\s)(@"([^"]*))$/u.exec(beforeCursor) if (quoted?.[1] !== undefined && quoted[2] !== undefined) { return { prefix: quoted[1], query: quoted[2], quoted: true } } const plain = /(?:^|\s)(@([^\s]*))$/u.exec(beforeCursor) if (plain?.[1] === undefined || plain[2] === undefined) return undefined return { prefix: plain[1], query: plain[2], quoted: false } } /** * Format a selected path as prompt text. Whitespace uses the quoted * `@"path"` grammar; a quoted directory keeps that quote open after its * trailing slash so completion can descend another level. * @param candidate - selected file or directory. * @param preserveQuote - retain an explicitly opened quote even when unnecessary. * @returns the insertion value, or `undefined` for a path the editor grammar cannot represent safely. */ export function formatFileMention( candidate: FileReferenceCandidate, preserveQuote: boolean, ): string | undefined { const path = candidate.kind === 'directory' ? `${candidate.path}/` : candidate.path if (/[\u0000-\u001f\u007f-\u009f"]/u.test(path)) return undefined const quoted = preserveQuote || /\s/u.test(path) if (!quoted) return `@${path}` if (candidate.kind === 'directory') return `@"${path}` return `@"${path}"` }