/** * ↑↓ 기록(337 설계 2-12 · H11) — 쉴 때 = 친 명령 기록 · 명령 중 = 그 물음(`prompt.history` · 없으면 글)의 최근 값 20. * 보이는 기록 줄(run-command 의 log · 알림 섞임)과 나눔. 캔버스 · 명령행이 같이 씀. 글 물음(TEXT 등)은 안 남김. */ import { matchCommandPrefixes, resolveCommandInput } from '../commands/registry'; import { currentPrompt } from './prompt-input'; const COMMANDS = '\u0000명령'; const LIMIT = { command: 50, value: 20 }; const groups = new Map(); let browsing: { group: string; index: number } | null = null; /** 지금 물음의 기록 묶음 · 글 물음 · 모름 = null */ function groupNow(): string | null { const prompt = currentPrompt(); if (prompt.kind === 'idle') return COMMANDS; if (prompt.kind === 'text' || prompt.kind === null) return null; return prompt.spec?.history ?? prompt.spec?.text ?? prompt.line; } /** 제출한 글 하나를 지금 물음 기록에 — 쉴 때는 명령이 된 글만 */ export function pushInput(text: string): void { browsing = null; const value = text.trim(); const group = groupNow(); if (!value || !group) return; if (group === COMMANDS && !resolveCommandInput(value)) return; const list = groups.get(group) ?? []; if (list[list.length - 1] !== value) list.push(value); list.splice(0, Math.max(0, list.length - (group === COMMANDS ? LIMIT.command : LIMIT.value))); groups.set(group, list); } /** ↑(-1) · ↓(+1) — 지금 물음 기록에서 한 칸 · 끝을 지나면 빈 글 · 기록 없으면 null(친 글 그대로) */ export function recallInput(dir: -1 | 1, _text: string): string | null { const group = groupNow(); const list = group ? groups.get(group) : undefined; if (!group || !list?.length) return null; const from = browsing?.group === group ? browsing.index : list.length; const index = Math.min(list.length, Math.max(0, from + dir)); browsing = { group, index }; return index === list.length ? '' : list[index]; } /** 명령 후보 돌림(Tab · 후보 보일 때 ↑↓) — base 로 시작하는 명령 중 index 번째(돌아감) */ export function commandCandidate(base: string, index: number): string | null { const candidates = matchCommandPrefixes(base); if (!candidates.length) return null; const n = candidates.length; return candidates[((index % n) + n) % n].id; }