@reformer/builder-plugin-api 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +206 -0
  3. package/dist/index.d.ts +147 -0
  4. package/dist/index.js +76 -0
  5. package/dist/internal.d.ts +78 -0
  6. package/dist/internal.js +289 -0
  7. package/dist/permissions-BWlsUVPs.js +551 -0
  8. package/dist/plugin/bundled-modules.d.ts +25 -0
  9. package/dist/plugin/layout.d.ts +27 -0
  10. package/dist/plugin/manifest-parser.d.ts +54 -0
  11. package/dist/plugin/manifest.d.ts +320 -0
  12. package/dist/plugin/messages-bundle.d.ts +24 -0
  13. package/dist/plugin/permissions.d.ts +48 -0
  14. package/dist/plugin/plugin-exports.d.ts +10 -0
  15. package/dist/plugin/runtime-modules.d.ts +20 -0
  16. package/dist/plugin/storage.d.ts +70 -0
  17. package/dist/plugin/types.d.ts +136 -0
  18. package/dist/primitives/capability.d.ts +72 -0
  19. package/dist/primitives/command.d.ts +262 -0
  20. package/dist/primitives/disposable.d.ts +23 -0
  21. package/dist/primitives/event.d.ts +38 -0
  22. package/dist/primitives/extension-point.d.ts +53 -0
  23. package/dist/primitives/module-path.d.ts +21 -0
  24. package/dist/primitives/resource.d.ts +159 -0
  25. package/dist/primitives/semver.d.ts +93 -0
  26. package/dist/primitives/service.d.ts +68 -0
  27. package/dist/primitives/when-context.d.ts +69 -0
  28. package/dist/primitives/when-expr.d.ts +177 -0
  29. package/dist/resource-clipboard-Bdk_Is5N.js +381 -0
  30. package/dist/runtime-modules-CiUDFMIn.js +597 -0
  31. package/dist/services/context-keys.d.ts +52 -0
  32. package/dist/services/diagnostics/fixes.d.ts +40 -0
  33. package/dist/services/diagnostics/service.d.ts +34 -0
  34. package/dist/services/diagnostics/types.d.ts +148 -0
  35. package/dist/services/document-models.d.ts +20 -0
  36. package/dist/services/documents.d.ts +94 -0
  37. package/dist/services/host-messages.d.ts +10 -0
  38. package/dist/services/i18n.d.ts +43 -0
  39. package/dist/services/modules.d.ts +49 -0
  40. package/dist/services/notifications.d.ts +56 -0
  41. package/dist/services/plugin-settings.d.ts +10 -0
  42. package/dist/services/plugins-catalog.d.ts +58 -0
  43. package/dist/services/preview.d.ts +163 -0
  44. package/dist/services/prompt.d.ts +111 -0
  45. package/dist/services/resource-clipboard.d.ts +28 -0
  46. package/dist/services/selection.d.ts +46 -0
  47. package/dist/services/settings.d.ts +44 -0
  48. package/dist/services/theme.d.ts +15 -0
  49. package/dist/services/validation/types.d.ts +98 -0
  50. package/dist/services/workspace-files.d.ts +84 -0
  51. package/dist/services/workspace-resources.d.ts +41 -0
  52. package/dist/services/workspace-save.d.ts +18 -0
  53. package/dist/tooling.d.ts +28 -0
  54. package/dist/tooling.js +35 -0
  55. package/dist/ui/contributions/decorations.d.ts +55 -0
  56. package/dist/ui/contributions/editors.d.ts +58 -0
  57. package/dist/ui/contributions/plugin-settings.d.ts +54 -0
  58. package/dist/ui/keyboard/keybinding-rules.d.ts +48 -0
  59. package/dist/ui/keyboard/keybindings.d.ts +55 -0
  60. package/dist/ui/keyboard/keymap.d.ts +81 -0
  61. package/dist/ui/keyboard/scope.d.ts +38 -0
  62. package/dist/ui/menu/editor-menu.d.ts +25 -0
  63. package/dist/ui/menu/menu.d.ts +228 -0
  64. package/dist/ui/menu/palette.d.ts +36 -0
  65. package/dist/ui/menu/resource-menu.d.ts +45 -0
  66. package/dist/ui/slots.d.ts +113 -0
  67. package/dist/ui/useActiveDocument.d.ts +5 -0
  68. package/dist/ui/useLocale.d.ts +9 -0
  69. package/dist/ui/useTranslate.d.ts +4 -0
  70. package/dist/workspace/document.d.ts +42 -0
  71. package/dist/workspace/model/editor-view-states.d.ts +29 -0
  72. package/dist/workspace/model/model-document.d.ts +86 -0
  73. package/dist/workspace/model/provider.d.ts +140 -0
  74. package/dist/workspace/model/text-editor-focus.d.ts +22 -0
  75. package/dist/workspace/resource-names.d.ts +92 -0
  76. package/dist/workspace/write-options.d.ts +36 -0
  77. package/package.json +69 -0
@@ -0,0 +1,228 @@
1
+ import { ComponentType } from 'react';
2
+ import { Disposable } from '../../primitives/disposable.js';
3
+ import { WhenContext } from '../../primitives/when-context.js';
4
+ /**
5
+ * Корневые меню, объявленные Host.
6
+ *
7
+ * Набор фиксирован по тому же доводу, что и набор слотов оболочки: состав шапки — часть
8
+ * идентичности приложения, а не следствие того, какие плагины включены. Добавление корня —
9
+ * правка Host, и пусть будет заметной.
10
+ *
11
+ * Плагин при этом не заперт: он вправе внести СВОЁ корневое меню ({@link MenuRootContribution}),
12
+ * но встанет оно в зону между `file` и `help` — см. {@link sortMenuRoots}.
13
+ *
14
+ * ## Почему только «Файл» и «Справка»
15
+ *
16
+ * «Правка» и «Вид» были и убраны. Обе состояли из действий, у которых уже есть более короткий
17
+ * путь: правка узлов — из сочетаний клавиш и контекстного меню на самом узле, а панели, тема
18
+ * и палитра — из рейла и `mod+shift+p`. Меню, дублирующее то, что рядом на экране, стоит
19
+ * не «ничего», а лишний уровень в шапке и лишнее место, где состав приложения надо повторить.
20
+ * Сами действия никуда не делись: команды на месте, сочетания работают, палитра их находит.
21
+ */
22
+ export type MenuRootId = 'file' | 'help';
23
+ /** Корневые меню Host. Порядок показа задаёт {@link sortMenuRoots}, а не этот список. */
24
+ export declare const MENU_ROOT_IDS: readonly MenuRootId[];
25
+ /**
26
+ * Контекстные меню, объявленные Host, — корни, у которых нет места в шапке.
27
+ *
28
+ * Они перечислены здесь по той же причине, что и корни шапки: набор поверхностей приложения
29
+ * не должен зависеть от того, какие плагины включены. Но показывает их не {@link buildMenuBar},
30
+ * а сама поверхность — дерево ресурсов строит своё меню через {@link buildMenu} в момент
31
+ * щелчка, потому что до щелчка у контекстного меню нет цели, а без цели половина его пунктов
32
+ * не имеет смысла.
33
+ *
34
+ * - `resource/context` — щелчок правой кнопкой по строке дерева ресурсов (или мимо строк).
35
+ * - `editor/title` — ряд действий справа в строке вкладок: то, что относится к ОТКРЫТОМУ
36
+ * документу, а не к приложению. Пункт со значком становится кнопкой, пункт без значка
37
+ * уходит под «…» — так же, как это устроено в редакторах, откуда пришла привычка.
38
+ */
39
+ export type ContextMenuId = 'resource/context' | 'editor/title';
40
+ /** Контекстные корни Host. Известны {@link unknownMenuPaths}, поэтому вклад в них — не промах. */
41
+ export declare const CONTEXT_MENU_IDS: readonly ContextMenuId[];
42
+ /**
43
+ * Адрес места в дереве меню: корень (`file`) или подменю (`file/recent`).
44
+ *
45
+ * Строка, а не объединение: пути подменю заводят плагины, и перечислить их Host не может.
46
+ * Промах по несуществующему пути не рисуется — найти его умеет {@link unknownMenuPaths}.
47
+ */
48
+ export type MenuPath = string;
49
+ /** Общее у всего, что вносится ВНУТРЬ меню. */
50
+ interface MenuPlacement {
51
+ /** Куда: корневое меню или подменю. */
52
+ readonly menu: MenuPath;
53
+ /**
54
+ * Группа внутри меню. Группы сортируются по имени, поэтому соглашение — числовой префикс:
55
+ * `1_new`, `2_open`. Отсутствие означает безымянную группу, и она идёт первой.
56
+ */
57
+ readonly group?: string;
58
+ /** Порядок внутри группы; меньше — раньше. Без значения берётся `order` вклада. */
59
+ readonly order?: number;
60
+ /**
61
+ * Показывать ли сейчас. Отсутствие означает «всегда».
62
+ *
63
+ * `when` скрывает, а недоступность гасит: первое — про принадлежность контексту (пункты
64
+ * редактора схемы не нужны на markdown), второе — про «сейчас нечего отменять». Скрывать
65
+ * второе значило бы, что меню меняет высоту под курсором.
66
+ */
67
+ readonly when?: (ctx: WhenContext, target: MenuTarget) => boolean;
68
+ /**
69
+ * Доступен ли пункт ПРИ ЭТОЙ ЦЕЛИ. Отсутствие означает «доступен».
70
+ *
71
+ * ## Зачем он, если недоступность уже считает реестр команд
72
+ *
73
+ * `CommandRegistry.isEnabled` отвечает на вопрос «есть ли чему сработать» по
74
+ * {@link WhenContext}, и цели щелчка там нет и быть не может: контекст применимости
75
+ * одинаков для палитры, клавиш и меню, а цель существует только в момент щелчка. Поэтому
76
+ * «сгенерировать можно в папку, но не в файл» через `enabled` невыразимо — команда не
77
+ * знает, по чему щёлкнули, и в палитре этот вопрос не имеет смысла вовсе.
78
+ *
79
+ * ## Почему не `when`
80
+ *
81
+ * `when` СКРЫВАЕТ, и для «применимо не к каждой строке дерева» это чаще всего правильно:
82
+ * «Переименовать» на пустом месте панели — пункт не про эту цель, и его там нет. Но там,
83
+ * где пункт про цель ОДНОГО РОДА (создание — про каталоги), исчезновение на файле читается
84
+ * как пропажа возможности: человек видел «Сгенерировать» на папке, щёлкнул по файлу — и
85
+ * пункта нет, а почему — меню не сказало. Серый пункт говорит это сам.
86
+ *
87
+ * Предикат обязан быть чистым и дешёвым — его зовут на каждую сборку меню. Бросок означает
88
+ * «недоступен»: запускать действие, условие которого неизвестно, хуже, чем не запускать.
89
+ */
90
+ readonly enabledWhen?: (ctx: WhenContext, target: MenuTarget) => boolean;
91
+ /**
92
+ * Сигнал «мой ответ изменился»: подписка, по которой поверхность пересчитывает меню.
93
+ *
94
+ * Нужен вкладу, чьи `when` и `toggled` зависят от состояния, о котором поверхность
95
+ * не знает вовсе. Пример, ради которого поле и появилось: кнопка markdown меняет значок
96
+ * вместе с режимом показа, а режим живёт в плагине — меню перерисовывать не с чего,
97
+ * и кнопка оставалась прежней, хотя документ уже показан иначе.
98
+ *
99
+ * Это ровно то же решение, что у вклада декорации ресурса (`./decorations`), и по той же
100
+ * причине: контекст применимости отвечает на вопрос «что происходит в приложении», а не
101
+ * «что происходит внутри плагина», и класть туда чужое состояние значило бы вносить
102
+ * предметное знание в платформу.
103
+ */
104
+ readonly onDidChange?: (cb: () => void) => Disposable;
105
+ }
106
+ /**
107
+ * То, ПО ЧЕМУ вызвали меню: строка дерева, узел канваса, вкладка. Для шапки — `undefined`.
108
+ *
109
+ * Непрозрачна для этого модуля намеренно. Меню — платформенная проекция реестра команд,
110
+ * и знать, что бывает строкой дерева, оно не вправе: сегодня контекстное меню открывают
111
+ * над ресурсом, завтра над узлом схемы, и перечислить это здесь значило бы менять модель
112
+ * меню на каждый новый вид поверхности. Типизированную форму цели объявляет тот, кто меню
113
+ * вызывает (для дерева — `./resource-menu`), и он же сужает `unknown` для своих вкладов.
114
+ *
115
+ * Отсюда и то, почему цель приходит ОТДЕЛЬНО от {@link WhenContext}: тот отвечает на вопрос
116
+ * «что происходит в приложении» и одинаков для палитры, клавиш и меню, а цель существует
117
+ * только в момент щелчка и только у контекстного вызова.
118
+ */
119
+ export type MenuTarget = unknown;
120
+ /** Пункт меню: ссылка на команду и ничего больше. */
121
+ export interface MenuItemContribution extends MenuPlacement {
122
+ readonly kind: 'item';
123
+ /** Идентификатор команды. Нет такой команды — пункта не будет. */
124
+ readonly command: string;
125
+ /** Аргументы команды. Уходят в `execute` как есть. */
126
+ readonly args?: unknown;
127
+ /**
128
+ * Аргументы, вычисляемые по цели щелчка. Заданы — {@link MenuItemContribution.args}
129
+ * не используется вовсе.
130
+ *
131
+ * Отдельным полем, а не «`args` может быть функцией»: аргументы команды — произвольное
132
+ * значение, и различать «функция как аргумент» от «функция, считающая аргумент» по типу
133
+ * означало бы запретить первое молча.
134
+ */
135
+ readonly argsOf?: (target: MenuTarget) => unknown;
136
+ /**
137
+ * Ключ заголовка, если он должен отличаться от заголовка команды. Разрешается словарём
138
+ * того, кто внёс ПУНКТ, — в отличие от заголовка команды, который принадлежит её владельцу.
139
+ *
140
+ * По умолчанию отсутствует, и берётся заголовок команды: одно действие — одно имя
141
+ * в палитре, в меню и у ассистента.
142
+ */
143
+ readonly titleKey?: string;
144
+ /**
145
+ * Состояние переключателя: галочка или точка радио. Отсутствие означает обычный пункт.
146
+ *
147
+ * Предикат, а не значение: состояние вычисляется на том же контексте, что и видимость,
148
+ * и держать его копию рядом означало бы иметь два ответа на один вопрос.
149
+ */
150
+ readonly toggled?: (ctx: WhenContext, target: MenuTarget) => boolean;
151
+ /**
152
+ * Значок — для поверхностей, где пункт рисуется КНОПКОЙ, а не строкой списка
153
+ * (`editor/title`). В списке он не показывается: ряд подписей со значками у одних
154
+ * пунктов и без значков у других читается хуже, чем ряд одних подписей.
155
+ *
156
+ * Отсутствие значка на кнопочной поверхности — не ошибка: такой пункт уходит под «…»,
157
+ * где у него есть место для подписи.
158
+ */
159
+ readonly icon?: ComponentType;
160
+ }
161
+ /**
162
+ * Подменю: заголовок в одном месте, содержимое — по собственному адресу.
163
+ *
164
+ * `submenu` — это путь, по которому в него вносят пункты. Заводит его тот, кто вносит
165
+ * заголовок, но наполнять может кто угодно: адрес и есть точка расширения.
166
+ */
167
+ export interface MenuSubmenuContribution extends MenuPlacement {
168
+ readonly kind: 'submenu';
169
+ /** Адрес содержимого, например `file/recent`. */
170
+ readonly submenu: MenuPath;
171
+ /** Ключ заголовка в словаре внёсшего. */
172
+ readonly titleKey: string;
173
+ }
174
+ /**
175
+ * Группа пунктов, состав которой известен только в рантайме: открытые панели, недавние
176
+ * проекты, шаблоны форм.
177
+ *
178
+ * Функция, а не список, потому что перечислить это заранее нельзя; зовётся при каждом
179
+ * пересчёте меню и обязана быть чистой и дешёвой — та же дисциплина, что у `when` панели.
180
+ */
181
+ export interface MenuDynamicContribution extends MenuPlacement {
182
+ readonly kind: 'dynamic';
183
+ readonly items: (ctx: WhenContext, target: MenuTarget) => readonly MenuDynamicItem[];
184
+ }
185
+ /** Пункт динамической группы. Готовая строка вместо ключа — имена файлов не переводятся. */
186
+ export interface MenuDynamicItem {
187
+ /** Уникален в пределах группы: служит React-ключом. */
188
+ readonly id: string;
189
+ readonly command: string;
190
+ readonly args?: unknown;
191
+ /** Готовый заголовок. Указан вместе с `titleKey` — выигрывает он. */
192
+ readonly title?: string;
193
+ /** Ключ заголовка в словаре внёсшего группу. */
194
+ readonly titleKey?: string;
195
+ /** Галочка. Значение, а не предикат: контекст группе уже дали. */
196
+ readonly toggled?: boolean;
197
+ }
198
+ /**
199
+ * Собственное корневое меню плагина.
200
+ *
201
+ * Встать перед «Файлом» или после «Справки» оно не может — см. {@link sortMenuRoots}: порядок
202
+ * корней держится зонами, а не сквозным числом, потому что числа со временем нарушают все.
203
+ */
204
+ export interface MenuRootContribution {
205
+ readonly kind: 'root';
206
+ /** Путь корня, по которому в него вносят пункты. Совпадение с корнем Host — отказ. */
207
+ readonly id: MenuPath;
208
+ readonly titleKey: string;
209
+ /** Порядок среди корней ПЛАГИНОВ. Без значения берётся `order` вклада. */
210
+ readonly order?: number;
211
+ }
212
+ /** Всё, что можно внести в точку расширения меню. */
213
+ export type MenuContribution = MenuItemContribution | MenuSubmenuContribution | MenuDynamicContribution | MenuRootContribution;
214
+ /**
215
+ * Точка расширения меню.
216
+ *
217
+ * Заполняется вкладами плагинов. Проекции платформенных реестров (список панелей, справка)
218
+ * вкладами НЕ являются — они приходят в {@link MenuBuildOptions.entries} от самой оболочки
219
+ * через {@link hostMenuEntry}: у корневого реестра метода `contribute` нет вовсе, и «вклад,
220
+ * внесённый Host» невыразим по построению.
221
+ *
222
+ * Недавние проекты сюда не относятся, хотя список тоже платформенный: подменю `file/recent`
223
+ * вносит плагин файлов — тем же порядком, что и «Открыть папку…». Решение «показать проекты
224
+ * в «Файле» и открывать их вот этой командой» предметное, а данные и глагол «открыть
225
+ * по записи» плагин получает портом.
226
+ */
227
+ export declare const MenuPoint: import('../../internal.js').ExtensionPoint<MenuContribution>;
228
+ export {};
@@ -0,0 +1,36 @@
1
+ import { WhenContext } from '../../primitives/when-context.js';
2
+ /**
3
+ * Пункт палитры.
4
+ *
5
+ * `titleKey` **либо** `title`: первое — для того, что переводится, второе — для динамических
6
+ * данных. Указаны оба — выигрывает `title`: готовая строка уже содержит то, что человек
7
+ * ожидает увидеть, а ключ рядом с ней означает, что вносящий не решил, и молча предпочесть
8
+ * перевод значило бы показать не тот текст.
9
+ */
10
+ export interface PaletteItem {
11
+ /** Уникален в пределах палитры. Служит React-ключом и адресом при слиянии. */
12
+ readonly id: string;
13
+ /** Ключ i18n — либо он… */
14
+ readonly titleKey?: string;
15
+ /** …либо готовая строка для динамических данных (имя файла). */
16
+ readonly title?: string;
17
+ /** Пояснение справа: путь, раздел, сочетание клавиш. Участвует в поиске. */
18
+ readonly detail?: string;
19
+ readonly run: () => unknown | Promise<unknown>;
20
+ /** Меньше — выше. По умолчанию `0`. */
21
+ readonly order?: number;
22
+ }
23
+ /**
24
+ * Поставщик динамических пунктов.
25
+ *
26
+ * `provide` получает запрос и контекст и вправе отвечать асинхронно. Отмены в сигнатуре нет
27
+ * намеренно: поставщик не обязан уметь прерываться, а устаревший ответ отбрасывает вызывающий
28
+ * (см. {@link createPaletteQueryRunner}). Требовать `AbortSignal` от каждого поставщика значило
29
+ * бы усложнить простой случай ради того, что и так решается на стороне палитры.
30
+ */
31
+ export interface PaletteItemProvider {
32
+ readonly id: string;
33
+ provide(query: string, ctx: WhenContext): PaletteItem[] | Promise<PaletteItem[]>;
34
+ }
35
+ /** Точка расширения динамических пунктов палитры. */
36
+ export declare const PaletteItemsPoint: import('../../internal.js').ExtensionPoint<PaletteItemProvider>;
@@ -0,0 +1,45 @@
1
+ import { ResourceId, ResourceRef } from '../../primitives/resource.js';
2
+ import { WhenContext } from '../../primitives/when-context.js';
3
+ import { ContextMenuId, MenuTarget } from './menu.js';
4
+ /** Адрес контекстного меню дерева: в него вносят пункты те, кому есть что предложить. */
5
+ export declare const RESOURCE_CONTEXT_MENU: ContextMenuId;
6
+ /** По чему щёлкнули в дереве. */
7
+ export interface ResourceMenuTarget {
8
+ /**
9
+ * Строка, по которой щёлкнули. `null` — щелчок по пустому месту панели, и это законный
10
+ * случай: «Новый файл…» там осмысленен, «Переименовать» — нет.
11
+ */
12
+ readonly ref: ResourceRef | null;
13
+ /** Каталог, ВНУТРЬ которого действуют пункты создания. Считается по правилу модуля. */
14
+ readonly dir: ResourceId;
15
+ /**
16
+ * К чему пункт применится: набор, если щёлкнули по его строке, иначе одна строка.
17
+ * Считает дерево (`actionTargets` в `./resource-tree`) — это его состояние, а не меню.
18
+ */
19
+ readonly selection: readonly ResourceRef[];
20
+ /** Корень показа: им вклад отличает «щёлкнули по проекту» от «щёлкнули по файлу». */
21
+ readonly rootId: ResourceId;
22
+ }
23
+ /**
24
+ * Сужает непрозрачную цель модели меню до цели дерева; `null` — меню открыли не над деревом.
25
+ *
26
+ * Проверка структурная, а не `instanceof`: цель проходит через модель меню как `unknown`,
27
+ * и вклад плагина обязан уметь получить `null`, если его пункт по ошибке внесли в чужое меню.
28
+ */
29
+ export declare function asResourceTarget(target: MenuTarget): ResourceMenuTarget | null;
30
+ /**
31
+ * Предикат видимости пункта по цели щелчка.
32
+ *
33
+ * Обёртка ради одного: без неё каждый вклад начинался бы с приведения `unknown` и проверки
34
+ * на `null`, то есть с трёх строк, которые все напишут по-разному.
35
+ */
36
+ export declare function whenResource(predicate: (target: ResourceMenuTarget, ctx: WhenContext) => boolean): (ctx: WhenContext, target: MenuTarget) => boolean;
37
+ /**
38
+ * Аргументы команды по цели щелчка.
39
+ *
40
+ * `undefined` для чужой цели — команда получит его вместо адресов и откажется сама, что
41
+ * честнее выдуманного аргумента.
42
+ */
43
+ export declare function argsOfResource<T>(compute: (target: ResourceMenuTarget) => T): (target: MenuTarget) => T | undefined;
44
+ /** Адреса выделенных ресурсов — самая частая форма аргументов команд дерева. */
45
+ export declare function selectedIds(target: ResourceMenuTarget): readonly ResourceId[];
@@ -0,0 +1,113 @@
1
+ import { ComponentType } from 'react';
2
+ import { WhenContext } from '../primitives/when-context.js';
3
+ /**
4
+ * Область оболочки, в которую можно внести панель.
5
+ *
6
+ * - `rail.left` — вертикальные вкладки слева: переключатель левой панели и мелкие кнопки;
7
+ * - `panel.left` / `panel.right` / `panel.bottom` — доки вокруг центра;
8
+ * - `toolbar` / `statusbar` — горизонтальные полосы сверху и снизу;
9
+ * - `editor.main` — центр.
10
+ *
11
+ * Вкладки документов Host держит сам: вкладка — это открытый ресурс, а открытые ресурсы
12
+ * принадлежат Workspace, поэтому слота под них нет.
13
+ */
14
+ export type SlotId = 'rail.left' | 'panel.left' | 'panel.right' | 'panel.bottom' | 'toolbar' | 'statusbar' | 'editor.main';
15
+ /** Все слоты в порядке обхода. Нужен диагностике и тестам, а не раскладке. */
16
+ export declare const SLOT_IDS: readonly SlotId[];
17
+ /** Проверка непрозрачной строки (настройки, диагностика) на принадлежность набору слотов. */
18
+ export declare function isSlotId(value: unknown): value is SlotId;
19
+ /**
20
+ * Панель — единица содержимого слота.
21
+ *
22
+ * `titleKey`, а не готовая строка: панель регистрируется один раз при активации плагина,
23
+ * а локаль меняется потом, и переведённый в момент регистрации заголовок остался бы
24
+ * на языке, который был активен тогда. Ключ разрешается в пространстве имён внёсшего
25
+ * плагина — см. panelTitle в ./panels.
26
+ */
27
+ export interface PanelContribution {
28
+ /**
29
+ * Идентификатор панели. Приходит в `Body` как `panelId` и служит адресом в настройках
30
+ * (какая вкладка дока активна).
31
+ *
32
+ * Уникальность в пределах точки расширения им **не** гарантируется: реестр проверяет
33
+ * только явный `meta.id` вклада. Поэтому React-ключом служит идентификатор вклада,
34
+ * а не этот.
35
+ */
36
+ readonly id: string;
37
+ readonly slot: SlotId;
38
+ /** Ключ i18n заголовка — в пространстве имён плагина, внёсшего панель. */
39
+ readonly titleKey: string;
40
+ /** Значок для рейла и шапки дока. Без него рейл покажет первые буквы заголовка. */
41
+ readonly icon?: ComponentType;
42
+ readonly Body: ComponentType<{
43
+ panelId: string;
44
+ }>;
45
+ /**
46
+ * Показывать ли панель сейчас. Отсутствие предиката означает «всегда».
47
+ *
48
+ * Предикат обязан быть чистым и быстрым: он вычисляется на каждое изменение
49
+ * {@link WhenContext} для каждой панели каждого слота.
50
+ */
51
+ readonly when?: (ctx: WhenContext) => boolean;
52
+ /**
53
+ * Порядок внутри слота; меньше — раньше. Без значения берётся `order` вклада.
54
+ *
55
+ * Два источника порядка — не дублирование: `order` вклада принадлежит регистрации
56
+ * (им плагин расставляет свои вклады между собой), а этот — самой панели, и он
57
+ * переживает перерегистрацию.
58
+ */
59
+ readonly order?: number;
60
+ /**
61
+ * Где стоит вкладка панели на рейле: в основной группе или прижатой к низу.
62
+ *
63
+ * Не то же, что {@link PanelContribution.order}. Порядок расставляет панели ВНУТРИ
64
+ * группы; эта пара говорит, к какому краю рейла панель принадлежит. Выразить прижатие
65
+ * к низу через большой порядок нельзя: «последняя в списке» и «у нижнего края»
66
+ * совпадают только пока рейл заполнен целиком.
67
+ *
68
+ * Нижняя группа — для того, что человек зовёт, а не просматривает: помощь, ассистент,
69
+ * настройки. Их место на краю постоянно, поэтому рука находит их не глядя, сколько бы
70
+ * панелей ни добавили сверху.
71
+ */
72
+ readonly railPlacement?: 'top' | 'bottom';
73
+ /**
74
+ * Значок на вкладке панели: число находок, точка «есть новое» и подобное.
75
+ *
76
+ * КОМПОНЕНТ, а не число: панель сама знает, на что подписаться и как часто это меняется,
77
+ * а оболочка не должна ни того ни другого. Верни он число — оболочке пришлось бы
78
+ * перевычислять его на каждый свой кадр либо завести отдельный канал уведомлений
79
+ * ради одной цифры.
80
+ *
81
+ * Значок — единственное, что видно у панели в свёрнутом доке, и ради него свёрнутое
82
+ * состояние и существует: полоса вкладок остаётся, чтобы ошибку было видно, не разворачивая.
83
+ */
84
+ readonly Badge?: ComponentType<{
85
+ panelId: string;
86
+ }>;
87
+ /**
88
+ * Действия панели — то, что стоит в шапке дока справа от заголовка.
89
+ *
90
+ * КОМПОНЕНТ по той же причине, что {@link PanelContribution.Badge}: доступность действия
91
+ * зависит от состояния, которое знает панель («перечитать» имеет смысл всегда, «сохранить»
92
+ * — только когда есть что), а оболочка ни этого состояния, ни поводов перерисоваться
93
+ * не имеет.
94
+ *
95
+ * Почему не в теле панели: кнопка, стоящая первой строкой содержимого, уезжает вместе
96
+ * с ним при прокрутке и занимает высоту у списка — а шапка дока уже нарисована и пуста
97
+ * справа. Тело панели — про содержимое; постоянные действия над ним — про шапку.
98
+ *
99
+ * Рисуется только там, где шапка есть, — в левом и правом доках. У нижнего её нет вовсе:
100
+ * его роль играет полоса вкладок.
101
+ */
102
+ readonly Actions?: ComponentType<{
103
+ panelId: string;
104
+ }>;
105
+ }
106
+ /**
107
+ * Точка расширения панелей.
108
+ *
109
+ * Заполняется только через `PluginContext.extensions`: у корневого реестра метода
110
+ * `contribute` нет вовсе, поэтому «панель, внесённая самим Host» невыразима, и вопрос
111
+ * «откуда здесь эта панель» всегда имеет ответ.
112
+ */
113
+ export declare const PanelPoint: import('../internal.js').ExtensionPoint<PanelContribution>;
@@ -0,0 +1,5 @@
1
+ import { ResourceId } from '../primitives/resource.js';
2
+ import { DocumentsService } from '../services/documents.js';
3
+ /** Служба документов в объёме хука: снимок и уведомление. */
4
+ export type ActiveDocumentSource = Pick<DocumentsService, 'activeResource' | 'onDidChange'>;
5
+ export declare function useActiveDocument(documents: ActiveDocumentSource): ResourceId | null;
@@ -0,0 +1,9 @@
1
+ import { PluginI18n } from '../services/i18n.js';
2
+ /**
3
+ * Текущая локаль — как повод перерисоваться, а не как значение.
4
+ *
5
+ * `t()` читается прямо из сервиса, но результат меняется при смене локали, а сервис
6
+ * не является React-состоянием. Этот хук и есть недостающая связь: он ничего не переводит,
7
+ * он делает перевод реактивным.
8
+ */
9
+ export declare function useLocale(i18n: Pick<PluginI18n, 'locale' | 'onDidChangeLocale'>): string;
@@ -0,0 +1,4 @@
1
+ import { PluginI18n } from '../services/i18n.js';
2
+ /** Перевод: ключ и параметры сообщения. Совпадает по форме с `PluginI18n.t`. */
3
+ export type Translate = (key: string, params?: Record<string, unknown>) => string;
4
+ export declare function useTranslate(i18n: Pick<PluginI18n, 'locale' | 't' | 'onDidChangeLocale'>): Translate;
@@ -0,0 +1,42 @@
1
+ import { Disposable } from '../primitives/disposable.js';
2
+ import { ResourceId, ResourceRef } from '../primitives/resource.js';
3
+ /**
4
+ * Вид документа — дискриминант союза `TextDocument | ModelDocument`.
5
+ *
6
+ * Он объявлен ЗДЕСЬ, а не в `model/`, потому что документ без провайдера модели должен
7
+ * оставаться текстовым, ничем себя для этого не оборачивая: «остаётся текстовым» —
8
+ * это и есть определение `TextDocument`, и обёртка вокруг него означала бы две ссылки
9
+ * на один документ, из которых свежая только одна.
10
+ *
11
+ * Сам буфер о видах не знает: фабрика документа в оболочке всегда делает `'text'`, а `'model'`
12
+ * появляется у документа, которому провайдер модели дал вторую истину (`model/model-document.ts`).
13
+ */
14
+ export type DocumentKind = 'text' | 'model';
15
+ /** Открытый ресурс: вкладка. Живёт, пока открыт, — в отличие от ресурса, который живёт в источнике. */
16
+ export interface Document {
17
+ /**
18
+ * Различает два вида документа. Поле, а не метод: оно не меняется за жизнь документа,
19
+ * и сужение союза обязано работать без вызова.
20
+ */
21
+ readonly kind: DocumentKind;
22
+ readonly id: ResourceId;
23
+ /** Ссылка на ресурс: путь, вид, медиатип. Стабильна на всё время жизни документа. */
24
+ readonly ref: ResourceRef;
25
+ /**
26
+ * Текущий текст буфера — то, что уйдёт в файл при сохранении.
27
+ *
28
+ * Метод, а не поле: буфер меняется, а `useSyncExternalStore` подписывается на
29
+ * {@link Document.onDidChangeContent} и читает снимок — поле заставило бы держать
30
+ * ссылку на документ и следить за мутацией самому.
31
+ */
32
+ getText(): string;
33
+ /** Расходится ли буфер с BASE — тем, что отдал источник. */
34
+ isDirty(): boolean;
35
+ /**
36
+ * Буфер сменился: правкой через Workspace, откатом или слиянием.
37
+ *
38
+ * Обработчик обязан быть дешёвым: уведомление синхронно относительно смены буфера,
39
+ * и тяжёлую работу (разбор, валидацию, перерисовку) планирует себе сам подписчик.
40
+ */
41
+ onDidChangeContent(cb: (text: string) => void): Disposable;
42
+ }
@@ -0,0 +1,29 @@
1
+ import { Capability } from '../../primitives/capability.js';
2
+ import { ResourceId } from '../../primitives/resource.js';
3
+ /** Вид хранилища для ОДНОГО редактора: ровно то, чем пользуется его тело. */
4
+ export interface EditorViewStateSlice {
5
+ /** Запоминает снимок. Повторная запись того же значения — дело вызывающего. */
6
+ record(id: ResourceId, state: unknown): void;
7
+ /** Последний записанный снимок или `undefined`. Разбирает его читающий. */
8
+ peek(id: ResourceId): unknown;
9
+ /** Забывает снимок этого документа у ЭТОГО редактора. */
10
+ forget(id: ResourceId): void;
11
+ }
12
+ export interface EditorViewStates {
13
+ /**
14
+ * Вид на хранилище от лица редактора `editorId`.
15
+ *
16
+ * `editorId` — идентификатор ВКЛАДА редактора (`EditorContribution.id`), а не плагина:
17
+ * плагин вправе внести два редактора, и снимки у них разные.
18
+ */
19
+ forEditor(editorId: string): EditorViewStateSlice;
20
+ }
21
+ /**
22
+ * Возможность «снимки вида редакторов».
23
+ *
24
+ * Провайдер — оболочка (`services/host-capabilities`), а не редактор: хранилище делят
25
+ * ВСЕ редакторы, и принадлежать оно не может ни одному из них. Версия `1.0.0` — исходная.
26
+ */
27
+ export declare const EditorViewStatesCapability: Capability<EditorViewStates>;
28
+ /** Токен службы — ТОТ ЖЕ объект: возможность расширяет токен, второго реестра нет. */
29
+ export declare const EditorViewStatesToken: Capability<EditorViewStates>;
@@ -0,0 +1,86 @@
1
+ import { Disposable } from '../../primitives/disposable.js';
2
+ import { Document } from '../document.js';
3
+ import { ApplyResult, EditOp, NodeId } from './provider.js';
4
+ /** Согласован ли буфер с моделью. `diverged` — буфер не разбирается, модель прежняя. */
5
+ export type DocumentSyncState = 'synced' | 'diverged';
6
+ /** Отказ разбора: чей и почему. */
7
+ export interface ParseFailure {
8
+ readonly providerId: string;
9
+ /** Сообщение провайдера: годится для диагностики и для лога, не для интерфейса. */
10
+ readonly message: string;
11
+ /** Исходная ошибка: у разбора с позициями в ней лежит смещение. */
12
+ readonly error?: unknown;
13
+ }
14
+ /** Что вызвало смену состояния модельного документа. */
15
+ export type ModelChangeReason = 'apply' | 'parse' | 'undo' | 'redo' | 'selection';
16
+ export interface ModelChange<M> {
17
+ readonly model: M;
18
+ readonly selection: readonly NodeId[];
19
+ readonly syncState: DocumentSyncState;
20
+ readonly reason: ModelChangeReason;
21
+ }
22
+ /**
23
+ * Документ с моделью. Наследует `Document` целиком: для текстового редактора модельный документ
24
+ * ничем не отличается от обычного, и это осознанно.
25
+ */
26
+ export interface ModelDocument<M = unknown> extends Document {
27
+ readonly kind: 'model';
28
+ /** Чей разбор. По нему плагин стека узнаёт СВОЙ документ и сужает модель. */
29
+ readonly providerId: string;
30
+ /** Последняя валидная модель. В расхождении — та, что была до поломки буфера. */
31
+ getModel(): M;
32
+ getSyncState(): DocumentSyncState;
33
+ /** `undefined`, когда согласовано. */
34
+ getParseFailure(): ParseFailure | undefined;
35
+ /** Выделение — часть модели правки: операция переносит его на `focus`, оно входит в снимок отмены. */
36
+ getSelection(): readonly NodeId[];
37
+ /** Ложь в расхождении: структурные редакторы там только на чтение. */
38
+ isStructurallyEditable(): boolean;
39
+ onDidChangeModel(cb: (change: ModelChange<M>) => void): Disposable;
40
+ }
41
+ /** Отказ применить операцию. Не исключение: оба случая — нормальные состояния, а не аварии. */
42
+ export type ApplyRejection =
43
+ /** Буфер не разбирается: правка модели затёрла бы работу пользователя при перерисовке. */
44
+ {
45
+ readonly status: 'rejected';
46
+ readonly reason: 'diverged';
47
+ readonly failure: ParseFailure;
48
+ }
49
+ /** Провайдер не смог применить операцию: неизвестный тип, исчезнувшая цель, битые параметры. */
50
+ | {
51
+ readonly status: 'rejected';
52
+ readonly reason: 'provider-error';
53
+ readonly error: unknown;
54
+ };
55
+ export type ApplyOutcome<M> = ({
56
+ readonly status: 'applied';
57
+ } & ApplyResult<M>) | ApplyRejection;
58
+ export interface ApplyOptions {
59
+ /** Ключ схлопывания в истории, обычно `свойство@узел`. */
60
+ readonly mergeKey?: string;
61
+ }
62
+ /**
63
+ * Ручки управления модельным документом — пишущее лицо.
64
+ *
65
+ * Разведено с {@link ModelDocument} так же, как документ и его владелец: читать может любой,
66
+ * а править — только через ручку, мимо истории и перерисовки буфера не пройти. Времени жизни
67
+ * здесь нет (`dispose`): ручкой владеет тот, кто открыл документ, то есть оболочка.
68
+ */
69
+ export interface ModelDocumentHandle<M> {
70
+ readonly document: ModelDocument<M>;
71
+ /** Применяет операцию: история, перенос выделения на `focus`, перерисовка буфера. */
72
+ apply(op: EditOp, options?: ApplyOptions): ApplyOutcome<M>;
73
+ setSelection(selection: readonly NodeId[]): void;
74
+ /** `false`, если отменять нечего или документ в расхождении. */
75
+ undo(): boolean;
76
+ redo(): boolean;
77
+ /** Ответит ли {@link undo} согласием; расхождение учтено здесь же. */
78
+ canUndo(): boolean;
79
+ canRedo(): boolean;
80
+ /** Явная граница схлопывания: конец хода ассистента, уход фокуса с поля. */
81
+ breakUndoMerge(): void;
82
+ /** Выполняет отложенную перерисовку буфера и дожидается записи. */
83
+ flush(): Promise<void>;
84
+ /** Ждёт ли документ перерисовки буфера, отложенной из-за фокуса. */
85
+ hasPendingSync(): boolean;
86
+ }