@silverbulletmd/silverbullet 2.4.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.
- package/LICENSE.md +18 -0
- package/README.md +98 -0
- package/client/asset_bundle/bundle.ts +95 -0
- package/client/data/datastore.ts +85 -0
- package/client/data/kv_primitives.ts +25 -0
- package/client/markdown_parser/constants.ts +13 -0
- package/client/plugos/event.ts +36 -0
- package/client/plugos/eventhook.ts +8 -0
- package/client/plugos/hooks/code_widget.ts +59 -0
- package/client/plugos/hooks/command.ts +104 -0
- package/client/plugos/hooks/document_editor.ts +77 -0
- package/client/plugos/hooks/event.ts +187 -0
- package/client/plugos/hooks/mq.ts +154 -0
- package/client/plugos/hooks/plug_namespace.ts +85 -0
- package/client/plugos/hooks/slash_command.ts +192 -0
- package/client/plugos/hooks/syscall.ts +66 -0
- package/client/plugos/manifest_cache.ts +67 -0
- package/client/plugos/plug.ts +99 -0
- package/client/plugos/plug_compile.ts +202 -0
- package/client/plugos/protocol.ts +40 -0
- package/client/plugos/proxy_fetch.ts +53 -0
- package/client/plugos/sandboxes/deno_worker_sandbox.ts +6 -0
- package/client/plugos/sandboxes/sandbox.ts +14 -0
- package/client/plugos/sandboxes/web_worker_sandbox.ts +17 -0
- package/client/plugos/sandboxes/worker_sandbox.ts +132 -0
- package/client/plugos/syscalls/asset.ts +35 -0
- package/client/plugos/syscalls/clientStore.ts +21 -0
- package/client/plugos/syscalls/client_code_widget.ts +12 -0
- package/client/plugos/syscalls/code_widget.ts +24 -0
- package/client/plugos/syscalls/config.ts +46 -0
- package/client/plugos/syscalls/datastore.ts +89 -0
- package/client/plugos/syscalls/editor.ts +673 -0
- package/client/plugos/syscalls/event.ts +36 -0
- package/client/plugos/syscalls/fetch.ts +128 -0
- package/client/plugos/syscalls/index.ts +102 -0
- package/client/plugos/syscalls/jsonschema.ts +69 -0
- package/client/plugos/syscalls/language.ts +23 -0
- package/client/plugos/syscalls/lua.ts +58 -0
- package/client/plugos/syscalls/markdown.ts +84 -0
- package/client/plugos/syscalls/mq.ts +52 -0
- package/client/plugos/syscalls/service_registry.ts +43 -0
- package/client/plugos/syscalls/shell.ts +39 -0
- package/client/plugos/syscalls/space.ts +139 -0
- package/client/plugos/syscalls/sync.ts +77 -0
- package/client/plugos/syscalls/system.ts +150 -0
- package/client/plugos/system.ts +201 -0
- package/client/plugos/types.ts +60 -0
- package/client/plugos/util.ts +14 -0
- package/client/plugos/worker_runtime.ts +195 -0
- package/client/space_lua/ast.ts +328 -0
- package/client/space_lua/ast_narrow.ts +81 -0
- package/client/space_lua/eval.ts +2478 -0
- package/client/space_lua/labels.ts +416 -0
- package/client/space_lua/numeric.ts +240 -0
- package/client/space_lua/parse.ts +1522 -0
- package/client/space_lua/query_collection.ts +232 -0
- package/client/space_lua/rp.ts +27 -0
- package/client/space_lua/runtime.ts +1702 -0
- package/client/space_lua/stdlib/crypto.ts +10 -0
- package/client/space_lua/stdlib/encoding.ts +19 -0
- package/client/space_lua/stdlib/format.ts +770 -0
- package/client/space_lua/stdlib/js.ts +73 -0
- package/client/space_lua/stdlib/load.ts +52 -0
- package/client/space_lua/stdlib/math.ts +193 -0
- package/client/space_lua/stdlib/net.ts +113 -0
- package/client/space_lua/stdlib/os.ts +368 -0
- package/client/space_lua/stdlib/space_lua.ts +153 -0
- package/client/space_lua/stdlib/string.ts +286 -0
- package/client/space_lua/stdlib/table.ts +401 -0
- package/client/space_lua/stdlib.ts +489 -0
- package/client/space_lua/tonumber.ts +501 -0
- package/client/space_lua/util.ts +96 -0
- package/dist/plug-compile.js +1513 -0
- package/package.json +120 -0
- package/plug-api/constants.ts +42 -0
- package/plug-api/lib/async.ts +162 -0
- package/plug-api/lib/crypto.ts +202 -0
- package/plug-api/lib/dates.ts +13 -0
- package/plug-api/lib/json.ts +136 -0
- package/plug-api/lib/limited_map.ts +72 -0
- package/plug-api/lib/memory_cache.ts +21 -0
- package/plug-api/lib/native_fetch.ts +6 -0
- package/plug-api/lib/ref.ts +275 -0
- package/plug-api/lib/resolve.ts +90 -0
- package/plug-api/lib/tags.ts +15 -0
- package/plug-api/lib/transclusion.ts +122 -0
- package/plug-api/lib/tree.ts +232 -0
- package/plug-api/lib/yaml.ts +284 -0
- package/plug-api/syscall.ts +15 -0
- package/plug-api/syscalls/asset.ts +36 -0
- package/plug-api/syscalls/client_store.ts +33 -0
- package/plug-api/syscalls/code_widget.ts +8 -0
- package/plug-api/syscalls/config.ts +58 -0
- package/plug-api/syscalls/datastore.ts +96 -0
- package/plug-api/syscalls/editor.ts +517 -0
- package/plug-api/syscalls/event.ts +47 -0
- package/plug-api/syscalls/index.ts +77 -0
- package/plug-api/syscalls/jsonschema.ts +25 -0
- package/plug-api/syscalls/language.ts +23 -0
- package/plug-api/syscalls/lua.ts +20 -0
- package/plug-api/syscalls/markdown.ts +38 -0
- package/plug-api/syscalls/mq.ts +79 -0
- package/plug-api/syscalls/shell.ts +14 -0
- package/plug-api/syscalls/space.ts +212 -0
- package/plug-api/syscalls/sync.ts +28 -0
- package/plug-api/syscalls/system.ts +102 -0
- package/plug-api/syscalls/yaml.ts +28 -0
- package/plug-api/syscalls.ts +21 -0
- package/plug-api/system_mock.ts +89 -0
- package/plug-api/types/client.ts +116 -0
- package/plug-api/types/config.ts +22 -0
- package/plug-api/types/datastore.ts +28 -0
- package/plug-api/types/event.ts +27 -0
- package/plug-api/types/index.ts +56 -0
- package/plug-api/types/manifest.ts +98 -0
- package/plug-api/types/namespace.ts +6 -0
- package/plugs/builtin_plugs.ts +14 -0
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
import type { Client } from "../../client.ts";
|
|
2
|
+
import {
|
|
3
|
+
foldAll,
|
|
4
|
+
foldCode,
|
|
5
|
+
toggleFold,
|
|
6
|
+
unfoldAll,
|
|
7
|
+
unfoldCode,
|
|
8
|
+
} from "@codemirror/language";
|
|
9
|
+
import {
|
|
10
|
+
deleteLine,
|
|
11
|
+
insertNewline,
|
|
12
|
+
insertNewlineAndIndent,
|
|
13
|
+
moveLineDown,
|
|
14
|
+
moveLineUp,
|
|
15
|
+
redo,
|
|
16
|
+
toggleComment,
|
|
17
|
+
undo,
|
|
18
|
+
} from "@codemirror/commands";
|
|
19
|
+
import type { Transaction } from "@codemirror/state";
|
|
20
|
+
import { EditorView } from "@codemirror/view";
|
|
21
|
+
import { getCM as vimGetCm, Vim } from "@replit/codemirror-vim";
|
|
22
|
+
import type { SysCallMapping } from "../system.ts";
|
|
23
|
+
import type {
|
|
24
|
+
FilterOption,
|
|
25
|
+
UploadFile,
|
|
26
|
+
} from "@silverbulletmd/silverbullet/type/client";
|
|
27
|
+
import { openSearchPanel } from "@codemirror/search";
|
|
28
|
+
import {
|
|
29
|
+
isValidPath,
|
|
30
|
+
parseToRef,
|
|
31
|
+
type Path,
|
|
32
|
+
type Ref,
|
|
33
|
+
} from "@silverbulletmd/silverbullet/lib/ref";
|
|
34
|
+
import { insertNewlineContinueMarkup } from "@codemirror/lang-markdown";
|
|
35
|
+
import type { VimConfig } from "@silverbulletmd/silverbullet/type/config";
|
|
36
|
+
import type { PageMeta } from "@silverbulletmd/silverbullet/type/index";
|
|
37
|
+
|
|
38
|
+
export function editorSyscalls(client: Client): SysCallMapping {
|
|
39
|
+
const syscalls: SysCallMapping = {
|
|
40
|
+
"editor.getCurrentPage": (): string => {
|
|
41
|
+
return client.currentName();
|
|
42
|
+
},
|
|
43
|
+
"editor.getCurrentPageMeta": (): Promise<PageMeta | undefined> => {
|
|
44
|
+
const name = client.currentName();
|
|
45
|
+
return client.objectIndex.getObjectByRef(name, "page", name);
|
|
46
|
+
},
|
|
47
|
+
"editor.getCurrentPath": (_ctx): string => {
|
|
48
|
+
return client.currentPath();
|
|
49
|
+
},
|
|
50
|
+
"editor.getCurrentEditor": (): string => {
|
|
51
|
+
return client.documentEditor?.name || "page";
|
|
52
|
+
},
|
|
53
|
+
"editor.getRecentlyOpenedPages": (): PageMeta[] => {
|
|
54
|
+
return client.ui.viewState.allPages.sort((a, b) =>
|
|
55
|
+
(b.lastOpened || 0) - (a.lastOpened || 0)
|
|
56
|
+
);
|
|
57
|
+
},
|
|
58
|
+
"editor.getText": () => {
|
|
59
|
+
return client.editorView.state.sliceDoc();
|
|
60
|
+
},
|
|
61
|
+
"editor.getCurrentLine": (): {
|
|
62
|
+
from: number;
|
|
63
|
+
to: number;
|
|
64
|
+
text: string;
|
|
65
|
+
textWithCursor: string;
|
|
66
|
+
} => {
|
|
67
|
+
const pos = client.editorView.state.selection.main.from;
|
|
68
|
+
const line = client.editorView.state.doc.lineAt(pos);
|
|
69
|
+
return {
|
|
70
|
+
...line,
|
|
71
|
+
textWithCursor: line.text.slice(0, pos - line.from) + "|^|" +
|
|
72
|
+
line.text.slice(pos - line.from),
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
"editor.setText": (_ctx, newText: string, shouldIsolateHistory = false) => {
|
|
76
|
+
client.setEditorText(newText, shouldIsolateHistory);
|
|
77
|
+
},
|
|
78
|
+
"editor.getCursor": (): number => {
|
|
79
|
+
return client.editorView.state.selection.main.from;
|
|
80
|
+
},
|
|
81
|
+
"editor.getSelection": (): { from: number; to: number; text: string } => {
|
|
82
|
+
const selection = client.editorView.state.selection.main;
|
|
83
|
+
const text = client.editorView.state.sliceDoc(
|
|
84
|
+
selection.from,
|
|
85
|
+
selection.to,
|
|
86
|
+
);
|
|
87
|
+
return {
|
|
88
|
+
from: selection.from,
|
|
89
|
+
to: selection.to,
|
|
90
|
+
text,
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
"editor.save": () => {
|
|
94
|
+
return client.save(true);
|
|
95
|
+
},
|
|
96
|
+
"editor.navigate": async (
|
|
97
|
+
_ctx,
|
|
98
|
+
ref: Ref | string,
|
|
99
|
+
replaceState = false,
|
|
100
|
+
newWindow = false,
|
|
101
|
+
) => {
|
|
102
|
+
if (typeof ref === "string") {
|
|
103
|
+
const parsedRef = parseToRef(ref);
|
|
104
|
+
if (!parsedRef) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
"Unable to parse string provided to `editor.navigate` as ref",
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
ref = parsedRef;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (
|
|
113
|
+
// @ts-ignore: Legacy support
|
|
114
|
+
ref.page !== undefined
|
|
115
|
+
) {
|
|
116
|
+
console.warn(
|
|
117
|
+
"You are using legacy navigation syntax with `editor.navigate()`, this will be phased out in the future",
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const legacyRef = ref as unknown as {
|
|
121
|
+
kind: "page" | "document";
|
|
122
|
+
page: string;
|
|
123
|
+
pos?: number | { line: number; column: number };
|
|
124
|
+
header?: string;
|
|
125
|
+
meta?: boolean;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
legacyRef.kind ??= "page";
|
|
129
|
+
|
|
130
|
+
let details: Ref["details"] = undefined;
|
|
131
|
+
|
|
132
|
+
if (typeof legacyRef.pos === "number") {
|
|
133
|
+
details = {
|
|
134
|
+
type: "position",
|
|
135
|
+
pos: legacyRef.pos,
|
|
136
|
+
};
|
|
137
|
+
} else if (legacyRef.pos) {
|
|
138
|
+
details = {
|
|
139
|
+
type: "linecolumn",
|
|
140
|
+
line: legacyRef.pos.line,
|
|
141
|
+
column: legacyRef.pos.column,
|
|
142
|
+
};
|
|
143
|
+
} else if (legacyRef.header) {
|
|
144
|
+
details = {
|
|
145
|
+
type: "header",
|
|
146
|
+
header: legacyRef.header,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
ref = {
|
|
151
|
+
path: (legacyRef.kind === "page"
|
|
152
|
+
? `${legacyRef.page}.md`
|
|
153
|
+
: legacyRef.page) as Path,
|
|
154
|
+
details,
|
|
155
|
+
meta: legacyRef.meta,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// This validation code should be connected to the Ref type. Ideally using
|
|
160
|
+
// some validation library. Didn't want to use jsonschemas here tho.
|
|
161
|
+
// Ideally this would be moved into a function too
|
|
162
|
+
if (!isValidPath(ref.path) && ref.path !== "") {
|
|
163
|
+
throw new Error(
|
|
164
|
+
"Path passed in ref to `editor.navigate` is invalid",
|
|
165
|
+
);
|
|
166
|
+
} else if (typeof ref.meta !== "boolean" && ref.meta !== undefined) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
"ref.meta has to be of type `boolean`",
|
|
169
|
+
);
|
|
170
|
+
} else if (ref.details !== undefined && typeof ref.details !== "object") {
|
|
171
|
+
throw new Error(
|
|
172
|
+
"ref.details has to be of type `object` or `undefined`",
|
|
173
|
+
);
|
|
174
|
+
} else if (
|
|
175
|
+
ref.details &&
|
|
176
|
+
!["position", "linecolumn", "header"].includes(ref.details.type)
|
|
177
|
+
) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
"ref.details.type has to be 'position', 'linecolumn' or 'header'",
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (
|
|
184
|
+
ref.details?.type === "position" && typeof ref.details.pos !== "number"
|
|
185
|
+
) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
"ref.details.pos has to be of type `number`",
|
|
188
|
+
);
|
|
189
|
+
} else if (
|
|
190
|
+
ref.details?.type === "header" && typeof ref.details.header !== "string"
|
|
191
|
+
) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
"ref.details.header has to be of type `string`",
|
|
194
|
+
);
|
|
195
|
+
} else if (
|
|
196
|
+
ref.details?.type === "linecolumn" &&
|
|
197
|
+
typeof ref.details.line !== "number" &&
|
|
198
|
+
typeof ref.details.column !== "number"
|
|
199
|
+
) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
"ref.details.line and ref.details.column has to be of type `number`",
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
await client.navigate(ref, replaceState, newWindow);
|
|
206
|
+
},
|
|
207
|
+
"editor.reloadPage": async () => {
|
|
208
|
+
await client.reloadEditor();
|
|
209
|
+
},
|
|
210
|
+
"editor.reloadUI": () => {
|
|
211
|
+
location.reload();
|
|
212
|
+
},
|
|
213
|
+
"editor.rebuildEditorState": () => {
|
|
214
|
+
client.rebuildEditorState();
|
|
215
|
+
},
|
|
216
|
+
"editor.reloadConfigAndCommands": async () => {
|
|
217
|
+
await client.clientSystem.system.localSyscall(
|
|
218
|
+
"system.loadScripts",
|
|
219
|
+
[],
|
|
220
|
+
);
|
|
221
|
+
await client.clientSystem.system.localSyscall(
|
|
222
|
+
"system.loadSpaceStyles",
|
|
223
|
+
[],
|
|
224
|
+
);
|
|
225
|
+
},
|
|
226
|
+
"editor.invokeCommand": (_ctx, name: string, args?: string[]) => {
|
|
227
|
+
return client.runCommandByName(name, args);
|
|
228
|
+
},
|
|
229
|
+
"editor.openUrl": (_ctx, url: string, existingWindow = false) => {
|
|
230
|
+
client.openUrl(url, existingWindow);
|
|
231
|
+
},
|
|
232
|
+
"editor.newWindow": () => {
|
|
233
|
+
globalThis.open(
|
|
234
|
+
location.href,
|
|
235
|
+
"rnd" + Math.random(),
|
|
236
|
+
`width=${globalThis.innerWidth},heigh=${globalThis.innerHeight}`,
|
|
237
|
+
);
|
|
238
|
+
},
|
|
239
|
+
"editor.goHistory": (_ctx, delta: number) => {
|
|
240
|
+
globalThis.history.go(delta);
|
|
241
|
+
},
|
|
242
|
+
"editor.downloadFile": (_ctx, filename: string, dataUrl: string) => {
|
|
243
|
+
const link = document.createElement("a");
|
|
244
|
+
link.href = dataUrl;
|
|
245
|
+
link.download = filename;
|
|
246
|
+
link.click();
|
|
247
|
+
},
|
|
248
|
+
"editor.uploadFile": (
|
|
249
|
+
_ctx,
|
|
250
|
+
accept?: string,
|
|
251
|
+
capture?: string,
|
|
252
|
+
): Promise<UploadFile> => {
|
|
253
|
+
return new Promise<UploadFile>((resolve, reject) => {
|
|
254
|
+
const input = document.createElement("input");
|
|
255
|
+
input.type = "file";
|
|
256
|
+
if (accept) {
|
|
257
|
+
input.accept = accept;
|
|
258
|
+
}
|
|
259
|
+
if (capture) {
|
|
260
|
+
input.capture = capture;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
input.onchange = () => {
|
|
264
|
+
const file = input.files?.item(0);
|
|
265
|
+
if (!file) {
|
|
266
|
+
reject(new Error("No file found"));
|
|
267
|
+
} else {
|
|
268
|
+
const reader = new FileReader();
|
|
269
|
+
reader.readAsArrayBuffer(file);
|
|
270
|
+
reader.onloadend = async (evt) => {
|
|
271
|
+
if (evt.target?.readyState == FileReader.DONE) {
|
|
272
|
+
resolve({
|
|
273
|
+
name: file.name,
|
|
274
|
+
contentType: file.type,
|
|
275
|
+
content: new Uint8Array(await file.arrayBuffer()),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
reader.onabort = (e) => {
|
|
280
|
+
reject(e);
|
|
281
|
+
};
|
|
282
|
+
reader.onerror = (e) => {
|
|
283
|
+
reject(e);
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
input.onabort = (e) => {
|
|
288
|
+
reject(e);
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
input.click();
|
|
292
|
+
});
|
|
293
|
+
},
|
|
294
|
+
"editor.flashNotification": (
|
|
295
|
+
_ctx,
|
|
296
|
+
message: string,
|
|
297
|
+
type: "error" | "info" = "info",
|
|
298
|
+
) => {
|
|
299
|
+
client.flashNotification(message, type);
|
|
300
|
+
},
|
|
301
|
+
"editor.filterBox": (
|
|
302
|
+
_ctx,
|
|
303
|
+
label: string,
|
|
304
|
+
options: FilterOption[],
|
|
305
|
+
helpText = "",
|
|
306
|
+
placeHolder = "",
|
|
307
|
+
): Promise<FilterOption | undefined> => {
|
|
308
|
+
return client.filterBox(label, options, helpText, placeHolder);
|
|
309
|
+
},
|
|
310
|
+
"editor.showPanel": (
|
|
311
|
+
_ctx,
|
|
312
|
+
id: string,
|
|
313
|
+
mode: number,
|
|
314
|
+
html: string,
|
|
315
|
+
script: string,
|
|
316
|
+
) => {
|
|
317
|
+
client.ui.viewDispatch({
|
|
318
|
+
type: "show-panel",
|
|
319
|
+
id: id as any,
|
|
320
|
+
config: { html, script, mode },
|
|
321
|
+
});
|
|
322
|
+
setTimeout(() => {
|
|
323
|
+
// Dummy dispatch to rerender the editor and toggle the panel
|
|
324
|
+
client.editorView.dispatch({});
|
|
325
|
+
});
|
|
326
|
+
},
|
|
327
|
+
"editor.hidePanel": (_ctx, id: string) => {
|
|
328
|
+
client.ui.viewDispatch({
|
|
329
|
+
type: "hide-panel",
|
|
330
|
+
id: id as any,
|
|
331
|
+
});
|
|
332
|
+
setTimeout(() => {
|
|
333
|
+
// Dummy dispatch to rerender the editor and toggle the panel
|
|
334
|
+
client.editorView.dispatch({});
|
|
335
|
+
});
|
|
336
|
+
},
|
|
337
|
+
"editor.showProgress": (
|
|
338
|
+
_ctx,
|
|
339
|
+
progressPercentage?: number,
|
|
340
|
+
progressType?: "sync" | "index",
|
|
341
|
+
) => {
|
|
342
|
+
client.showProgress(progressPercentage, progressType);
|
|
343
|
+
},
|
|
344
|
+
"editor.insertAtPos": (
|
|
345
|
+
_ctx,
|
|
346
|
+
text: string,
|
|
347
|
+
pos: number,
|
|
348
|
+
cursorPlaceHolder = false,
|
|
349
|
+
) => {
|
|
350
|
+
let cursorPlaceholderPos = -1;
|
|
351
|
+
if (cursorPlaceHolder) {
|
|
352
|
+
cursorPlaceholderPos = text.indexOf("|^|");
|
|
353
|
+
if (cursorPlaceholderPos !== -1) {
|
|
354
|
+
text = text.slice(0, cursorPlaceholderPos) +
|
|
355
|
+
text.slice(cursorPlaceholderPos + 3);
|
|
356
|
+
} else {
|
|
357
|
+
cursorPlaceHolder = false;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
client.editorView.dispatch({
|
|
361
|
+
changes: {
|
|
362
|
+
insert: text,
|
|
363
|
+
from: pos,
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
if (cursorPlaceHolder) {
|
|
367
|
+
const cursorPos = pos + cursorPlaceholderPos;
|
|
368
|
+
client.editorView.dispatch({
|
|
369
|
+
selection: {
|
|
370
|
+
anchor: cursorPos,
|
|
371
|
+
},
|
|
372
|
+
effects: [
|
|
373
|
+
EditorView.scrollIntoView(cursorPos),
|
|
374
|
+
],
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
"editor.replaceRange": (
|
|
379
|
+
_ctx,
|
|
380
|
+
from: number,
|
|
381
|
+
to: number,
|
|
382
|
+
text: string,
|
|
383
|
+
cursorPlaceHolder = false,
|
|
384
|
+
) => {
|
|
385
|
+
let cursorPlaceholderPos = -1;
|
|
386
|
+
if (cursorPlaceHolder) {
|
|
387
|
+
cursorPlaceholderPos = text.indexOf("|^|");
|
|
388
|
+
text = text.slice(0, cursorPlaceholderPos) +
|
|
389
|
+
text.slice(cursorPlaceholderPos + 3);
|
|
390
|
+
}
|
|
391
|
+
client.editorView.dispatch({
|
|
392
|
+
changes: {
|
|
393
|
+
insert: text,
|
|
394
|
+
from: from,
|
|
395
|
+
to: to,
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
if (cursorPlaceHolder) {
|
|
399
|
+
const cursorPos = from + cursorPlaceholderPos;
|
|
400
|
+
client.editorView.dispatch({
|
|
401
|
+
selection: {
|
|
402
|
+
anchor: cursorPos,
|
|
403
|
+
},
|
|
404
|
+
effects: [
|
|
405
|
+
EditorView.scrollIntoView(cursorPos),
|
|
406
|
+
],
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
"editor.moveCursor": (_ctx, pos: number, center = false) => {
|
|
411
|
+
client.editorView.dispatch({
|
|
412
|
+
selection: {
|
|
413
|
+
anchor: pos,
|
|
414
|
+
},
|
|
415
|
+
});
|
|
416
|
+
if (center) {
|
|
417
|
+
client.editorView.dispatch({
|
|
418
|
+
effects: [
|
|
419
|
+
EditorView.scrollIntoView(
|
|
420
|
+
pos,
|
|
421
|
+
{
|
|
422
|
+
y: "center",
|
|
423
|
+
},
|
|
424
|
+
),
|
|
425
|
+
],
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
client.editorView.focus();
|
|
429
|
+
},
|
|
430
|
+
"editor.moveCursorToLine": (
|
|
431
|
+
_ctx,
|
|
432
|
+
line: number,
|
|
433
|
+
column = 1,
|
|
434
|
+
center = false,
|
|
435
|
+
) => {
|
|
436
|
+
// CodeMirror already keeps information about lines
|
|
437
|
+
const cmLine = client.editorView.state.doc.line(line);
|
|
438
|
+
// How much to move inside the line, column number starts from 1
|
|
439
|
+
const offset = Math.max(0, Math.min(cmLine.length, column - 1));
|
|
440
|
+
// Just reuse the implementation above
|
|
441
|
+
syscalls["editor.moveCursor"](_ctx, cmLine.from + offset, center);
|
|
442
|
+
},
|
|
443
|
+
"editor.setSelection": (_ctx, from: number, to: number) => {
|
|
444
|
+
client.editorView.dispatch({
|
|
445
|
+
selection: {
|
|
446
|
+
anchor: from,
|
|
447
|
+
head: to,
|
|
448
|
+
},
|
|
449
|
+
});
|
|
450
|
+
},
|
|
451
|
+
|
|
452
|
+
"editor.insertAtCursor": (
|
|
453
|
+
_ctx,
|
|
454
|
+
text: string,
|
|
455
|
+
scrollIntoView = false,
|
|
456
|
+
cursorPlaceHolder = false,
|
|
457
|
+
) => {
|
|
458
|
+
const editorView = client.editorView;
|
|
459
|
+
const from = editorView.state.selection.main.from;
|
|
460
|
+
const cursorPlaceholderPos = text.indexOf("|^|");
|
|
461
|
+
if (cursorPlaceHolder && cursorPlaceholderPos !== -1) {
|
|
462
|
+
text = text.slice(0, cursorPlaceholderPos) +
|
|
463
|
+
text.slice(cursorPlaceholderPos + 3);
|
|
464
|
+
} else {
|
|
465
|
+
cursorPlaceHolder = false;
|
|
466
|
+
}
|
|
467
|
+
editorView.dispatch({
|
|
468
|
+
changes: {
|
|
469
|
+
insert: text,
|
|
470
|
+
from: from,
|
|
471
|
+
},
|
|
472
|
+
selection: {
|
|
473
|
+
anchor: cursorPlaceHolder
|
|
474
|
+
? from + cursorPlaceholderPos
|
|
475
|
+
: from + text.length,
|
|
476
|
+
},
|
|
477
|
+
scrollIntoView,
|
|
478
|
+
});
|
|
479
|
+
},
|
|
480
|
+
"editor.dispatch": (_ctx, change: Transaction) => {
|
|
481
|
+
client.editorView.dispatch(change);
|
|
482
|
+
},
|
|
483
|
+
"editor.prompt": (
|
|
484
|
+
_ctx,
|
|
485
|
+
message: string,
|
|
486
|
+
defaultValue = "",
|
|
487
|
+
): Promise<string | undefined> => {
|
|
488
|
+
return client.prompt(message, defaultValue);
|
|
489
|
+
},
|
|
490
|
+
"editor.confirm": (_ctx, message: string): Promise<boolean> => {
|
|
491
|
+
return client.confirm(message);
|
|
492
|
+
},
|
|
493
|
+
"editor.alert": (_ctx, message: string) => {
|
|
494
|
+
alert(message);
|
|
495
|
+
},
|
|
496
|
+
"editor.getUiOption": (_ctx, key: string): any => {
|
|
497
|
+
return (client.ui.viewState.uiOptions as any)[key];
|
|
498
|
+
},
|
|
499
|
+
"editor.setUiOption": (_ctx, key: string, value: any) => {
|
|
500
|
+
client.ui.viewDispatch({
|
|
501
|
+
type: "set-ui-option",
|
|
502
|
+
key,
|
|
503
|
+
value,
|
|
504
|
+
});
|
|
505
|
+
client.reloadEditor();
|
|
506
|
+
},
|
|
507
|
+
"editor.vimEx": (_ctx, exCommand: string) => {
|
|
508
|
+
const cm = vimGetCm(client.editorView);
|
|
509
|
+
if (cm && cm.state.vim) {
|
|
510
|
+
return Vim.handleEx(cm as any, exCommand);
|
|
511
|
+
} else {
|
|
512
|
+
throw new Error("Vim mode not active or not initialized.");
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
"editor.configureVimMode": () => {
|
|
516
|
+
// Override the default "o" binding to be more intelligent and follow the markdown editor's behavior
|
|
517
|
+
Vim.mapCommand("o", "action", "newline-continue-markup", {}, {});
|
|
518
|
+
Vim.mapCommand("O", "action", "back-newline-continue-markup", {}, {});
|
|
519
|
+
Vim.unmap("<C-q>", undefined as any);
|
|
520
|
+
Vim.defineAction("newline-continue-markup", (cm) => {
|
|
521
|
+
// Append at end of line
|
|
522
|
+
Vim.handleKey(cm, "A", "+input");
|
|
523
|
+
// Insert newline continuing markup where appropriate
|
|
524
|
+
insertNewlineContinueMarkup(client.editorView) ||
|
|
525
|
+
insertNewlineAndIndent(client.editorView);
|
|
526
|
+
});
|
|
527
|
+
Vim.defineAction("back-newline-continue-markup", (cm) => {
|
|
528
|
+
// Determine current line
|
|
529
|
+
const pos = client.editorView.state.selection.main.from;
|
|
530
|
+
const line = client.editorView.state.doc.lineAt(pos).number;
|
|
531
|
+
if (line === 1) {
|
|
532
|
+
// We're on the top line
|
|
533
|
+
// Go to 0:0
|
|
534
|
+
Vim.handleKey(cm, "0", "+input");
|
|
535
|
+
// Insert a newline
|
|
536
|
+
insertNewline(client.editorView);
|
|
537
|
+
// Go up to the new line
|
|
538
|
+
Vim.handleKey(cm, "k", "+input");
|
|
539
|
+
// Into insert mode
|
|
540
|
+
Vim.handleKey(cm, "i", "+input");
|
|
541
|
+
} else {
|
|
542
|
+
// We're elsewhere in the document
|
|
543
|
+
// Go up
|
|
544
|
+
Vim.handleKey(cm, "k", "+input");
|
|
545
|
+
// Append mode at the end of the line
|
|
546
|
+
Vim.handleKey(cm, "A", "+input");
|
|
547
|
+
// Insert a newline using the continue markup thing
|
|
548
|
+
insertNewlineContinueMarkup(client.editorView) ||
|
|
549
|
+
insertNewlineAndIndent(client.editorView);
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
// Load the config if any
|
|
554
|
+
const config = client.config.get<VimConfig>("vim", {});
|
|
555
|
+
if (config) {
|
|
556
|
+
config.unmap?.forEach((binding) => {
|
|
557
|
+
if (typeof binding === "string") {
|
|
558
|
+
console.log("Unmapping " + binding);
|
|
559
|
+
// @ts-ignore: unmap expects a string for the mode, this is problematic with Ex mappings which requires undefined or false
|
|
560
|
+
Vim.unmap(binding, undefined);
|
|
561
|
+
} else if (binding.key) {
|
|
562
|
+
console.log(
|
|
563
|
+
"Unmapping " + binding.key + " in " + (binding.mode ?? "normal"),
|
|
564
|
+
);
|
|
565
|
+
Vim.unmap(binding.key, binding.mode ?? "normal");
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
config.map?.forEach(({ map, to, mode }) => {
|
|
569
|
+
console.log(
|
|
570
|
+
"Mapping " + map + " to " + to + " for " + (mode ?? "normal"),
|
|
571
|
+
);
|
|
572
|
+
Vim.map(map, to, mode ?? "normal");
|
|
573
|
+
});
|
|
574
|
+
config.noremap?.forEach(({ map, to, mode }) => {
|
|
575
|
+
console.log(
|
|
576
|
+
"Noremapping " + map + " to " + to + " for " + (mode ?? "normal"),
|
|
577
|
+
);
|
|
578
|
+
Vim.noremap(map, to, mode ?? "normal");
|
|
579
|
+
});
|
|
580
|
+
config.commands?.forEach(({ ex, command }) => {
|
|
581
|
+
console.log("Mapping command '" + command + "' to Ex " + ex);
|
|
582
|
+
Vim.defineEx(ex, "", () => client.runCommandByName(command));
|
|
583
|
+
});
|
|
584
|
+
} else {
|
|
585
|
+
console.log("No vim config found");
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
"editor.openPageNavigator": (
|
|
589
|
+
_ctx,
|
|
590
|
+
mode: "page" | "meta" | "document" | "all" = "page",
|
|
591
|
+
) => {
|
|
592
|
+
client.startPageNavigate(mode);
|
|
593
|
+
},
|
|
594
|
+
"editor.openCommandPalette": () => {
|
|
595
|
+
client.startCommandPalette();
|
|
596
|
+
},
|
|
597
|
+
"editor.deleteLine": () => {
|
|
598
|
+
deleteLine(client.editorView);
|
|
599
|
+
},
|
|
600
|
+
"editor.toggleComment": () => {
|
|
601
|
+
return toggleComment({
|
|
602
|
+
state: client.editorView.state,
|
|
603
|
+
dispatch: client.editorView.dispatch,
|
|
604
|
+
});
|
|
605
|
+
},
|
|
606
|
+
"editor.moveLineUp": () => {
|
|
607
|
+
return moveLineUp({
|
|
608
|
+
state: client.editorView.state,
|
|
609
|
+
dispatch: client.editorView.dispatch,
|
|
610
|
+
});
|
|
611
|
+
},
|
|
612
|
+
"editor.moveLineDown": () => {
|
|
613
|
+
return moveLineDown({
|
|
614
|
+
state: client.editorView.state,
|
|
615
|
+
dispatch: client.editorView.dispatch,
|
|
616
|
+
});
|
|
617
|
+
},
|
|
618
|
+
// Folding
|
|
619
|
+
"editor.fold": () => {
|
|
620
|
+
foldCode(client.editorView);
|
|
621
|
+
},
|
|
622
|
+
"editor.unfold": () => {
|
|
623
|
+
unfoldCode(client.editorView);
|
|
624
|
+
},
|
|
625
|
+
"editor.toggleFold": () => {
|
|
626
|
+
toggleFold(client.editorView);
|
|
627
|
+
},
|
|
628
|
+
"editor.foldAll": () => {
|
|
629
|
+
foldAll(client.editorView);
|
|
630
|
+
},
|
|
631
|
+
"editor.unfoldAll": () => {
|
|
632
|
+
unfoldAll(client.editorView);
|
|
633
|
+
},
|
|
634
|
+
"editor.undo": () => {
|
|
635
|
+
return undo(client.editorView);
|
|
636
|
+
},
|
|
637
|
+
"editor.redo": () => {
|
|
638
|
+
return redo(client.editorView);
|
|
639
|
+
},
|
|
640
|
+
"editor.openSearchPanel": () => {
|
|
641
|
+
openSearchPanel(client.editorView);
|
|
642
|
+
},
|
|
643
|
+
"editor.copyToClipboard": async (_ctx, data: string | Blob) => {
|
|
644
|
+
try {
|
|
645
|
+
if (typeof data === "string") {
|
|
646
|
+
await navigator.clipboard.writeText(data);
|
|
647
|
+
} else {
|
|
648
|
+
await navigator.clipboard.write([
|
|
649
|
+
new ClipboardItem({ [data.type]: data }),
|
|
650
|
+
]);
|
|
651
|
+
}
|
|
652
|
+
} catch (e) {
|
|
653
|
+
console.error(e);
|
|
654
|
+
client.flashNotification(`Could not copy to clipboard: ${e}`);
|
|
655
|
+
}
|
|
656
|
+
},
|
|
657
|
+
"editor.sendMessage": (_ctx, type: string, data: any) => {
|
|
658
|
+
if (!client.isDocumentEditor()) return;
|
|
659
|
+
|
|
660
|
+
client.documentEditor.sendPublicMessage({
|
|
661
|
+
type,
|
|
662
|
+
data,
|
|
663
|
+
});
|
|
664
|
+
},
|
|
665
|
+
|
|
666
|
+
"editor.isMobile": () => {
|
|
667
|
+
const mouseDetected = globalThis.matchMedia("(pointer:fine)").matches;
|
|
668
|
+
return !mouseDetected;
|
|
669
|
+
},
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
return syscalls;
|
|
673
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { SysCallMapping } from "../system.ts";
|
|
2
|
+
import type { EventHookI } from "../eventhook.ts";
|
|
3
|
+
import type { EventSubscription } from "@silverbulletmd/silverbullet/type/event";
|
|
4
|
+
import { LuaStackFrame, luaValueToJS } from "../../space_lua/runtime.ts";
|
|
5
|
+
import type { Client } from "../../client.ts";
|
|
6
|
+
|
|
7
|
+
export function eventSyscalls(
|
|
8
|
+
eventHook: EventHookI,
|
|
9
|
+
client: Client,
|
|
10
|
+
): SysCallMapping {
|
|
11
|
+
return {
|
|
12
|
+
"event.dispatch": (_ctx, eventName: string, data: any) => {
|
|
13
|
+
return eventHook.dispatchEvent(eventName, data);
|
|
14
|
+
},
|
|
15
|
+
"event.listEvents": () => {
|
|
16
|
+
return eventHook.listEvents();
|
|
17
|
+
},
|
|
18
|
+
/**
|
|
19
|
+
* Define a Lua event listener
|
|
20
|
+
*/
|
|
21
|
+
"event.listen": (
|
|
22
|
+
_ctx,
|
|
23
|
+
def: EventSubscription,
|
|
24
|
+
) => {
|
|
25
|
+
// console.log("Registering Lua event listener: ", def.name);
|
|
26
|
+
client.config.insert([
|
|
27
|
+
"eventListeners",
|
|
28
|
+
def.name,
|
|
29
|
+
], async (...args: any[]) => {
|
|
30
|
+
// Convert return value to JS
|
|
31
|
+
const val = await def.run(...args);
|
|
32
|
+
return luaValueToJS(val, LuaStackFrame.lostFrame);
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|