/** ⌘F in a console (Run, Debug): the IDEA find bar (Match Case / Words / Regex, n/m, previous/next). */ import { type KeyboardEvent, type RefObject } from "react"; import clsx from "clsx"; import { IconButton } from "./Button"; import { SearchField } from "./Input"; import { formatBinding, hasPrimaryModifier } from "@/platform/keymap"; import { tx } from "@/platform/i18n"; import type { FindOptions } from "./consoleFind"; import styles from "./ConsoleFindBar.module.css"; export interface ConsoleFindBarProps { query: string; onChange: (query: string) => void; options: FindOptions; onOptionsChange: (options: FindOptions) => void; /** Number of matches. */ count: number; /** Zero-based current match; -1 when there is none. */ active: number; /** More matches exist than were collected. */ overflow: boolean; /** The query is an invalid regular expression. */ invalid: boolean; inputRef: RefObject; onNext: () => void; onPrevious: () => void; onClose: () => void; } export function ConsoleFindBar({ query, onChange, options, onOptionsChange, count, active, overflow, invalid, inputRef, onNext, onPrevious, onClose, }: ConsoleFindBarProps) { const none = !!query && !invalid && count === 0; const counter = invalid ? "Bad regex" : !query ? "" : count === 0 ? "0 results" : `${active + 1}/${count}${overflow ? "+" : ""}`; const toggle = (key: keyof FindOptions) => onOptionsChange({ ...options, [key]: !options[key] }); const onKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { e.preventDefault(); e.stopPropagation(); onClose(); } else if (e.key === "Enter") { // Enter on a button (Match Case, Close, …) must activate it, not jump to the next match. if ((e.target as Element | null)?.closest?.("button")) return; e.preventDefault(); if (e.shiftKey) onPrevious(); else onNext(); } else if (e.key === "F3" || (hasPrimaryModifier(e) && e.key.toLowerCase() === "g")) { e.preventDefault(); if (e.shiftKey) onPrevious(); else onNext(); } else if (hasPrimaryModifier(e) && e.key.toLowerCase() === "f") { e.preventDefault(); inputRef.current?.focus(); inputRef.current?.select(); } else if (e.altKey && !e.metaKey && !e.ctrlKey) { const key = e.code === "KeyC" ? "caseSensitive" : e.code === "KeyW" ? "wholeWord" : e.code === "KeyX" ? "regex" : null; if (key) { e.preventDefault(); toggle(key); } } }; return (
toggle("caseSensitive"), }, { id: "word", icon: "word", title: "Words", shortcut: formatBinding("Alt+W"), pressed: options.wholeWord, onToggle: () => toggle("wholeWord"), }, { id: "regex", icon: "regex", title: "Regex", shortcut: formatBinding("Alt+X"), pressed: options.regex, onToggle: () => toggle("regex"), }, ]} /> {counter}
); }