@orz/query 0.0.0-stage → 0.0.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.
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Slash commands (T-161), the text half. A command is an ordinary draft line
3
+ * that happens to parse: it lives in the document, deleting the line cancels
4
+ * it, and typing `/close` by hand is exactly as good as picking it from the
5
+ * panel — the panel is input assistance, not the carrier. Submitting strips
6
+ * the recognized lines out of the body and compiles each one to the caller's
7
+ * own value.
8
+ *
9
+ * Everything here is pure so both an editor (highlighting, the panel) and
10
+ * the caller (button label, submit) read the same verdict, and a server or a
11
+ * CLI can read a draft without an editor at all.
12
+ */
13
+ import { BRACE_LIMIT, expandBraces } from "./braces.js";
14
+ import { shellWords } from "./shell-words.js";
15
+ import { didYouMean } from "./suggest.js";
16
+ import { providerOf, suggestionCandidates, valueVerdict, worstVerdict, } from "./values.js";
17
+ const COMMAND_NAME = /^[a-z0-9-]+$/;
18
+ /**
19
+ * Throws on a name no line could ever spell and on a name given twice, which
20
+ * would silently shadow the first: both are mistakes in the caller's list,
21
+ * not in user input. So are a required argument after an optional one and a
22
+ * rest argument that is not last, since no line could reach them.
23
+ */
24
+ export function createCommandRegistry(commands) {
25
+ const byName = new Map();
26
+ for (const command of commands) {
27
+ if (!COMMAND_NAME.test(command.name)) {
28
+ throw new Error(`slash command "${command.name}" must match ${COMMAND_NAME}`);
29
+ }
30
+ if (byName.has(command.name)) {
31
+ throw new Error(`slash command "${command.name}" is defined twice`);
32
+ }
33
+ for (const [i, arg] of command.args.entries()) {
34
+ const next = command.args[i + 1];
35
+ if (next === undefined)
36
+ continue;
37
+ if (arg.optional && !next.optional) {
38
+ throw new Error(`slash command "${command.name}": required argument "${next.name}" follows optional "${arg.name}"`);
39
+ }
40
+ if (arg.rest) {
41
+ throw new Error(`slash command "${command.name}": only the last argument may take the rest of the line, not "${arg.name}"`);
42
+ }
43
+ }
44
+ byName.set(command.name, command);
45
+ }
46
+ return { commands, byName };
47
+ }
48
+ /**
49
+ * `In Progress` → `in-progress`. Lowercase, whitespace folded to hyphens,
50
+ * everything a slash command cannot carry dropped: a status named `Won't
51
+ * fix` still gets a typeable command.
52
+ */
53
+ export function slugifyCommandName(name) {
54
+ return name
55
+ .toLowerCase()
56
+ .trim()
57
+ .replace(/\s+/g, "-")
58
+ .replace(/[^a-z0-9-]/g, "")
59
+ .replace(/-{2,}/g, "-")
60
+ .replace(/^-|-$/g, "");
61
+ }
62
+ const COMMAND_HEAD = /^\/([a-z0-9-]+)(?:[ \t]|$)/;
63
+ /** A rest argument's remainder, read with no quoting at all. */
64
+ export function rawWord(text, start) {
65
+ const raw = text.slice(start);
66
+ return {
67
+ start,
68
+ end: text.length,
69
+ raw,
70
+ value: raw,
71
+ segments: [{ start, end: text.length, quoted: "none" }],
72
+ };
73
+ }
74
+ /**
75
+ * The words after the command name, one per argument, or null when there
76
+ * are too few for the required arguments or too many for the command: then
77
+ * the line is not that command and stays prose, so `/close the door` is a
78
+ * sentence. An open quote swallows the words after it, so with one the line
79
+ * still counts when it has too few: it is reported broken, not posted.
80
+ *
81
+ * A rest argument whose text does not start with a quote is the raw
82
+ * remainder, so `/label In Progress` and `/status Won't fix` need no quoting
83
+ * and no escape is processed. A quoted one is a shell word like any other.
84
+ */
85
+ function argumentWords(command, text, nameEnd) {
86
+ const words = shellWords(text.slice(nameEnd), nameEnd);
87
+ const last = command.args.length - 1;
88
+ const head = words[last];
89
+ if (command.args[last]?.rest &&
90
+ head !== undefined &&
91
+ !/^['"]/.test(head.raw)) {
92
+ words.splice(last, words.length - last, rawWord(text, head.start));
93
+ }
94
+ const required = command.args.filter((arg) => !arg.optional).length;
95
+ const unclosed = words.at(-1)?.unclosed === true;
96
+ if ((!unclosed && words.length < required) ||
97
+ words.length > command.args.length) {
98
+ return null;
99
+ }
100
+ return words;
101
+ }
102
+ function invalidMessage(spec, value, provider) {
103
+ const message = spec.kind === "enum"
104
+ ? `"${value}" is not one of ${spec.values.join(", ")}`
105
+ : `nothing named "${value}"`;
106
+ const suggestion = didYouMean(value, suggestionCandidates(spec, provider));
107
+ return suggestion === null
108
+ ? message
109
+ : `${message} (did you mean "${suggestion}"?)`;
110
+ }
111
+ /**
112
+ * Does this line, on its own, spell a command? Only whole lines count: a `/`
113
+ * mid-sentence is prose, and prose is the default — an unknown `/clsoe` is
114
+ * left exactly where it was typed rather than guessed at.
115
+ *
116
+ * A recognized line is broken, and `compile` is not called, when a quote is
117
+ * left open, a brace expansion goes over the limit, or a value has an
118
+ * `invalid` verdict. Otherwise `compile` runs and stays the authority.
119
+ */
120
+ export function recognizeCommandLine(line, registry, providers) {
121
+ if (!line.startsWith("/"))
122
+ return null;
123
+ const text = line.trimEnd();
124
+ const match = COMMAND_HEAD.exec(text);
125
+ if (match === null)
126
+ return null;
127
+ const name = match[1];
128
+ const command = registry.byName.get(name);
129
+ if (command === undefined)
130
+ return null;
131
+ const nameEnd = name.length + 1;
132
+ const words = argumentWords(command, text, nameEnd);
133
+ if (words === null)
134
+ return null;
135
+ const args = [];
136
+ const verdicts = [];
137
+ let reason;
138
+ const fail = (why) => {
139
+ reason ??= `/${command.name}: ${why}`;
140
+ };
141
+ for (const [i, word] of words.entries()) {
142
+ const spec = command.args[i];
143
+ const provider = providerOf(spec.value, providers);
144
+ if (word.unclosed)
145
+ fail("unclosed quote");
146
+ let values = [word.value];
147
+ if (spec.multiple) {
148
+ const expansion = expandBraces(word, { limit: BRACE_LIMIT });
149
+ if (!expansion.ok) {
150
+ fail(`"${word.value}" ${expansion.reason}`);
151
+ args.push(values);
152
+ verdicts.push("invalid");
153
+ continue;
154
+ }
155
+ values = expansion.words;
156
+ args.push(values);
157
+ }
158
+ else {
159
+ args.push(word.value);
160
+ }
161
+ const each = values.map((value) => {
162
+ const verdict = valueVerdict(spec.value, value, provider);
163
+ if (verdict === "invalid") {
164
+ fail(invalidMessage(spec.value, value, provider));
165
+ }
166
+ return verdict;
167
+ });
168
+ const worst = worstVerdict(each) ?? "valid";
169
+ verdicts.push(word.unclosed ? "invalid" : worst);
170
+ }
171
+ if (reason !== undefined) {
172
+ return { command, words, args, verdicts, compiled: null, reason };
173
+ }
174
+ const compiled = command.compile(args);
175
+ if (compiled !== null)
176
+ return { command, words, args, verdicts, compiled };
177
+ const typed = text.slice(nameEnd).trim();
178
+ return {
179
+ command,
180
+ words,
181
+ args,
182
+ verdicts,
183
+ compiled,
184
+ reason: command.invalidReason?.(args) ??
185
+ (typed === ""
186
+ ? `/${command.name} has no target here`
187
+ : `/${command.name}: nothing named "${typed}"`),
188
+ };
189
+ }
190
+ /**
191
+ * Which lines a fenced code block covers. Line-based on purpose: 4-space code
192
+ * blocks cannot be told from list continuations without a full markdown parse.
193
+ */
194
+ function fencedLines(lines) {
195
+ const inCode = lines.map(() => false);
196
+ let fence = null;
197
+ for (const [i, line] of lines.entries()) {
198
+ const open = /^ {0,3}(`{3,}|~{3,})/.exec(line);
199
+ if (open?.[1] !== undefined) {
200
+ const char = open[1][0];
201
+ const len = open[1].length;
202
+ if (fence === null) {
203
+ fence = { char, len };
204
+ inCode[i] = true;
205
+ continue;
206
+ }
207
+ if (char === fence.char && len >= fence.len) {
208
+ fence = null;
209
+ inCode[i] = true;
210
+ continue;
211
+ }
212
+ }
213
+ if (fence !== null)
214
+ inCode[i] = true;
215
+ }
216
+ return inCode;
217
+ }
218
+ /** Line indices (0-based) that carry a recognized command. */
219
+ export function commandLinesOf(text, registry, providers) {
220
+ const lines = text.split("\n");
221
+ const inCode = fencedLines(lines);
222
+ const found = new Map();
223
+ for (const [i, line] of lines.entries()) {
224
+ if (inCode[i])
225
+ continue;
226
+ const recognized = recognizeCommandLine(line, registry, providers);
227
+ if (recognized !== null)
228
+ found.set(i, recognized);
229
+ }
230
+ return found;
231
+ }
232
+ export function parseCommandLines(text, registry, providers) {
233
+ const lines = text.split("\n");
234
+ const found = commandLinesOf(text, registry, providers);
235
+ const commands = [];
236
+ const invalid = [];
237
+ const summaries = [];
238
+ const kept = [];
239
+ for (const [i, line] of lines.entries()) {
240
+ const recognized = found.get(i);
241
+ if (recognized === undefined) {
242
+ kept.push(line);
243
+ continue;
244
+ }
245
+ if (recognized.compiled === null) {
246
+ invalid.push({ line: line.trim(), reason: recognized.reason ?? "" });
247
+ continue;
248
+ }
249
+ commands.push(recognized.compiled);
250
+ summaries.push(recognized.command.summarize(recognized.args));
251
+ }
252
+ return { body: kept.join("\n").trim(), commands, invalid, summaries };
253
+ }
254
+ /** "close" / "close and label bug" — the tail of the submit button's label. */
255
+ export function summarizeCommands(summaries) {
256
+ if (summaries.length <= 1)
257
+ return summaries.join("");
258
+ return `${summaries.slice(0, -1).join(", ")} and ${summaries.at(-1)}`;
259
+ }
260
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Completion at the caret, the pure half: what is being typed, where it
3
+ * starts and ends, and which values fit. Fetching and rendering stay with
4
+ * the editor that asks — a search box and a CodeMirror panel read the same
5
+ * slot.
6
+ */
7
+ import { type CommandRegistry } from "./commands.ts";
8
+ import { type CompiledQuerySchema, type QueryPart } from "./query.ts";
9
+ import type { ValueOption, ValueProvider, ValueSpec } from "./values.ts";
10
+ /** Offsets are into the query, or into the line for the command kinds. */
11
+ export type CompletionSlot = {
12
+ kind: "key";
13
+ from: number;
14
+ to: number;
15
+ typed: string;
16
+ negated: boolean;
17
+ } | {
18
+ kind: "value";
19
+ from: number;
20
+ to: number;
21
+ typed: string;
22
+ spec: ValueSpec;
23
+ key: string;
24
+ /** Only the current alternative of a brace group is the slot. */
25
+ inBraces: boolean;
26
+ } | {
27
+ kind: "command";
28
+ from: number;
29
+ to: number;
30
+ typed: string;
31
+ } | {
32
+ kind: "argument";
33
+ from: number;
34
+ to: number;
35
+ typed: string;
36
+ spec: ValueSpec;
37
+ command: string;
38
+ index: number;
39
+ last: boolean;
40
+ optional: boolean;
41
+ /** A rest argument's raw remainder, where a value goes in as typed. */
42
+ rest: boolean;
43
+ inBraces: boolean;
44
+ };
45
+ export type ValueSlot = Extract<CompletionSlot, {
46
+ kind: "value";
47
+ } | {
48
+ kind: "argument";
49
+ }>;
50
+ /**
51
+ * What one source offers, and whether the caret is actually on something it
52
+ * recognises. `matched` is the whole ordering rule: a source that only
53
+ * *could* be useful stays below the submit row.
54
+ */
55
+ export type SourceResult<Row> = {
56
+ matched: boolean;
57
+ rows: readonly Row[];
58
+ /** Takes only what the other sources left, and never crowds them out. */
59
+ yields?: boolean;
60
+ };
61
+ /**
62
+ * One offered completion. A single line: an icon, the syntax in a monospace
63
+ * run, and at most a very short grey note beside it — never a second line of
64
+ * description. The dropdown is something to glance at mid-keystroke.
65
+ */
66
+ export type CompletionRow = {
67
+ id: string;
68
+ /** The syntax itself, e.g. `harness:` or `area:web`. */
69
+ text: string;
70
+ /** A few words at most, on the same line. */
71
+ hint?: string;
72
+ icon: "key" | "value";
73
+ /** The whole query after accepting this, and where the caret lands. */
74
+ apply: {
75
+ value: string;
76
+ caret: number;
77
+ };
78
+ };
79
+ /**
80
+ * The slot under the caret in a query: a value where the caret stands after
81
+ * a known key's colon, else the bare word the caret stands in (or an empty
82
+ * one where it stands) as a key. Null where a key cannot go at all: inside a
83
+ * filter's key, and inside quotes, which is how one asks for a literal.
84
+ */
85
+ export declare function queryCompletionSlot(schema: CompiledQuerySchema, parts: readonly QueryPart[], query: string, caret: number): CompletionSlot | null;
86
+ /**
87
+ * The slot at the end of `lineBeforeCaret`: the command word itself, or the
88
+ * argument the caret is in. Null where the line cannot be a command, past
89
+ * the last argument, and for a command that takes none.
90
+ */
91
+ export declare function commandCompletionSlot<T>(registry: CommandRegistry<T>, lineBeforeCaret: string): CompletionSlot | null;
92
+ /**
93
+ * The values a slot can take: the enum's set, the specials, and the
94
+ * provider's snapshot, in that order. `"fetch"` tells the caller to ask a
95
+ * `search` provider for `slot.typed` and call again with what it answered.
96
+ */
97
+ export declare function slotValues(slot: ValueSlot, provider?: ValueProvider, fetched?: readonly ValueOption[]): readonly ValueOption[] | "fetch";
98
+ /** Options that `typed` prefixes, case-insensitively. */
99
+ export declare function filterOptions(options: readonly ValueOption[], typed: string): ValueOption[];
100
+ /** Splice `text` over `[start, end)` and put the caret after it. */
101
+ export declare function splice(query: string, start: number, end: number, text: string): {
102
+ value: string;
103
+ caret: number;
104
+ };
105
+ /**
106
+ * Splice, and leave the caret past a space, so that taking an offer ends a
107
+ * word rather than parking the caret against the next one.
108
+ *
109
+ * Only values do this. A key is completed to its colon and the value comes
110
+ * next with nothing in between, so a space there would have to be deleted
111
+ * again.
112
+ *
113
+ * The cost, taken deliberately: a value can be a comma-separated list, and
114
+ * closing it after one value means a second one needs a backspace. That is
115
+ * the rare path; ending a word is every time.
116
+ */
117
+ export declare function spliceThenSpace(query: string, start: number, end: number, text: string): {
118
+ value: string;
119
+ caret: number;
120
+ };
121
+ /**
122
+ * How a value is spelled into a command argument: as one shell word, except
123
+ * in a rest argument's raw remainder, where quotes would be read literally.
124
+ */
125
+ export declare function quoteArgument(slot: Extract<CompletionSlot, {
126
+ kind: "argument";
127
+ }>, value: string): string;
128
+ /**
129
+ * Keys, offered against the word the caret is in. Only a non-empty prefix of
130
+ * a real key counts as a match; a bare space lists them all, below submit.
131
+ */
132
+ export declare function keyRows(schema: CompiledQuerySchema, query: string, slot: CompletionSlot | null): SourceResult<CompletionRow>;
133
+ /**
134
+ * Values, offered whenever the caret stands where one goes. The colon is
135
+ * already typed by then, so the intent is not in doubt and this always counts
136
+ * as a match — including on an empty value, where the whole list is what the
137
+ * reader is asking for. `options` are offered as given; narrow them with
138
+ * `filterOptions` first. Inside a brace group no space follows, since the
139
+ * group is still open.
140
+ */
141
+ export declare function valueRows(schema: CompiledQuerySchema, query: string, slot: CompletionSlot | null, options: readonly ValueOption[]): SourceResult<CompletionRow>;
142
+ /**
143
+ * The offered rows, in order: **the submit row is first** unless something
144
+ * is genuinely being completed.
145
+ *
146
+ * The reader is typing a query, not filling in a form. Putting a completion
147
+ * above the submit row means Enter submits something they did not ask for,
148
+ * and the only evidence that they wanted one is that what they typed
149
+ * actually prefixes something. Everything else — the full list of keys after
150
+ * a space, say — is still shown, because that is how the syntax is
151
+ * discovered, but it waits below.
152
+ *
153
+ * `limit` is spent here rather than inside each source, so that a source
154
+ * added later lands in the same budget without having to remember one. The
155
+ * matched rows spend it first — they are what the reader is completing — the
156
+ * submit row is never spent, and whatever is left goes to the rest.
157
+ *
158
+ * A source marked `yields` waits for both of those to be served and takes
159
+ * only the remainder. History is the one that does in todou (T-270): a
160
+ * completion or a jump answers the string being typed right now, history
161
+ * answers what was searched before, and the keys are the only place the
162
+ * syntax can be discovered — so history is what gives way when the panel is
163
+ * full.
164
+ */
165
+ export declare function orderRows<Row>(results: readonly SourceResult<Row>[], submit: Row, limit?: number): Row[];
166
+ //# sourceMappingURL=completion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../src/completion.ts"],"names":[],"mappings":"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"}