@coldsmirk/inkstone-mongodb 0.22.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 +51 -0
- package/dist/document-CVB9YunA.js +408 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +90 -0
- package/dist/markers-Bdvbut2O.js +1023 -0
- package/dist/markers-CQOm7diu.d.ts +218 -0
- package/dist/monaco.d.ts +40 -0
- package/dist/monaco.js +170 -0
- package/dist/react.d.ts +93 -0
- package/dist/react.js +104 -0
- package/package.json +78 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
//#region src/commands.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The vocabulary the assist offers: the database commands a bench reaches for with the fields
|
|
4
|
+
* each takes, and the operators a key position takes by where it stands — a filter's, an
|
|
5
|
+
* update's, a pipeline's stages and their keys. Curated from the MongoDB manual, not
|
|
6
|
+
* exhaustive; the server's answer is the authority.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* One database command as the document names it in its first key.
|
|
10
|
+
*/
|
|
11
|
+
interface MongoCommand {
|
|
12
|
+
name: string;
|
|
13
|
+
summary: string;
|
|
14
|
+
/**
|
|
15
|
+
* The command's other top-level keys.
|
|
16
|
+
*/
|
|
17
|
+
fields: string[];
|
|
18
|
+
/**
|
|
19
|
+
* Whether the command's own value names a collection (`{ find: "orders" }`), rather than
|
|
20
|
+
* being `1` or a name of another kind.
|
|
21
|
+
*/
|
|
22
|
+
collection: boolean;
|
|
23
|
+
}
|
|
24
|
+
declare const MONGO_COMMANDS: readonly MongoCommand[];
|
|
25
|
+
declare const QUERY_OPERATORS: string[];
|
|
26
|
+
declare const FIELD_OPERATORS: string[];
|
|
27
|
+
declare const UPDATE_OPERATORS: string[];
|
|
28
|
+
declare const PUSH_MODIFIERS: string[];
|
|
29
|
+
declare const STAGES: string[];
|
|
30
|
+
declare const EXPRESSION_OPERATORS: string[];
|
|
31
|
+
/**
|
|
32
|
+
* The keys to offer at a key position, by the keys enclosing the caret and — at the top level
|
|
33
|
+
* — the keys already there, the first of which names the command. Empty where the names are
|
|
34
|
+
* the reader's own (a field, a variable, a facet).
|
|
35
|
+
*/
|
|
36
|
+
declare function keysAt(path: readonly string[], siblings: readonly string[], commands?: readonly MongoCommand[]): string[];
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/assist.d.ts
|
|
39
|
+
/**
|
|
40
|
+
* One collection as the catalog lists it, under its database.
|
|
41
|
+
*/
|
|
42
|
+
interface CatalogCollection {
|
|
43
|
+
database: string;
|
|
44
|
+
name: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The catalog is the host's to fetch — and it MUST be a cache-backed read (the tree's own
|
|
48
|
+
* list), never a fresh fetch: completion reads it on every keystroke in a value.
|
|
49
|
+
*/
|
|
50
|
+
interface MongodbCatalog {
|
|
51
|
+
collections: () => readonly CatalogCollection[];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* One suggestion: a command, one of its fields, an operator, a pipeline stage, a collection
|
|
55
|
+
* or a database.
|
|
56
|
+
*/
|
|
57
|
+
interface MongodbCompletionCandidate {
|
|
58
|
+
label: string;
|
|
59
|
+
kind: "command" | "field" | "operator" | "stage" | "collection" | "database";
|
|
60
|
+
insertText: string;
|
|
61
|
+
/**
|
|
62
|
+
* `insertText` is a snippet (`$0` marks the caret) rather than plain text.
|
|
63
|
+
*/
|
|
64
|
+
snippet?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* How many characters before the caret the insert replaces — the key or the name typed so
|
|
67
|
+
* far, bare or inside its quote.
|
|
68
|
+
*/
|
|
69
|
+
replace: number;
|
|
70
|
+
detail?: string;
|
|
71
|
+
documentation?: string;
|
|
72
|
+
sortGroup: number;
|
|
73
|
+
}
|
|
74
|
+
interface MongodbHoverCard {
|
|
75
|
+
from: number;
|
|
76
|
+
to: number;
|
|
77
|
+
markdown: string[];
|
|
78
|
+
}
|
|
79
|
+
interface MongodbAssist {
|
|
80
|
+
complete: (text: string, offset: number) => MongodbCompletionCandidate[];
|
|
81
|
+
hover: (text: string, offset: number) => MongodbHoverCard | null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Completion for keys and for the values that name a collection or a database, hover for the
|
|
85
|
+
* command under its key — all reading the document through `document.ts`.
|
|
86
|
+
*/
|
|
87
|
+
declare function createMongodbAssist(catalog?: MongodbCatalog, commands?: readonly MongoCommand[]): MongodbAssist;
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/document.d.ts
|
|
90
|
+
/**
|
|
91
|
+
* A MongoDB command is one document in the shell's own spelling — the command as its first
|
|
92
|
+
* key, `$db` naming the database — read here as the polyglot sidecar's MongoDB engine reads it
|
|
93
|
+
* with the driver's `BsonDocument.parse`: keys quoted or bare, strings in double or single
|
|
94
|
+
* quotes, the shell's constructors (`ObjectId("…")`, `ISODate("…")`, `NumberLong(…)`, `new
|
|
95
|
+
* Date(…)` …), regular-expression literals, `undefined`, `MinKey` and `MaxKey`, no comments and
|
|
96
|
+
* no trailing commas. A text this reader refuses is a text the engine refuses, for the same
|
|
97
|
+
* reason.
|
|
98
|
+
*/
|
|
99
|
+
/**
|
|
100
|
+
* Why a text is not a command — a code, never a sentence; the host owns the wording:
|
|
101
|
+
* `"not-a-document"` where it does not parse as one document, `"no-command"` where the document
|
|
102
|
+
* holds nothing but `$db`.
|
|
103
|
+
*/
|
|
104
|
+
type DocumentRefusal = "empty" | "not-a-document" | "no-command";
|
|
105
|
+
interface DocumentRead {
|
|
106
|
+
/**
|
|
107
|
+
* The top-level keys in order, `$db` among them.
|
|
108
|
+
*/
|
|
109
|
+
keys: string[];
|
|
110
|
+
/**
|
|
111
|
+
* The first key that is not `$db` — the command; null where there is none.
|
|
112
|
+
*/
|
|
113
|
+
command: string | null;
|
|
114
|
+
/**
|
|
115
|
+
* `$db`'s value where it is a string.
|
|
116
|
+
*/
|
|
117
|
+
database: string | null;
|
|
118
|
+
/**
|
|
119
|
+
* The command key's `[from, to)` in the text, quotes included; null where there is none.
|
|
120
|
+
*/
|
|
121
|
+
commandSpan: {
|
|
122
|
+
from: number;
|
|
123
|
+
to: number;
|
|
124
|
+
} | null;
|
|
125
|
+
refusal: DocumentRefusal | null;
|
|
126
|
+
/**
|
|
127
|
+
* Where a `not-a-document` fault is, as an offset into the text; -1 otherwise.
|
|
128
|
+
*/
|
|
129
|
+
at: number;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The constructors the shell's spelling allows in a value, `new` before any of them.
|
|
133
|
+
*/
|
|
134
|
+
declare const CONSTRUCTORS: readonly ["ObjectId", "ISODate", "Date", "NumberLong", "NumberInt", "NumberDecimal", "Timestamp", "BinData", "UUID", "HexData", "RegExp", "DBRef", "DBPointer", "MinKey", "MaxKey"];
|
|
135
|
+
/**
|
|
136
|
+
* The bare words a value may be.
|
|
137
|
+
*/
|
|
138
|
+
declare const LITERALS: readonly ["true", "false", "null", "undefined", "Infinity", "NaN", "MinKey", "MaxKey"];
|
|
139
|
+
/**
|
|
140
|
+
* The command a text holds, read as the engine reads it.
|
|
141
|
+
*/
|
|
142
|
+
declare function readDocument(text: string): DocumentRead;
|
|
143
|
+
/**
|
|
144
|
+
* What one run should send: the selection where there is one, else the whole text — a command
|
|
145
|
+
* is one document; `none` where either is blank.
|
|
146
|
+
*/
|
|
147
|
+
type RunnableDocument = {
|
|
148
|
+
kind: "one";
|
|
149
|
+
text: string;
|
|
150
|
+
from: number;
|
|
151
|
+
to: number;
|
|
152
|
+
} | {
|
|
153
|
+
kind: "none";
|
|
154
|
+
};
|
|
155
|
+
declare function runnableDocument(text: string, from: number, to: number): RunnableDocument;
|
|
156
|
+
/**
|
|
157
|
+
* Where the caret stands, for completion: at a key of an object, at the value of a key, or
|
|
158
|
+
* nowhere the assist has anything to say.
|
|
159
|
+
*/
|
|
160
|
+
type CaretContext = {
|
|
161
|
+
kind: "key";
|
|
162
|
+
/**
|
|
163
|
+
* The keys of the objects enclosing the caret, outermost first, arrays and constructor
|
|
164
|
+
* calls passed through: `["filter", "age"]` inside the age's own object.
|
|
165
|
+
*/
|
|
166
|
+
path: string[];
|
|
167
|
+
/**
|
|
168
|
+
* The keys already bound in the object the caret is a key of, in order.
|
|
169
|
+
*/
|
|
170
|
+
siblings: string[];
|
|
171
|
+
/**
|
|
172
|
+
* The key's characters typed before the caret.
|
|
173
|
+
*/
|
|
174
|
+
typed: string;
|
|
175
|
+
/**
|
|
176
|
+
* Whether the caret is inside an opened quote.
|
|
177
|
+
*/
|
|
178
|
+
quoted: boolean;
|
|
179
|
+
} | {
|
|
180
|
+
kind: "value";
|
|
181
|
+
path: string[];
|
|
182
|
+
/**
|
|
183
|
+
* The key whose value the caret is at.
|
|
184
|
+
*/
|
|
185
|
+
key: string;
|
|
186
|
+
typed: string;
|
|
187
|
+
quoted: boolean;
|
|
188
|
+
} | {
|
|
189
|
+
kind: "none";
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* A light scan of the text before the caret — not a parse, since a document being typed is
|
|
193
|
+
* rarely one — for the position the caret is at.
|
|
194
|
+
*/
|
|
195
|
+
declare function caretContext(text: string, offset: number): CaretContext;
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region src/markers.d.ts
|
|
198
|
+
/**
|
|
199
|
+
* What a marker says — a code, never a sentence; the host owns the wording:
|
|
200
|
+
*
|
|
201
|
+
* - `"not-a-document"` — the text is not one document in the shell's spelling; the engine
|
|
202
|
+
* refuses it (`error`), marked from where the reading stopped to the end of that line;
|
|
203
|
+
* - `"no-command"` — a document with nothing but `$db` in it; the engine refuses it (`error`);
|
|
204
|
+
* - `"unknown-command"` — a first key the table does not know (`hint`: the server may).
|
|
205
|
+
*/
|
|
206
|
+
type MongodbMarkerCode = Exclude<DocumentRefusal, "empty"> | "unknown-command";
|
|
207
|
+
interface MongodbMarker {
|
|
208
|
+
from: number;
|
|
209
|
+
to: number;
|
|
210
|
+
code: MongodbMarkerCode;
|
|
211
|
+
severity: "error" | "hint";
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* The markers of the text as one command document; none for a blank text.
|
|
215
|
+
*/
|
|
216
|
+
declare function documentMarkers(text: string, commands?: readonly MongoCommand[]): MongodbMarker[];
|
|
217
|
+
//#endregion
|
|
218
|
+
export { PUSH_MODIFIERS as C, keysAt as D, UPDATE_OPERATORS as E, MongoCommand as S, STAGES as T, MongodbHoverCard as _, CaretContext as a, FIELD_OPERATORS as b, LITERALS as c, readDocument as d, runnableDocument as f, MongodbCompletionCandidate as g, MongodbCatalog as h, CONSTRUCTORS as i, RunnableDocument as l, MongodbAssist as m, MongodbMarkerCode as n, DocumentRead as o, CatalogCollection as p, documentMarkers as r, DocumentRefusal as s, MongodbMarker as t, caretContext as u, createMongodbAssist as v, QUERY_OPERATORS as w, MONGO_COMMANDS as x, EXPRESSION_OPERATORS as y };
|
package/dist/monaco.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { m as MongodbAssist, t as MongodbMarker } from "./markers-CQOm7diu.js";
|
|
2
|
+
import * as monacoEditor from "monaco-editor";
|
|
3
|
+
import { IDisposable, editor } from "monaco-editor";
|
|
4
|
+
//#region src/monaco.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The monaco module itself, as `onMount` hands it over.
|
|
7
|
+
*/
|
|
8
|
+
type MonacoModule = typeof monacoEditor;
|
|
9
|
+
/**
|
|
10
|
+
* The language id the editor registers a command document under.
|
|
11
|
+
*/
|
|
12
|
+
declare const MONGODB_LANGUAGE_ID = "mongodb";
|
|
13
|
+
/**
|
|
14
|
+
* Register the language — the id, its Monarch grammar and its bracket configuration — once per
|
|
15
|
+
* monaco module. Call it before the first model of the language is created (`beforeMount`).
|
|
16
|
+
*/
|
|
17
|
+
declare function defineMongodbLanguage(monaco: MonacoModule, languageId?: string): void;
|
|
18
|
+
interface MongodbLanguageHooks {
|
|
19
|
+
/**
|
|
20
|
+
* The assist as it stands right now — read per request rather than captured, so the host
|
|
21
|
+
* can swap it without touching Monaco's registry; null answers nothing.
|
|
22
|
+
*/
|
|
23
|
+
assist: () => MongodbAssist | null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Register completion and hover for one model. The disposables are the caller's to hold:
|
|
27
|
+
* Monaco's registry outlives any editor.
|
|
28
|
+
*/
|
|
29
|
+
declare function registerMongodbLanguage(monaco: MonacoModule, modelPath: string, hooks: MongodbLanguageHooks, languageId?: string): IDisposable[];
|
|
30
|
+
/**
|
|
31
|
+
* The owner Monaco files this package's markers under.
|
|
32
|
+
*/
|
|
33
|
+
declare const MONGODB_MARKER_OWNER = "inkstone-mongodb";
|
|
34
|
+
/**
|
|
35
|
+
* Put `markers` on the model as Monaco's own, replacing this package's last set; the words
|
|
36
|
+
* beside each code are the host's, handed in per code.
|
|
37
|
+
*/
|
|
38
|
+
declare function setMongodbMarkers(monaco: MonacoModule, model: editor.ITextModel, markers: readonly MongodbMarker[], wording: (marker: MongodbMarker) => string): void;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { MONGODB_LANGUAGE_ID, MONGODB_MARKER_OWNER, MonacoModule, MongodbLanguageHooks, defineMongodbLanguage, registerMongodbLanguage, setMongodbMarkers };
|
package/dist/monaco.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { n as LITERALS, t as CONSTRUCTORS } from "./document-CVB9YunA.js";
|
|
2
|
+
//#region src/monaco.ts
|
|
3
|
+
const MONGODB_LANGUAGE_ID = "mongodb";
|
|
4
|
+
const LANGUAGES_KEY = Symbol.for("coldsmirk.inkstone.mongodb.languages");
|
|
5
|
+
function definedLanguages(monaco) {
|
|
6
|
+
const holder = globalThis;
|
|
7
|
+
const registry = holder[LANGUAGES_KEY] ?? /* @__PURE__ */ new WeakMap();
|
|
8
|
+
holder[LANGUAGES_KEY] = registry;
|
|
9
|
+
const existing = registry.get(monaco);
|
|
10
|
+
if (existing) return existing;
|
|
11
|
+
const created = /* @__PURE__ */ new Set();
|
|
12
|
+
registry.set(monaco, created);
|
|
13
|
+
return created;
|
|
14
|
+
}
|
|
15
|
+
function grammar() {
|
|
16
|
+
const calls = CONSTRUCTORS.filter((name) => name !== "MinKey" && name !== "MaxKey").join("|");
|
|
17
|
+
return {
|
|
18
|
+
unicode: true,
|
|
19
|
+
tokenizer: { root: [
|
|
20
|
+
[/\$[\w$]*(?=\s*:)/u, "keyword"],
|
|
21
|
+
[/"\$(?:[^"\\]|\\.)*"(?=\s*:)/u, "keyword"],
|
|
22
|
+
[/'\$(?:[^'\\]|\\.)*'(?=\s*:)/u, "keyword"],
|
|
23
|
+
[/"(?:[^"\\]|\\.)*"(?=\s*:)/u, "support.type.property-name.json"],
|
|
24
|
+
[/'(?:[^'\\]|\\.)*'(?=\s*:)/u, "support.type.property-name.json"],
|
|
25
|
+
[/[A-Za-z_$][\w$]*(?=\s*:)/u, "support.type.property-name.json"],
|
|
26
|
+
[/"(?:[^"\\]|\\.)*"/u, "string"],
|
|
27
|
+
[/'(?:[^'\\]|\\.)*'/u, "string"],
|
|
28
|
+
[/\/(?:[^/\\\n]|\\.)+\/[a-z]*/u, "string.regexp"],
|
|
29
|
+
[/-?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?/iu, "constant.numeric"],
|
|
30
|
+
[/\bnew\b/u, "keyword"],
|
|
31
|
+
[new RegExp(String.raw`\b(?:${calls})\b(?=\s*\()`, "u"), "support.function"],
|
|
32
|
+
[new RegExp(String.raw`\b(?:${LITERALS.join("|")})\b`, "u"), "constant.language"],
|
|
33
|
+
[/[{}[\]()]/u, "delimiter.bracket"],
|
|
34
|
+
[/[,:]/u, "delimiter"],
|
|
35
|
+
[/\s+/u, "white"],
|
|
36
|
+
[/[^\s"'{}[\]():,]+/u, "variable"]
|
|
37
|
+
] }
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function defineMongodbLanguage(monaco, languageId = MONGODB_LANGUAGE_ID) {
|
|
41
|
+
const defined = definedLanguages(monaco);
|
|
42
|
+
if (defined.has(languageId)) return;
|
|
43
|
+
defined.add(languageId);
|
|
44
|
+
monaco.languages.register({ id: languageId });
|
|
45
|
+
monaco.languages.setLanguageConfiguration(languageId, {
|
|
46
|
+
brackets: [
|
|
47
|
+
["{", "}"],
|
|
48
|
+
["[", "]"],
|
|
49
|
+
["(", ")"]
|
|
50
|
+
],
|
|
51
|
+
autoClosingPairs: [
|
|
52
|
+
{
|
|
53
|
+
open: "{",
|
|
54
|
+
close: "}"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
open: "[",
|
|
58
|
+
close: "]"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
open: "(",
|
|
62
|
+
close: ")"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
open: "\"",
|
|
66
|
+
close: "\"",
|
|
67
|
+
notIn: ["string"]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
open: "'",
|
|
71
|
+
close: "'",
|
|
72
|
+
notIn: ["string"]
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
surroundingPairs: [
|
|
76
|
+
{
|
|
77
|
+
open: "{",
|
|
78
|
+
close: "}"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
open: "[",
|
|
82
|
+
close: "]"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
open: "(",
|
|
86
|
+
close: ")"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
open: "\"",
|
|
90
|
+
close: "\""
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
open: "'",
|
|
94
|
+
close: "'"
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
wordPattern: /[\w$]+/u
|
|
98
|
+
});
|
|
99
|
+
monaco.languages.setMonarchTokensProvider(languageId, grammar());
|
|
100
|
+
}
|
|
101
|
+
function itemKinds(monaco) {
|
|
102
|
+
const kinds = monaco.languages.CompletionItemKind;
|
|
103
|
+
return {
|
|
104
|
+
command: kinds.Function,
|
|
105
|
+
field: kinds.Field,
|
|
106
|
+
operator: kinds.Keyword,
|
|
107
|
+
stage: kinds.Method,
|
|
108
|
+
collection: kinds.Struct,
|
|
109
|
+
database: kinds.Module
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function registerMongodbLanguage(monaco, modelPath, hooks, languageId = MONGODB_LANGUAGE_ID) {
|
|
113
|
+
const kinds = itemKinds(monaco);
|
|
114
|
+
const path = monaco.Uri.parse(modelPath).toString();
|
|
115
|
+
const answering = (model) => model.uri.toString() === path ? hooks.assist() : null;
|
|
116
|
+
return [monaco.languages.registerCompletionItemProvider(languageId, {
|
|
117
|
+
triggerCharacters: [
|
|
118
|
+
"\"",
|
|
119
|
+
"'",
|
|
120
|
+
"$"
|
|
121
|
+
],
|
|
122
|
+
provideCompletionItems: (model, position) => {
|
|
123
|
+
const assist = answering(model);
|
|
124
|
+
if (assist === null) return { suggestions: [] };
|
|
125
|
+
return { suggestions: assist.complete(model.getValue(), model.getOffsetAt(position)).map((candidate, index) => {
|
|
126
|
+
return {
|
|
127
|
+
label: candidate.label,
|
|
128
|
+
kind: kinds[candidate.kind],
|
|
129
|
+
insertText: candidate.insertText,
|
|
130
|
+
insertTextRules: candidate.snippet ? monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet : void 0,
|
|
131
|
+
detail: candidate.detail,
|
|
132
|
+
documentation: candidate.documentation,
|
|
133
|
+
range: new monaco.Range(position.lineNumber, Math.max(1, position.column - candidate.replace), position.lineNumber, position.column),
|
|
134
|
+
sortText: `${String(candidate.sortGroup)}:${String(index).padStart(4, "0")}`
|
|
135
|
+
};
|
|
136
|
+
}) };
|
|
137
|
+
}
|
|
138
|
+
}), monaco.languages.registerHoverProvider(languageId, { provideHover: (model, position) => {
|
|
139
|
+
const assist = answering(model);
|
|
140
|
+
if (assist === null) return null;
|
|
141
|
+
const card = assist.hover(model.getValue(), model.getOffsetAt(position));
|
|
142
|
+
if (card === null) return null;
|
|
143
|
+
const from = model.getPositionAt(card.from);
|
|
144
|
+
const to = model.getPositionAt(card.to);
|
|
145
|
+
return {
|
|
146
|
+
range: new monaco.Range(from.lineNumber, from.column, to.lineNumber, to.column),
|
|
147
|
+
contents: card.markdown.map((value) => {
|
|
148
|
+
return { value };
|
|
149
|
+
})
|
|
150
|
+
};
|
|
151
|
+
} })];
|
|
152
|
+
}
|
|
153
|
+
const MONGODB_MARKER_OWNER = "inkstone-mongodb";
|
|
154
|
+
function setMongodbMarkers(monaco, model, markers, wording) {
|
|
155
|
+
const severities = monaco.MarkerSeverity;
|
|
156
|
+
monaco.editor.setModelMarkers(model, MONGODB_MARKER_OWNER, markers.map((marker) => {
|
|
157
|
+
const from = model.getPositionAt(marker.from);
|
|
158
|
+
const to = model.getPositionAt(marker.to);
|
|
159
|
+
return {
|
|
160
|
+
severity: marker.severity === "error" ? severities.Error : severities.Hint,
|
|
161
|
+
message: wording(marker),
|
|
162
|
+
startLineNumber: from.lineNumber,
|
|
163
|
+
startColumn: from.column,
|
|
164
|
+
endLineNumber: to.lineNumber,
|
|
165
|
+
endColumn: to.column
|
|
166
|
+
};
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
export { MONGODB_LANGUAGE_ID, MONGODB_MARKER_OWNER, defineMongodbLanguage, registerMongodbLanguage, setMongodbMarkers };
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { m as MongodbAssist, t as MongodbMarker } from "./markers-CQOm7diu.js";
|
|
2
|
+
import { MonacoModule } from "./monaco.js";
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
import { editor } from "monaco-editor";
|
|
5
|
+
//#region src/react.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* What a host reaches into the editor for: inserting at the caret — a collection tree is an
|
|
8
|
+
* input device — and the focus.
|
|
9
|
+
*/
|
|
10
|
+
interface MongodbEditorHandle {
|
|
11
|
+
insert: (text: string) => void;
|
|
12
|
+
focus: () => void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Where the operator is in the document, in offsets — what `runnableDocument` reads to decide
|
|
16
|
+
* what a run sends. `from` and `to` are the selection's bounds and equal where nothing is
|
|
17
|
+
* selected; the text rides with them because the two must belong to the same instant.
|
|
18
|
+
*/
|
|
19
|
+
interface MongodbCaret {
|
|
20
|
+
text: string;
|
|
21
|
+
from: number;
|
|
22
|
+
to: number;
|
|
23
|
+
}
|
|
24
|
+
interface MongodbEditorProps {
|
|
25
|
+
value: string;
|
|
26
|
+
onChange: (value: string) => void;
|
|
27
|
+
/**
|
|
28
|
+
* Render with the dark theme; the component never reads the OS `prefers-color-scheme`.
|
|
29
|
+
*
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
dark?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Editor height in px, or `"fill"` to consume the parent's resolved height (the parent must be
|
|
35
|
+
* a `min-height: 0` flex item).
|
|
36
|
+
*/
|
|
37
|
+
height?: number | "fill";
|
|
38
|
+
fontSize?: number;
|
|
39
|
+
/**
|
|
40
|
+
* UI locale for Monaco's chrome — page-global and locked by the first editor to mount.
|
|
41
|
+
*/
|
|
42
|
+
locale?: "en" | "zh-cn";
|
|
43
|
+
placeholder?: string;
|
|
44
|
+
/**
|
|
45
|
+
* The editor's accessible name, set on Monaco's own input surface.
|
|
46
|
+
*/
|
|
47
|
+
ariaLabel: string;
|
|
48
|
+
/**
|
|
49
|
+
* The Monaco language id the model registers under; the grammar is defined under it.
|
|
50
|
+
*
|
|
51
|
+
* @default "mongodb"
|
|
52
|
+
*/
|
|
53
|
+
languageId?: string;
|
|
54
|
+
loading?: ReactNode;
|
|
55
|
+
failure?: ReactNode;
|
|
56
|
+
/**
|
|
57
|
+
* Ctrl/Cmd-Enter — the run key, handed the editor's live caret; the host reads the document
|
|
58
|
+
* to send off it with `runnableDocument`.
|
|
59
|
+
*/
|
|
60
|
+
onRun?: (caret: MongodbCaret) => void;
|
|
61
|
+
/**
|
|
62
|
+
* The label the run action shows in Monaco's own chrome.
|
|
63
|
+
*
|
|
64
|
+
* @default "Run command"
|
|
65
|
+
*/
|
|
66
|
+
runActionLabel?: string;
|
|
67
|
+
/**
|
|
68
|
+
* The caret moved, or the text under it changed.
|
|
69
|
+
*/
|
|
70
|
+
onCaret?: (caret: MongodbCaret) => void;
|
|
71
|
+
onReady?: (handle: MongodbEditorHandle) => void;
|
|
72
|
+
/**
|
|
73
|
+
* Runs after this component's own mount wiring, with the live editor and the monaco module.
|
|
74
|
+
*/
|
|
75
|
+
onMount?: (instance: editor.IStandaloneCodeEditor, monaco: MonacoModule) => void;
|
|
76
|
+
/**
|
|
77
|
+
* What completion and hover answer from; neither without it.
|
|
78
|
+
*/
|
|
79
|
+
assist?: MongodbAssist;
|
|
80
|
+
/**
|
|
81
|
+
* The words each marker shows, per code (`documentMarkers`) — the host's language. No
|
|
82
|
+
* markers are drawn without it.
|
|
83
|
+
*/
|
|
84
|
+
markerWording?: (marker: MongodbMarker) => string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The command editing surface: Monaco under the `mongodb` grammar, one command document in the
|
|
88
|
+
* shell's spelling, the run key handing the caret over, completion and hover wired through
|
|
89
|
+
* `registerMongodbLanguage`, and the markers refreshed on every change.
|
|
90
|
+
*/
|
|
91
|
+
declare function MongodbEditor({ value, onChange, dark, height, fontSize, locale, placeholder, ariaLabel, languageId, loading, failure, onRun, runActionLabel, onCaret, onReady, onMount, assist, markerWording }: MongodbEditorProps): import("react").JSX.Element;
|
|
92
|
+
//#endregion
|
|
93
|
+
export { MongodbCaret, MongodbEditor, MongodbEditorHandle, MongodbEditorProps };
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { t as documentMarkers } from "./markers-Bdvbut2O.js";
|
|
2
|
+
import { MONGODB_LANGUAGE_ID, defineMongodbLanguage, registerMongodbLanguage, setMongodbMarkers } from "./monaco.js";
|
|
3
|
+
import { MonacoEditor } from "@coldsmirk/inkstone-react/monaco";
|
|
4
|
+
import { useEffect, useInsertionEffect, useRef, useState } from "react";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
//#region src/react.tsx
|
|
7
|
+
const EDITOR_ID_KEY = Symbol.for("coldsmirk.inkstone.mongodb.editorId");
|
|
8
|
+
function nextEditorId() {
|
|
9
|
+
const holder = globalThis;
|
|
10
|
+
const id = holder[EDITOR_ID_KEY] ?? 0;
|
|
11
|
+
holder[EDITOR_ID_KEY] = id + 1;
|
|
12
|
+
return id;
|
|
13
|
+
}
|
|
14
|
+
function useLatest(value) {
|
|
15
|
+
const ref = useRef(value);
|
|
16
|
+
useInsertionEffect(() => {
|
|
17
|
+
ref.current = value;
|
|
18
|
+
});
|
|
19
|
+
return ref;
|
|
20
|
+
}
|
|
21
|
+
function caretOf(instance) {
|
|
22
|
+
const model = instance.getModel();
|
|
23
|
+
const selection = instance.getSelection();
|
|
24
|
+
if (model === null || selection === null) return {
|
|
25
|
+
text: "",
|
|
26
|
+
from: 0,
|
|
27
|
+
to: 0
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
text: model.getValue(),
|
|
31
|
+
from: model.getOffsetAt(selection.getStartPosition()),
|
|
32
|
+
to: model.getOffsetAt(selection.getEndPosition())
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function MongodbEditor({ value, onChange, dark = false, height = 200, fontSize, locale, placeholder, ariaLabel, languageId = MONGODB_LANGUAGE_ID, loading, failure, onRun, runActionLabel = "Run command", onCaret, onReady, onMount, assist, markerWording }) {
|
|
36
|
+
const [modelPath] = useState(() => `file:///inkstone-mongodb/${String(nextEditorId())}.mongodb`);
|
|
37
|
+
const bindings = useRef([]);
|
|
38
|
+
const assistRef = useLatest(assist ?? null);
|
|
39
|
+
const runRef = useLatest(onRun);
|
|
40
|
+
const caretChangedRef = useLatest(onCaret);
|
|
41
|
+
const readyRef = useLatest(onReady);
|
|
42
|
+
const mountRef = useLatest(onMount);
|
|
43
|
+
const wordingRef = useLatest(markerWording);
|
|
44
|
+
useEffect(() => () => {
|
|
45
|
+
for (const binding of bindings.current) binding.dispose();
|
|
46
|
+
bindings.current = [];
|
|
47
|
+
}, []);
|
|
48
|
+
const fill = height === "fill";
|
|
49
|
+
return /* @__PURE__ */ jsx("div", {
|
|
50
|
+
style: fill ? {
|
|
51
|
+
height: "100%",
|
|
52
|
+
minHeight: 0
|
|
53
|
+
} : { height },
|
|
54
|
+
children: /* @__PURE__ */ jsx(MonacoEditor, {
|
|
55
|
+
showLineNumbers: true,
|
|
56
|
+
dark,
|
|
57
|
+
failure,
|
|
58
|
+
fontSize,
|
|
59
|
+
height: fill ? "100%" : height,
|
|
60
|
+
language: languageId,
|
|
61
|
+
loading,
|
|
62
|
+
locale,
|
|
63
|
+
options: { ariaLabel },
|
|
64
|
+
path: modelPath,
|
|
65
|
+
placeholder,
|
|
66
|
+
value,
|
|
67
|
+
beforeMount: (monaco) => {
|
|
68
|
+
defineMongodbLanguage(monaco, languageId);
|
|
69
|
+
},
|
|
70
|
+
onChange,
|
|
71
|
+
onMount: (live, monaco) => {
|
|
72
|
+
const mark = () => {
|
|
73
|
+
const model = live.getModel();
|
|
74
|
+
const wording = wordingRef.current;
|
|
75
|
+
if (model !== null && wording !== void 0) setMongodbMarkers(monaco, model, documentMarkers(model.getValue()), wording);
|
|
76
|
+
};
|
|
77
|
+
bindings.current.push(live.onDidChangeCursorSelection(() => caretChangedRef.current?.(caretOf(live))), live.onDidChangeModelContent(mark), live.addAction({
|
|
78
|
+
id: "inkstone-mongodb.run",
|
|
79
|
+
label: runActionLabel,
|
|
80
|
+
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
|
|
81
|
+
run: (current) => runRef.current?.(caretOf(current))
|
|
82
|
+
}), ...registerMongodbLanguage(monaco, modelPath, { assist: () => assistRef.current }, languageId));
|
|
83
|
+
mark();
|
|
84
|
+
readyRef.current?.({
|
|
85
|
+
insert: (text) => {
|
|
86
|
+
const selection = live.getSelection();
|
|
87
|
+
if (selection) live.executeEdits("inkstone-mongodb-insert", [{
|
|
88
|
+
range: selection,
|
|
89
|
+
text,
|
|
90
|
+
forceMoveMarkers: true
|
|
91
|
+
}]);
|
|
92
|
+
live.focus();
|
|
93
|
+
},
|
|
94
|
+
focus: () => {
|
|
95
|
+
live.focus();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
mountRef.current?.(live, monaco);
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
export { MongodbEditor };
|