@rigee/plugin-api 0.0.0-stage → 0.2.0
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.
- package/README.md +137 -2
- package/dist/context.d.ts +130 -0
- package/dist/context.js +23 -0
- package/dist/data.d.ts +62 -0
- package/dist/data.js +14 -0
- package/dist/host.d.ts +460 -0
- package/dist/host.js +234 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +21 -0
- package/dist/manifest.d.ts +331 -0
- package/dist/manifest.js +70 -0
- package/dist/ui.d.ts +106 -0
- package/dist/ui.js +12 -0
- package/package.json +45 -4
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell, as a plugin's renderer half sees it.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS IS A SHIM AND NOT AN IMPLEMENTATION. A plugin bundled with the app could simply import
|
|
5
|
+
* the shell's modules; a plugin from somebody else's repository cannot — it is built separately,
|
|
6
|
+
* against this package, and only meets the shell at runtime. So the package declares the SHAPE and
|
|
7
|
+
* the shell installs the BEHAVIOUR, once, before any plugin activates. Both kinds of plugin then
|
|
8
|
+
* write the same import and get the same object, and there is exactly one implementation to keep
|
|
9
|
+
* correct.
|
|
10
|
+
*
|
|
11
|
+
* It matters that there is exactly one copy of this module in the window. Everything below reads
|
|
12
|
+
* module-level state, so two copies would mean a plugin talking to a host nobody installed. For
|
|
13
|
+
* bundled plugins the build guarantees it (one alias, one chunk); for disk plugins the renderer's
|
|
14
|
+
* import map points `@rigee/plugin-api` at the shell's own copy, which is the entire reason that
|
|
15
|
+
* import map exists.
|
|
16
|
+
*
|
|
17
|
+
* CORDIS-FREE, ON PURPOSE. A plugin uses `ctx` for things that must be revoked when it is
|
|
18
|
+
* disposed — binding components, subscribing to its main half — and this module for everything
|
|
19
|
+
* else. A cordis service access costs ~15 us against ~8 ns for a plain call, which is unacceptable
|
|
20
|
+
* inside a React render, and none of this needs a lifecycle.
|
|
21
|
+
*/
|
|
22
|
+
import type { FileStat, TabId, Workspace, WorkspaceId } from './data';
|
|
23
|
+
import type { PluginId, ViewType } from './manifest';
|
|
24
|
+
import type { IconSet, NoticeProps } from './ui';
|
|
25
|
+
import type { ComponentType, ReactElement } from 'react';
|
|
26
|
+
export interface PluginTabRequest {
|
|
27
|
+
viewType: ViewType;
|
|
28
|
+
title: string;
|
|
29
|
+
/** Hover text for the tab strip — the path, the URL, whatever identifies it to a reader. */
|
|
30
|
+
tooltip?: string;
|
|
31
|
+
state: Record<string, unknown>;
|
|
32
|
+
/**
|
|
33
|
+
* Identity for de-duplication. Opening the same key twice re-activates the existing tab rather
|
|
34
|
+
* than adding a second one; leave it out when duplicates are legitimate.
|
|
35
|
+
*
|
|
36
|
+
* De-duplication is PER EDITOR GROUP: the same key open in two columns is two tabs, because the
|
|
37
|
+
* same document side by side is what splitting is for.
|
|
38
|
+
*/
|
|
39
|
+
key?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Open beside this tab — in whichever group holds it — instead of in the active group.
|
|
42
|
+
*
|
|
43
|
+
* For a tab opened BY another tab rather than by the user. A `window.open` popup from a page in
|
|
44
|
+
* the right-hand group belongs in the right-hand group: the user's focus may be anywhere by the
|
|
45
|
+
* time the page gets round to opening it, and landing in the wrong column looks like the click
|
|
46
|
+
* did something else entirely. Ignored when the tab is gone or lives in another workspace.
|
|
47
|
+
*/
|
|
48
|
+
nearTabId?: TabId;
|
|
49
|
+
}
|
|
50
|
+
/** Open a file in whichever viewer claims it. What clicking a file in a tree should do. */
|
|
51
|
+
export interface OpenFileRequest {
|
|
52
|
+
/** Absolute path. */
|
|
53
|
+
path: string;
|
|
54
|
+
/** Tab title. Defaults to the basename. */
|
|
55
|
+
title?: string;
|
|
56
|
+
readOnly?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Open it in this editor instead of the one the shell would pick — one `editorsOnRequest`
|
|
59
|
+
* offered. Ignored unless that editor claims the file, so a stale menu still opens something.
|
|
60
|
+
*/
|
|
61
|
+
viewType?: ViewType;
|
|
62
|
+
/** As on `PluginTabRequest`: put it beside this tab rather than in the active group. */
|
|
63
|
+
nearTabId?: TabId;
|
|
64
|
+
/**
|
|
65
|
+
* Where to put the cursor — a search result's match. Delivered to the editor through
|
|
66
|
+
* `onRevealInFile`, whether the tab is new or was already open.
|
|
67
|
+
*/
|
|
68
|
+
selection?: TextSelection;
|
|
69
|
+
/**
|
|
70
|
+
* Leave the keyboard where it is instead of handing it to the file's viewer. What a single click
|
|
71
|
+
* in a file tree does, so the tree's arrow keys and Delete go on acting on the tree. Without it
|
|
72
|
+
* the viewer takes the keyboard — as it mounts, or at once if the file is already on screen.
|
|
73
|
+
*/
|
|
74
|
+
preserveFocus?: boolean;
|
|
75
|
+
}
|
|
76
|
+
/** A file or folder a plugin renamed or moved: where it was, and where it is now. */
|
|
77
|
+
export interface FileMove {
|
|
78
|
+
from: string;
|
|
79
|
+
to: string;
|
|
80
|
+
}
|
|
81
|
+
/** A place in a text file. 1-based, columns in UTF-16 code units, as Monaco counts them. */
|
|
82
|
+
export interface TextSelection {
|
|
83
|
+
line: number;
|
|
84
|
+
column: number;
|
|
85
|
+
/** Defaults to `line`. */
|
|
86
|
+
endLine?: number;
|
|
87
|
+
/** Defaults to `column`: a caret, not a range. */
|
|
88
|
+
endColumn?: number;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* How the shell saves or drops a file's unsaved changes, when a tab closing or the app quitting
|
|
92
|
+
* asks the user and the user answers. Both reject to say they could not.
|
|
93
|
+
*/
|
|
94
|
+
export interface FileHandlers {
|
|
95
|
+
save: () => Promise<void>;
|
|
96
|
+
discard: () => Promise<void>;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* One run of a line of code that tokenises as one thing: where it starts, and what it is.
|
|
100
|
+
*
|
|
101
|
+
* `type` is a Monarch token type, as the text editor's tokenizers name them — `keyword.ts`,
|
|
102
|
+
* `string.escape.js`, `attribute.value.html`. The first segment is the kind; the last is the
|
|
103
|
+
* language and rarely matters.
|
|
104
|
+
*/
|
|
105
|
+
export interface CodeToken {
|
|
106
|
+
/** In UTF-16 code units from the start of the line. */
|
|
107
|
+
offset: number;
|
|
108
|
+
type: string;
|
|
109
|
+
}
|
|
110
|
+
/** What `pluginTabs` reports about one of a plugin's own tabs. */
|
|
111
|
+
/**
|
|
112
|
+
* One row of a context menu a plugin asks the shell to show: a command, or a separator. Plain data;
|
|
113
|
+
* what a command does stays with the plugin, which gets back the `id` of the row picked.
|
|
114
|
+
*/
|
|
115
|
+
export type MenuItem = {
|
|
116
|
+
kind: 'item';
|
|
117
|
+
id: string;
|
|
118
|
+
label: string;
|
|
119
|
+
enabled?: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* The shortcut drawn beside the label, in Electron's accelerator syntax: `CmdOrCtrl+C`. Shown,
|
|
122
|
+
* not listened for — the plugin answers the key itself, wherever it has the keyboard.
|
|
123
|
+
*/
|
|
124
|
+
accelerator?: string;
|
|
125
|
+
} | {
|
|
126
|
+
kind: 'separator';
|
|
127
|
+
};
|
|
128
|
+
export interface PluginTabInfo {
|
|
129
|
+
tabId: TabId;
|
|
130
|
+
state: Record<string, unknown>;
|
|
131
|
+
key?: string;
|
|
132
|
+
}
|
|
133
|
+
/** An editor that opens a file only when asked, as a file's menu offers it. */
|
|
134
|
+
export interface EditorOffer {
|
|
135
|
+
viewType: ViewType;
|
|
136
|
+
/** In the editor's own words, from its manifest: `Open Preview`. */
|
|
137
|
+
label: string;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Everything the shell provides. Implemented once, in the shell; see `installShellHost`.
|
|
141
|
+
*
|
|
142
|
+
* Adding a method here is backwards compatible. Removing or reshaping one is not — bump
|
|
143
|
+
* `PLUGIN_API_VERSION` when you do, because a disk plugin built against the old shape is refused
|
|
144
|
+
* at discovery rather than left to fail at a call site.
|
|
145
|
+
*/
|
|
146
|
+
export interface ShellHost {
|
|
147
|
+
isMac: () => boolean;
|
|
148
|
+
/** The font stack the shell's editors use, so a plugin's does not look foreign. */
|
|
149
|
+
monoFamily: () => string;
|
|
150
|
+
editorFontSize: () => number;
|
|
151
|
+
activeWorkspaceId: () => WorkspaceId | null;
|
|
152
|
+
/** React hook. Re-renders when the workspace changes. */
|
|
153
|
+
useActiveWorkspace: () => Workspace | null;
|
|
154
|
+
/**
|
|
155
|
+
* The workspace's own folder — the one its `.code-workspace` file is in, where its terminals
|
|
156
|
+
* start — or null for a workspace with no file: never saved, a plain folder, Scratch. Not
|
|
157
|
+
* necessarily one of `folders`, and usually not: those are the repos it groups.
|
|
158
|
+
*/
|
|
159
|
+
workspaceDir: (workspace: Workspace) => string | null;
|
|
160
|
+
addWorkspaceFolders: (paths: string[]) => Promise<void>;
|
|
161
|
+
/**
|
|
162
|
+
* Take some of the active workspace's `folders` out of it, and out of its `.code-workspace` file
|
|
163
|
+
* when it has one. Only the workspace changes: the folders stay on disk, and so do the tabs of
|
|
164
|
+
* files in them. Paths it does not list are ignored.
|
|
165
|
+
*/
|
|
166
|
+
removeWorkspaceFolders: (paths: string[]) => Promise<void>;
|
|
167
|
+
/**
|
|
168
|
+
* Open a terminal in the active workspace whose shell starts in `cwd` — a folder the user picked,
|
|
169
|
+
* so it never stops to ask about saving the workspace. `tab` (the default) is a new terminal tab;
|
|
170
|
+
* `split` is a new pane beside the active one in the terminal on screen, or a new tab when no
|
|
171
|
+
* terminal is showing. Returns the tab it is in, or null with no workspace to put it in.
|
|
172
|
+
*/
|
|
173
|
+
openTerminal: (request: {
|
|
174
|
+
cwd: string;
|
|
175
|
+
where?: 'tab' | 'split';
|
|
176
|
+
}) => TabId | null;
|
|
177
|
+
openPluginTab: (pluginId: PluginId, request: PluginTabRequest) => TabId | null;
|
|
178
|
+
openFile: (request: OpenFileRequest) => TabId | null;
|
|
179
|
+
editorsOnRequest: (path: string) => EditorOffer[];
|
|
180
|
+
patchPluginTab: (tabId: TabId, patch: {
|
|
181
|
+
title?: string;
|
|
182
|
+
tooltip?: string;
|
|
183
|
+
key?: string;
|
|
184
|
+
state?: Record<string, unknown>;
|
|
185
|
+
}) => void;
|
|
186
|
+
closePluginTab: (tabId: TabId) => void;
|
|
187
|
+
activateTab: (tabId: TabId) => void;
|
|
188
|
+
pluginTabs: (viewType: ViewType) => PluginTabInfo[];
|
|
189
|
+
liveTabIds: () => Set<TabId>;
|
|
190
|
+
subscribeTabs: (listener: (live: Set<TabId>) => void) => () => void;
|
|
191
|
+
duplicatedFrom: (tabId: TabId) => TabId | null;
|
|
192
|
+
focusedTabId: () => TabId | null;
|
|
193
|
+
useFocusedTabId: () => TabId | null;
|
|
194
|
+
mountedTabIds: () => TabId[];
|
|
195
|
+
subscribeMountedTabs: (listener: (tabIds: TabId[]) => void) => () => void;
|
|
196
|
+
pluginState: (pluginId: PluginId, workspaceId: WorkspaceId) => Record<string, unknown> | undefined;
|
|
197
|
+
usePluginState: (pluginId: PluginId, workspaceId: WorkspaceId | null) => Record<string, unknown> | undefined;
|
|
198
|
+
setPluginState: (pluginId: PluginId, workspaceId: WorkspaceId, patch: Record<string, unknown>) => void;
|
|
199
|
+
setFileDirty: (path: string, dirty: boolean, handlers?: FileHandlers) => void;
|
|
200
|
+
onRevealInFile: (tabId: TabId, cb: (selection: TextSelection) => void) => () => void;
|
|
201
|
+
onViewRevealed: (viewId: string, cb: () => void) => () => void;
|
|
202
|
+
openedInBackground: (tabId: TabId) => boolean;
|
|
203
|
+
filesMoved: (moves: FileMove[]) => void;
|
|
204
|
+
filesDeleted: (paths: string[]) => void;
|
|
205
|
+
unsavedFiles: (paths: string[]) => string[];
|
|
206
|
+
setFileDecorations: (source: string, decorations: Record<string, FileDecoration>) => void;
|
|
207
|
+
useFileDecoration: (path: string) => FileDecoration | undefined;
|
|
208
|
+
readFile: (path: string) => Promise<{
|
|
209
|
+
content: string;
|
|
210
|
+
truncated: boolean;
|
|
211
|
+
tooLarge: boolean;
|
|
212
|
+
notUtf8?: boolean;
|
|
213
|
+
}>;
|
|
214
|
+
writeFile: (path: string, content: string) => Promise<void>;
|
|
215
|
+
listDir: (path: string) => Promise<FileStat[]>;
|
|
216
|
+
tokenizeCode: (text: string, language: string) => Promise<CodeToken[][] | null>;
|
|
217
|
+
workerUrl: (script: string | URL) => string;
|
|
218
|
+
/**
|
|
219
|
+
* A native context menu at the pointer: the id of the row picked, or null when dismissed. Native
|
|
220
|
+
* because a browser tab's page is drawn over the whole document, and a menu drawn in it can end
|
|
221
|
+
* up underneath.
|
|
222
|
+
*/
|
|
223
|
+
popupMenu: (items: MenuItem[]) => Promise<string | null>;
|
|
224
|
+
icons: IconSet;
|
|
225
|
+
Notice: ComponentType<NoticeProps>;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Called by the shell, exactly once, before any plugin activates.
|
|
229
|
+
*
|
|
230
|
+
* Throwing on a second call is not pedantry: a second install means a second copy of this module
|
|
231
|
+
* got loaded, and the plugins that imported the first copy are now talking to a host the shell has
|
|
232
|
+
* stopped updating. That failure is invisible — components render, calls resolve, and one plugin's
|
|
233
|
+
* tabs quietly go to the wrong place. Far better to fail at the install.
|
|
234
|
+
*/
|
|
235
|
+
export declare function installShellHost(impl: ShellHost): void;
|
|
236
|
+
/** True once the shell has installed itself. For the shell's own start-up assertions. */
|
|
237
|
+
export declare function shellHostInstalled(): boolean;
|
|
238
|
+
/**
|
|
239
|
+
* The shell's icons, filled in at install.
|
|
240
|
+
*
|
|
241
|
+
* A mutable object rather than named exports because named exports cannot be assigned from
|
|
242
|
+
* outside the module, and a per-icon forwarding component would add a React layer to every row of
|
|
243
|
+
* a file tree. After install these are the shell's own components, reached by one property read.
|
|
244
|
+
*/
|
|
245
|
+
export declare const icons: IconSet;
|
|
246
|
+
export declare const isMac: () => boolean;
|
|
247
|
+
export declare const monoFamily: () => string;
|
|
248
|
+
export declare const editorFontSize: () => number;
|
|
249
|
+
export declare const activeWorkspaceId: () => WorkspaceId | null;
|
|
250
|
+
export declare const useActiveWorkspace: () => Workspace | null;
|
|
251
|
+
export declare const workspaceDir: (workspace: Workspace) => string | null;
|
|
252
|
+
export declare const addWorkspaceFolders: (paths: string[]) => Promise<void>;
|
|
253
|
+
export declare const removeWorkspaceFolders: (paths: string[]) => Promise<void>;
|
|
254
|
+
export declare const openTerminal: (request: {
|
|
255
|
+
cwd: string;
|
|
256
|
+
where?: "tab" | "split";
|
|
257
|
+
}) => TabId | null;
|
|
258
|
+
export declare const popupMenu: (items: MenuItem[]) => Promise<string | null>;
|
|
259
|
+
/**
|
|
260
|
+
* Open (or re-activate) a tab this plugin owns, in the active workspace's active group.
|
|
261
|
+
*
|
|
262
|
+
* Returns the tab's id, or null when there is no workspace to put it in. The view type must be one
|
|
263
|
+
* this plugin declared; the shell throws if it belongs to somebody else.
|
|
264
|
+
*/
|
|
265
|
+
export declare const openPluginTab: (pluginId: PluginId, request: PluginTabRequest) => TabId | null;
|
|
266
|
+
/**
|
|
267
|
+
* Open a file in whichever editor claims it.
|
|
268
|
+
*
|
|
269
|
+
* The resolution is the SHELL's — it reads the manifests, so no plugin is loaded to answer "who
|
|
270
|
+
* opens a .pdf" and the caller does not need to know that editors have priorities. This is what a
|
|
271
|
+
* file tree calls; before it existed the explorer reached into the store, invented a tab id and
|
|
272
|
+
* resolved the view type itself, which meant three shell internals in one click handler.
|
|
273
|
+
*/
|
|
274
|
+
export declare const openFile: (request: OpenFileRequest) => TabId | null;
|
|
275
|
+
/**
|
|
276
|
+
* The editors that open this file only when asked — a Markdown file's preview — in the words to
|
|
277
|
+
* offer them in. What a file tree puts in a file's menu; picking one is `openFile({ viewType })`.
|
|
278
|
+
* Read off the manifests, so nothing is loaded to answer, and empty for most files.
|
|
279
|
+
*/
|
|
280
|
+
export declare const editorsOnRequest: (path: string) => EditorOffer[];
|
|
281
|
+
export declare const patchPluginTab: (tabId: TabId, patch: {
|
|
282
|
+
title?: string;
|
|
283
|
+
tooltip?: string;
|
|
284
|
+
key?: string;
|
|
285
|
+
state?: Record<string, unknown>;
|
|
286
|
+
}) => void;
|
|
287
|
+
export declare const closePluginTab: (tabId: TabId) => void;
|
|
288
|
+
/**
|
|
289
|
+
* Make this tab the one its column shows, and its column the active one. For content that takes
|
|
290
|
+
* the keyboard where the shell cannot see it — a click inside a native view — so that the shell's
|
|
291
|
+
* idea of "where the user is working", which every shortcut acts on, follows. A no-op when it is
|
|
292
|
+
* already so.
|
|
293
|
+
*/
|
|
294
|
+
export declare const activateTab: (tabId: TabId) => void;
|
|
295
|
+
/**
|
|
296
|
+
* Every open tab of one of this plugin's own view types, across every workspace.
|
|
297
|
+
*
|
|
298
|
+
* Exists for one job: repairing tabs restored from a persisted state older than the plugin. The
|
|
299
|
+
* shell can move an old tab's fields into `state`, but it cannot invent a dedup key — only the
|
|
300
|
+
* plugin knows what identifies one of its tabs.
|
|
301
|
+
*/
|
|
302
|
+
export declare const pluginTabs: (viewType: ViewType) => PluginTabInfo[];
|
|
303
|
+
/**
|
|
304
|
+
* Every tab id that exists, in every group of every workspace.
|
|
305
|
+
*
|
|
306
|
+
* For plugins holding a resource per tab — a native browser view, a parsed document — that has to
|
|
307
|
+
* be released when the tab goes. Callers treat anything absent from this set as closed, so it spans
|
|
308
|
+
* every workspace deliberately: switching workspace must not read as "every tab was closed".
|
|
309
|
+
*/
|
|
310
|
+
export declare const liveTabIds: () => Set<TabId>;
|
|
311
|
+
/** Watch for tabs disappearing. Wrap the returned undo in `ctx.effect` so it dies with the plugin. */
|
|
312
|
+
export declare const subscribeTabs: (listener: (live: Set<TabId>) => void) => (() => void);
|
|
313
|
+
/**
|
|
314
|
+
* The tab this one was split from, or null.
|
|
315
|
+
*
|
|
316
|
+
* CONSUME-ONCE. Read it on first mount and keep what you got; the second caller, and every render
|
|
317
|
+
* after the first, gets null. StrictMode's double-invoked effect counts as a second caller, so this
|
|
318
|
+
* belongs in a `useState` initialiser or a ref, never in an effect body.
|
|
319
|
+
*/
|
|
320
|
+
export declare const duplicatedFrom: (tabId: TabId) => TabId | null;
|
|
321
|
+
/**
|
|
322
|
+
* The one tab with the keyboard: the active group's active tab, in the active workspace.
|
|
323
|
+
*
|
|
324
|
+
* One rule, in one place. Two copies of it is how two visible panes end up disagreeing about which
|
|
325
|
+
* of them should be typing.
|
|
326
|
+
*/
|
|
327
|
+
export declare const focusedTabId: () => TabId | null;
|
|
328
|
+
/** The same rule as a hook, for a pane that has to re-render when focus moves to another column. */
|
|
329
|
+
export declare const useFocusedTabId: () => TabId | null;
|
|
330
|
+
/**
|
|
331
|
+
* The active tab of every group in the active workspace, left to right.
|
|
332
|
+
*
|
|
333
|
+
* What is actually on screen. A plugin compositing something outside the DOM — a native view — has
|
|
334
|
+
* to know exactly this set, and only for the active workspace: the tabs of the workspace you
|
|
335
|
+
* switched away from are alive but not visible.
|
|
336
|
+
*/
|
|
337
|
+
export declare const mountedTabIds: () => TabId[];
|
|
338
|
+
/** Fires when `mountedTabIds` changes — a split, a drag between groups, a workspace switch. */
|
|
339
|
+
export declare const subscribeMountedTabs: (listener: (tabIds: TabId[]) => void) => (() => void);
|
|
340
|
+
/**
|
|
341
|
+
* A plugin's own persisted state for one workspace.
|
|
342
|
+
*
|
|
343
|
+
* Saved and restored with the workspace by the shell, which never looks inside it. For the things
|
|
344
|
+
* a panel wants to remember per project — which directories are expanded, a filter, a scroll
|
|
345
|
+
* position. Undefined means "nothing recorded yet", which is different from "recorded as empty":
|
|
346
|
+
* the file tree opens its root folders on first sight and must not do it again after the user
|
|
347
|
+
* collapses everything.
|
|
348
|
+
*/
|
|
349
|
+
export declare const pluginState: (pluginId: PluginId, workspaceId: WorkspaceId) => Record<string, unknown> | undefined;
|
|
350
|
+
/** The same, as a hook. Null workspace yields undefined rather than throwing. */
|
|
351
|
+
export declare const usePluginState: (pluginId: PluginId, workspaceId: WorkspaceId | null) => Record<string, unknown> | undefined;
|
|
352
|
+
/** Merge into this plugin's state for one workspace, and persist it. */
|
|
353
|
+
export declare const setPluginState: (pluginId: PluginId, workspaceId: WorkspaceId, patch: Record<string, unknown>) => void;
|
|
354
|
+
/**
|
|
355
|
+
* Say whether a file has unsaved changes, and how to save or drop them.
|
|
356
|
+
*
|
|
357
|
+
* The shell marks its tabs with a dot and, when one would close — or the app would quit — with
|
|
358
|
+
* the changes unsaved, asks the user and calls `save` or `discard`. Keyed by PATH, not tab: one
|
|
359
|
+
* document can be open in two columns, and it is the document that is unsaved.
|
|
360
|
+
*/
|
|
361
|
+
export declare const setFileDirty: (path: string, dirty: boolean, handlers?: FileHandlers) => void;
|
|
362
|
+
/** How a file's name is drawn in the tree: source control's colours, in VS Code's manner. */
|
|
363
|
+
export interface FileDecoration {
|
|
364
|
+
/** A CSS colour, usually one of the theme's variables: `var(--git-modified)`. */
|
|
365
|
+
color: string;
|
|
366
|
+
/** One or two characters shown at the end of the row — `M`, `U`, `A`. */
|
|
367
|
+
badge?: string;
|
|
368
|
+
tooltip?: string;
|
|
369
|
+
/** Which wins when two decorations meet on one folder. Higher is more important. */
|
|
370
|
+
weight?: number;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Replace every decoration one source publishes, keyed by absolute path. A path ending in a
|
|
374
|
+
* separator covers the folder and everything inside it. An empty record withdraws the source.
|
|
375
|
+
* Folders above a decorated path take its colour, so collapsed trees still show where changes are.
|
|
376
|
+
*/
|
|
377
|
+
export declare const setFileDecorations: (source: string, decorations: Record<string, FileDecoration>) => void;
|
|
378
|
+
/** The decoration to draw for a path. A hook: re-renders when that path's decoration changes. */
|
|
379
|
+
export declare const useFileDecoration: (path: string) => FileDecoration | undefined;
|
|
380
|
+
/**
|
|
381
|
+
* Where the cursor should go in the file this tab shows — the target of a search result or any
|
|
382
|
+
* other `openFile({ selection })`. A request made before the editor subscribed is delivered as it
|
|
383
|
+
* subscribes, so subscribe on mount. Wrap the returned undo in the unmount.
|
|
384
|
+
*/
|
|
385
|
+
export declare const onRevealInFile: (tabId: TabId, cb: (selection: TextSelection) => void) => (() => void);
|
|
386
|
+
/**
|
|
387
|
+
* Called when the user asks for this side panel by keyboard — the panel should take the keyboard,
|
|
388
|
+
* as a search box does. Delivered on subscribe if the request came before the panel mounted.
|
|
389
|
+
*/
|
|
390
|
+
export declare const onViewRevealed: (viewId: string, cb: () => void) => (() => void);
|
|
391
|
+
/**
|
|
392
|
+
* Whether this tab was opened with `preserveFocus`, so its viewer should leave the keyboard where
|
|
393
|
+
* it is instead of taking it as it mounts. Answers true once, for the open that asked: call it at
|
|
394
|
+
* the moment the viewer would focus itself, not before.
|
|
395
|
+
*/
|
|
396
|
+
export declare const openedInBackground: (tabId: TabId) => boolean;
|
|
397
|
+
/**
|
|
398
|
+
* Files a plugin has just renamed or moved for the user — a file tree's Rename, its Cut and Paste,
|
|
399
|
+
* a drag — so the tabs showing them follow, under their new names. A folder covers everything in
|
|
400
|
+
* it. Files moved by other programs are not reported here, and their tabs stay where they were.
|
|
401
|
+
*/
|
|
402
|
+
export declare const filesMoved: (moves: FileMove[]) => void;
|
|
403
|
+
/**
|
|
404
|
+
* Files a plugin has just deleted for the user. Their tabs close, and unsaved changes in them are
|
|
405
|
+
* dropped — so ask about those first, with `unsavedFiles`. A folder covers everything in it.
|
|
406
|
+
*/
|
|
407
|
+
export declare const filesDeleted: (paths: string[]) => void;
|
|
408
|
+
/** Which files at these paths, or inside them, have unsaved changes. */
|
|
409
|
+
export declare const unsavedFiles: (paths: string[]) => string[];
|
|
410
|
+
/**
|
|
411
|
+
* Needs `files:read`. `notUtf8` means the bytes were not valid UTF-8 and `content` is a lossy
|
|
412
|
+
* decoding — show it, but never write it back.
|
|
413
|
+
*/
|
|
414
|
+
export declare const readFile: (path: string) => Promise<{
|
|
415
|
+
content: string;
|
|
416
|
+
truncated: boolean;
|
|
417
|
+
tooLarge: boolean;
|
|
418
|
+
notUtf8?: boolean;
|
|
419
|
+
}>;
|
|
420
|
+
/** Needs `files:write`. */
|
|
421
|
+
export declare const writeFile: (path: string, content: string) => Promise<void>;
|
|
422
|
+
/** Needs `files:read`. */
|
|
423
|
+
export declare const listDir: (path: string) => Promise<FileStat[]>;
|
|
424
|
+
/**
|
|
425
|
+
* Tokenise code with the text editor's own tokenizers: the tokens of each line, in order, or null
|
|
426
|
+
* when no language goes by that name. The name may be a language id, an alias or an extension —
|
|
427
|
+
* `ts`, `typescript` and `TypeScript` all find TypeScript — so a Markdown fence's word can be
|
|
428
|
+
* passed as it is.
|
|
429
|
+
*
|
|
430
|
+
* The tokens' text is the caller's: slice each line at the offsets. That is the point of asking
|
|
431
|
+
* for tokens rather than coloured HTML — HTML from the editor writes spaces as no-break spaces, so
|
|
432
|
+
* code copied out of it would not be the code that was written.
|
|
433
|
+
*
|
|
434
|
+
* Loads the editor's tokenizers the first time, which is megabytes: call it for code that is about
|
|
435
|
+
* to be shown, not speculatively. Since Rigee 1.1.0.
|
|
436
|
+
*/
|
|
437
|
+
export declare const tokenizeCode: (text: string, language: string) => Promise<CodeToken[][] | null>;
|
|
438
|
+
/**
|
|
439
|
+
* The URL to start a Web Worker from, for a script that ships with this plugin.
|
|
440
|
+
*
|
|
441
|
+
* A window may only start a worker from its own origin, and a plugin's files are served from
|
|
442
|
+
* another — `rigee-plugin://` — so `new Worker(new URL('./x.js', import.meta.url))` is refused.
|
|
443
|
+
* Pass the same URL here instead and start the worker from what comes back, as a module worker:
|
|
444
|
+
*
|
|
445
|
+
* new Worker(workerUrl(new URL('./parse.worker.js', import.meta.url)), { type: 'module' })
|
|
446
|
+
*
|
|
447
|
+
* The script must be self-contained: a worker has no import map, so `react` and this package
|
|
448
|
+
* cannot be imported from inside one. Messages posted before the script has loaded are held until
|
|
449
|
+
* it has, and then delivered. The worker gets no Content Security Policy of its own, so it may
|
|
450
|
+
* compile WebAssembly, which the window may not. Since Rigee 1.1.0.
|
|
451
|
+
*/
|
|
452
|
+
export declare const workerUrl: (script: string | URL) => string;
|
|
453
|
+
/**
|
|
454
|
+
* The shell's "this pane has nothing to show" state.
|
|
455
|
+
*
|
|
456
|
+
* A wrapper rather than an entry in `icons`, because a notice is rendered once per failed pane
|
|
457
|
+
* where an icon is rendered once per row — the extra component layer costs nothing here and reads
|
|
458
|
+
* better at the call site.
|
|
459
|
+
*/
|
|
460
|
+
export declare function Notice(props: NoticeProps): ReactElement;
|