Julius Marmingerm unused9e877f58 months agoHistory
90 lines · 2.5 KB · TypeScript
RawDownload
export type ComposerTriggerKind = "path" | "slash-command" | "slash-model"; export interface ComposerTrigger {  kind: ComposerTriggerKind;  query: string;  rangeStart: number;  rangeEnd: number;} function clampCursor(text: string, cursor: number): number {  if (!Number.isFinite(cursor)) return text.length;  return Math.max(0, Math.min(text.length, Math.floor(cursor)));} function isWhitespace(char: string): boolean {  return char === " " || char === "\n" || char === "\t" || char === "\r";} function tokenStartForCursor(text: string, cursor: number): number {  let index = cursor - 1;  while (index >= 0 && !isWhitespace(text[index] ?? "")) {    index -= 1;  }  return index + 1;} export function detectComposerTrigger(text: string, cursorInput: number): ComposerTrigger | null {  const cursor = clampCursor(text, cursorInput);  const lineStart = text.lastIndexOf("\n", Math.max(0, cursor - 1)) + 1;  const linePrefix = text.slice(lineStart, cursor);   if (linePrefix.startsWith("/")) {    const commandMatch = /^\/(\S*)$/.exec(linePrefix);    if (commandMatch) {      const commandQuery = commandMatch[1] ?? "";      if (commandQuery.toLowerCase() === "model") {        return {          kind: "slash-model",          query: "",          rangeStart: lineStart,          rangeEnd: cursor,        };      }      if ("model".startsWith(commandQuery.toLowerCase())) {        return {          kind: "slash-command",          query: commandQuery,          rangeStart: lineStart,          rangeEnd: cursor,        };      }      return null;    }     const modelMatch = /^\/model(?:\s+(.*))?$/.exec(linePrefix);    if (modelMatch) {      return {        kind: "slash-model",        query: (modelMatch[1] ?? "").trim(),        rangeStart: lineStart,        rangeEnd: cursor,      };    }  }   const tokenStart = tokenStartForCursor(text, cursor);  const token = text.slice(tokenStart, cursor);  if (!token.startsWith("@")) {    return null;  }   return {    kind: "path",    query: token.slice(1),    rangeStart: tokenStart,    rangeEnd: cursor,  };} export function replaceTextRange(  text: string,  rangeStart: number,  rangeEnd: number,  replacement: string,): { text: string; cursor: number } {  const safeStart = Math.max(0, Math.min(text.length, rangeStart));  const safeEnd = Math.max(safeStart, Math.min(text.length, rangeEnd));  const nextText = `${text.slice(0, safeStart)}${replacement}${text.slice(safeEnd)}`;  return { text: nextText, cursor: safeStart + replacement.length };}