Forge
markdowndeeb25a2
1# ADR 0112: Режимы строки палитры (`f:` / `t:` / `m:` / `x:` / `c:`) — модель режимов, стратегии и **бэкенды** workspace-поиска
2
3**Статус:** Accepted · Implemented
4**Дата:** 2026-05-11 · обновлено 2026-05-12
5## Связанные ADR
6
7| ADR | Роль |
8|-----|------|
9| [0013](0013-command-surface-and-discoverability.md) | палитра, keyboard-first |
10| [0030](0030-command-ids-hotkeys-and-ui-registry-layers.md) | `IdeCommands`, каталог палитры |
11| [0060](0060-keyboard-chord-stack-fms-tactical-strategic.md) | Command Melody `c:` и CascadeChord — ортогонально префиксам **в строке** палитры |
12| [0070](0070-command-palette-direct-overlay-surface.md) | палитра как overlay |
13| [0079](0079-ide-display-system-ids-overlay-pipeline.md) | IDS, подсказки оверлея |
14| [0081](0081-parametric-intent-melodies-editor-line-ranges.md) | параметрический хвост после alias в `c:` |
15| [0109](0109-declarative-parametric-melody-catalog-toml-and-code-binders.md) | каталог melody |
16| [0105](0105-hybrid-codebase-index-for-csharp-web.md) | Hybrid Codebase Index (ядро + MCP) for C# stacks with Roslyn Truth |
17| [0106](0106-hybrid-codebase-index-cascadeide-integration-and-semantic-map.md) | Hybrid Codebase Index — HCI |
18
19### Вне ADR
20
21| Документ | Роль |
22|----------|------|
23| [intent-melody-language-v1.md](../intent-melody-language-v1.md) | intent melody language v1 |
24**Точки кода (фактический baseline):** `IdeCommandPaletteFilterOrchestrator`, `IntentMelodyAliases.TryGetTail`, `GoToAllQueryParser`, `CommandPaletteChromeProjection`, `IdeCommandPaletteExecutionOrchestrator`.
25
26### Снимок реализации
27
28| Элемент | Значение |
29|---------|----------|
30| — | этапы 1–4: режимная модель разбора, chrome, rg/hci/auto бэкенды, TOML |
31| — | стратегии по режиму — см. код `CommandPaletteParsedQueryParser` и ветви оркестратора |
32
33## Резюме
34
35- Палитра (**Ctrl+Q**): режимы строки запроса (`t:`/`m:`/`x:`), стратегии бэкенда.
36- Контракт workspace-поиска и переключение в `settings.toml`.
37- Direct overlay surface — [0070](0070-command-palette-direct-overlay-surface.md).
38
39---
40## Контекст
41
42Хоткей палитры (по умолчанию **Ctrl+Q**) открывает **одну строку запроса** и **общий список результатов**, но семантика ввода **не однородна**:
43
44| Префикс | Имя (рабочее) | Смысл запроса | Где задаётся сейчас |
45|---------|---------------|---------------|---------------------|
46| *(нет)* | каталог команд | fuzzy по названию/`command_id` | дефолтная ветка оркестратора |
47| `f:` | go-to файл | фильтрация файлов решения | `GoToAllQueryParser` + `RefreshGoToPaletteFilter` |
48| `t:` | go-to тип | поиск типов по `.cs` | то же |
49| `m:` | go-to член | эвристика по членам `.cs` | то же |
50| `x:` | текст | ripgrep по workspace | то же |
51| `c:` | Command Melody | alias → `command_id`, параметрика `:start:end` | `IntentMelodyAliases.TryGetTail` + `RefreshMelodyPaletteFilter` |
52
53Подсказки в футере/плейсхолдере дублируют этот набор текстом (**`CommandPaletteChromeProjection`**), а **разветвление по режимам** сосредоточено в **цепочке `if` в начале `RefreshCommandPaletteFilter`**: сначала `c:`, потом `f|t|m|x`, иначе каталог.
54
55**Проблема:** режим — **неименованная концепция в коде**; порядок проверок, отмена async go-to (`goToHandle.Cancel()`), типы результатов строк и особенности UI (`IdeCommandPaletteRowViewModel`: melody vs каталог vs go-to) размазаны по методам без единого контрактa «режим палитры».
56
57Риски при росте: новый префикс или изменение приоритета (например, конфликт с будущим `w:`) — правки в нескольких местах; тестируемость режимов не структурирована.
58
59---
60
61## Решение
62
63Ввести два согласованных слоя:
64
651. **Режим строки** (что ввёл пользователь) — **`CommandPaletteQueryMode`** / **`CommandPaletteParsedQuery`** (§1–2).
662. **Бэкенд workspace-поиска** для подрежимов `t:` / `m:` / `x:` — **отдельный контракт** с подключаемыми реализациями и настройкой переключения (§7). Это **не** режим UI: один и тот же префикс может обслуживаться разными движками без смены синтаксиса палитры.
67
68Обработку режимов оформить как **стратегию на режим** (паттерн Strategy / небольшой реестр — детали реализации не фиксируем жёстко). Стратегия go-to для `t:`/`m:`/`x:` **делегирует** поиск выбранному бэкенду (или цепочке fallback), а не вызывает `rg` напрямую из оркестратора.
69
70<a id="adr0112-p1"></a>
71
72### 1. Модель
73
74- **`CommandPaletteParsedQuery`** (или аналог): дискриминированное объединение:
75 - **Melody** — нормализованный хвост после `c:` (как сейчас `TryGetTail`);
76 - **GoTo** — `GoToAllQuery` (префикс `f` | `t` | `m` | `x` + терм);
77 - **Catalog** — строка без зарезервированного режимного префикса (трим + fuzzy по каталогу).
78
79Явно задокументировать **приоритет разбора** (совместимость с текущим поведением):
80
811. `c:` — первым (чтобы `c:` не пересекался с go-to и каталогом);
822. затем `f:` / `t:` / `m:` / `x:`;
833. иначе каталог.
84
85<a id="adr0112-p2"></a>
86
87### 2. Стратегия на режим
88
89Для каждого режима — **один вход**: контекст фильтрации (решение, roots, путь файла, текст редактора, горячие клавиши, семья UI mode и т.д.) + **действие**:
90
91- очистить/заполнить `filteredEntries`;
92- выставить выбранный индекс;
93- вызвать `refreshCommandPaletteSurfaceSnapshot`;
94- для go-to — управление **отменой** фонового поиска (`CommandPaletteGoToAsyncHandle`), как сейчас.
95
96**Цель рефакторинга:** `RefreshCommandPaletteFilter` становится **диспетчером**: `parse → lookup strategy → execute`, без дублирования правил «кто отменяет go-to».
97
98<a id="adr0112-p3"></a>
99
100### 3. Связь с исполнением (Enter)
101
102**Выполнение** выбора уже частично унифицировано через `IdeCommandPaletteRowViewModel.RowKind` и `IdeCommandPaletteExecutionOrchestrator` (команда MCP vs go-to navigation). Режим фильтрации и RowKind должны оставаться **согласованными**; при внедрении стратегий не менять пользовательский контракт строк без отдельного ADR.
103
104<a id="adr0112-p4"></a>
105
106### 4. Chrome и обнаруживаемость
107
108Тексты **`CommandPaletteChromeProjection`** (footer / placeholder) должны **строиться из канонического списка режимов** (имя префикса + короткая подпись), чтобы новый режим добавлялся **в одном месте** данных, а не копированием строки `"f: файл · …"`.
109
110<a id="adr0112-p5"></a>
111
112### 5. Тестируемость
113
114- Юнит-тесты на **разбор** сырой строки → режим + полезная нагрузка (граничные случаи: `c:` без хвоста, `x:` только пробелы, конфликт не ожидается в v1).
115- По возможности — тесты на **порядок** стратегий (регресс: `c:something` не уходит в каталог как plain text без отдельного решения продукта).
116
117<a id="adr0112-p6"></a>
118
119### 6. Бэкенд go-to: ripgrep vs **HCI** (Hybrid Codebase Index)
120
121Идея использовать **`SearchHybridAsync`** / индекс вместо **`RipgrepWorkspaceSearchService`** для `t:` / `m:` / `x:` **не автоматически** «быстрее и эффективнее» для всех режимов — зависит от **цели режима** и **свежести индекса**.
122
123| Префикс | Сейчас (baseline) | HCI как кандидат | Нюансы |
124|---------|-------------------|------------------|--------|
125| `f:` | дерево решения, фильтрация путей в памяти | обычно **не нужен** | Узкое место — не поиск по диску; индекс почти не даёт выигрыша. |
126| `x:` | литерал через ripgrep по workspace | **часто уместен**: FTS по проиндексированным чанкам | Плюс: нет процесса `rg`, предсказуемая задержка при **прогретом** индексе. Минус: до reindex — **рассинхрон** с диском; семантика FTS (токены, AND) ≠ «как rg»; для дословного «как текстовый редактор» палитре может понадаться **fallback на rg**. |
127| `t:` / `m:` | **regex по `.cs`** (`GoToPaletteRipgrepPatternBuilder`: объявление типа / `идентификатор(`) | **спорно без доработки модели попаданий** | HCI индексирует **текстовые фрагменты**, не обязательно с тем же качеством, что объявление Roslyn/VS «Go to Type/Member». Возможны лишние или пропущенные строки по сравнению с текущей эвристикой regex. Целевая точность для «тип/член» ближе к **Roslyn** (отдельный трек), чем к «просто FTS». |
128| (опционально) | — | **`semantic`** в HCI | Полезно для **похожего по смыслу**, а не для контрактов «строго по имени символа» — не подменять `t:`/`m:` без явного режима продукта. |
129
130Детали контракта и переключения — в §7.
131
132<a id="adr0112-p7"></a>
133
134### 7. Первоклассный **бэкенд** workspace-поиска (`t:` / `m:` / `x:`)
135
136**Граница:** понятие «бэкенд» относится к **асинхронному поиску по содержимому файлов в корне workspace** для префиксов **`t:`**, **`m:`**, **`x:`**. Режим **`f:`** (фильтрация путей по дереву решения) **в этот контракт не входит** — отдельная синхронная стратегия без смены движка «как у rg».
137
138**Контракт (working name):** абстракция уровня **`ICommandPaletteGoToSearchBackend`** (или эквивалент) с единым входом и выходом:
139
140- **Вход:** нормализованный `GoToAllQuery`, корень workspace, лимиты (`MaxRipgrepMatches` / аналог), scope индекса (согласованный с [HCI scope](0106-hybrid-codebase-index-cascadeide-integration-and-semantic-map.md)), `CancellationToken`.
141- **Выход:** упорядоченный список **канонических попаданий** для строк палитры (путь, строка, колонка, короткая подпись/категория — совместимо с проекцией в `IdeCommandPaletteRowViewModel` / существующими `CommandPaletteGoTo*NavRowsProjection`).
142
143**Реализации (минимальный набор):**
144
145| Реализация | Роль |
146|------------|------|
147| **Ripgrep** | Поведение как сейчас: `GoToPaletteRipgrepPatternBuilder` + `RipgrepWorkspaceSearchService`. |
148| **HCI-FTS** | Запрос к Hybrid Index (без обязательного semantic), маппинг hit → тот же DTO попадания. |
149| **Composite / Auto** | Цепочка: например HCI → при пустом результате, ошибке или «индекс не готов» — **fallback** на Ripgrep. Политика задаётся явно, а не неявно в коде оркестратора. |
150
151**Конфигурация переключения** — только в **пользовательском** **`settings.toml`** (`%LocalAppData%\CascadeIDE\`, см. [0028](0028-user-settings-toml-localappdata-and-secrets.md)): то же дерево, что `CascadeIdeSettings` / `SettingsService.Load`, **не** репозиторный `.cascade/workspace.toml` (`UiWorkspaceToml` — другая модель: маршрутизация зон, пресеты навигации и т.д.). Выбор «как искать из палитры» — предпочтение **рабочего места**, а не артефакта конкретного репозитория (командную политику «только rg» при необходимости можно добавить позже отдельным механизмом overlay).
152
153**Куда положить в схеме (согласовано с имеющимися ключами):**
154
155| Вариант | Вердикт |
156|---------|---------|
157| **`[hybrid_index]`** | **Не использовать** под `backend`: при значении **`rg`** настройка не про индекс; смешивает включение/параметры HCI и независимый выбор движка палитры. Связь `auto` ↔ HCI — только в **коде** фасада. |
158| **`[workspace]`** | **Не использовать**: в коде это `WorkspaceSettings` — панели, `Flight`, splitters (`docs/samples/settings.toml`); к поисковому бэкенду не относится. |
159| **`[command_palette…]`** | **Да**: новое верхнеуровневое свойство на `CascadeIdeSettings` (рядом с `HybridIndex`, `Workspace`, …), вложенная таблица в TOML — как у `[display.screens.grammar]` / markdown: snake_case ключи (`CascadeIdeSettingsTomlDeserializeTests`). |
160
161Маппинг при реализации (ориентир): `CascadeIdeSettings.CommandPalette.GoToSearch.Backend` → таблица **`[command_palette.go_to_search]`**, поле **`backend`**.
162
163Канонические значения бэкенда (короткие строковые литералы для TOML):
164
165| Значение | Смысл |
166|----------|--------|
167| **`rg`** | Только **ripgrep** (текущее baseline-поведение; рекомендуемый **дефолт** до стабилизации HCI-пути). |
168| **`hci`** | Только **Hybrid Codebase Index** (FTS-хит → попадания палитры). |
169| **`auto`** | Сначала HCI; при неготовом индексе, ошибке или пустом ответе уместного рода — **fallback** на `rg`. Политика fallback — в коде фасада Composite, не размазана по UI. |
170
171Пример (в том же пользовательском файле, что и `[hybrid_index]`):
172
173```toml
174[command_palette.go_to_search]
175backend = "rg" # "rg" | "hci" | "auto"
176```
177
178- **Пер-префиксные оверрайды** (`x:` ≠ `t:`) — опционально **вторая итерация** (напр. отдельный ключ или подтаблица), если без них проще выпустить v1.
179
180**Поддержка и тестирование:**
181
182- Новый бэкенд добавляется **одной реализацией интерфейса** + регистрация в DI / фабрике выбора; оркестратор не ветвится по `if (useHci)`.
183- Юнит-тесты оркестратора на **фейковом бэкенде**; отдельные тесты на каждую реализацию (маппинг hit ↔ строка палитры).
184
185**UX (опционально):** короткая метка источника в subtitle («rg» / «index») или только в диагностике — решение продукта; по умолчанию пользователю не обязательно видеть имя бэкенда.
186
187---
188
189## Отклонённые / отложенные альтернативы
190
191- **Плагинируемые префиксы из TOML в v1** — дороже протокола и валидации; оставить на будущее, когда появится второй источник префиксов кроме кода.
192- **Объединить `c:` и go-to в одну грамматику** — ломает ментальную модель IML и VS-style go-to; не делаем.
193- **Единый regex-парсер всей строки** — возможно как имплементация-деталь; архитектурно важен не синтаксис, а **тип режима и стратегия**.
194
195---
196
197## Последствия
198
199- Положительные: один явный вход для документации режимов; проще добавлять префикс или менять побочные эффекты (отмена go‑to); меньше расхождения между подсказками chrome и кодом; **смена движка поиска** (`rg` ↔ HCI ↔ цепочка) без правок разветвления в `IdeCommandPaletteFilterOrchestrator`.
200- Отрицательные: объём рефакторинга — orchestrator, DI, настройки, тесты; поведение для пользователя **в v1 после выноса интерфейса** должно оставаться **паритетным** с Ripgrep-backend по умолчанию (смена только через настройку — осознанно).
201
202---
203
204## Статус реализации
205
206Реализовано в приложении и тестах: `CommandPaletteParsedQuery` / `CommandPaletteParsedQueryParser`, `ICommandPaletteGoToSearchBackend` (+ `rg` / HCI / `auto`), `[command_palette.go_to_search]` в settings, канон подсказок `CommandPaletteChromeModeHints`.
207
208Дальнейшие улучшения (например плагинируемые префиксы или расширение семантики `x:`) — по этому ADR как открытым направлениям, не блокеры текущего объёма.
209
View only · write via MCP/CIDE