neonctl 2.27.1 → 2.29.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 +35 -3
- package/dist/analytics.js +52 -34
- package/dist/api.js +643 -13
- package/dist/auth.js +50 -44
- package/dist/cli.js +8 -1
- package/dist/commands/auth.js +64 -51
- package/dist/commands/bootstrap.js +115 -157
- package/dist/commands/branches.js +160 -150
- package/dist/commands/bucket.js +183 -146
- package/dist/commands/checkout.js +51 -51
- package/dist/commands/config.js +228 -82
- package/dist/commands/connection_string.js +62 -62
- package/dist/commands/data_api.js +100 -101
- package/dist/commands/databases.js +29 -26
- package/dist/commands/deploy.js +12 -12
- package/dist/commands/dev.js +114 -114
- package/dist/commands/env.js +43 -43
- package/dist/commands/functions.js +101 -104
- package/dist/commands/index.js +27 -25
- package/dist/commands/init.js +23 -22
- package/dist/commands/ip_allow.js +29 -29
- package/dist/commands/link.js +232 -182
- package/dist/commands/neon_auth.js +385 -370
- package/dist/commands/operations.js +11 -11
- package/dist/commands/orgs.js +8 -8
- package/dist/commands/projects.js +103 -101
- package/dist/commands/psql.js +31 -31
- package/dist/commands/roles.js +27 -24
- package/dist/commands/schema_diff.js +25 -26
- package/dist/commands/set_context.js +17 -17
- package/dist/commands/status.js +40 -0
- package/dist/commands/user.js +5 -5
- package/dist/commands/vpc_endpoints.js +50 -50
- package/dist/config.js +7 -7
- package/dist/config_format.js +5 -5
- package/dist/context.js +37 -14
- package/dist/current_branch_fast_path.js +55 -0
- package/dist/dev/env.js +33 -33
- package/dist/dev/functions.js +4 -4
- package/dist/dev/inputs.js +6 -6
- package/dist/dev/runtime.js +25 -25
- package/dist/env.js +14 -14
- package/dist/env_file.js +13 -13
- package/dist/errors.js +68 -5
- package/dist/functions_api.js +10 -10
- package/dist/help.js +15 -15
- package/dist/index.js +110 -107
- package/dist/log.js +2 -2
- package/dist/parameters.gen.js +14 -14
- package/dist/pkg.js +5 -5
- package/dist/psql/cli.js +4 -2
- package/dist/psql/command/cmd_cond.js +61 -61
- package/dist/psql/command/cmd_connect.js +159 -154
- package/dist/psql/command/cmd_copy.js +107 -97
- package/dist/psql/command/cmd_describe.js +368 -363
- package/dist/psql/command/cmd_format.js +276 -263
- package/dist/psql/command/cmd_io.js +269 -263
- package/dist/psql/command/cmd_lo.js +74 -66
- package/dist/psql/command/cmd_meta.js +148 -148
- package/dist/psql/command/cmd_misc.js +17 -17
- package/dist/psql/command/cmd_pipeline.js +142 -135
- package/dist/psql/command/cmd_restrict.js +25 -25
- package/dist/psql/command/cmd_show.js +183 -168
- package/dist/psql/command/dispatch.js +26 -26
- package/dist/psql/command/shared.js +14 -14
- package/dist/psql/complete/filenames.js +16 -16
- package/dist/psql/complete/index.js +4 -4
- package/dist/psql/complete/matcher.js +33 -32
- package/dist/psql/complete/psqlVars.js +173 -173
- package/dist/psql/complete/queries.js +5 -3
- package/dist/psql/complete/rules.js +900 -863
- package/dist/psql/core/common.js +136 -133
- package/dist/psql/core/help.js +343 -343
- package/dist/psql/core/mainloop.js +160 -153
- package/dist/psql/core/prompt.js +126 -123
- package/dist/psql/core/settings.js +111 -111
- package/dist/psql/core/sqlHelp.js +150 -150
- package/dist/psql/core/startup.js +211 -205
- package/dist/psql/core/syncVars.js +14 -14
- package/dist/psql/core/variables.js +24 -24
- package/dist/psql/describe/formatters.js +302 -289
- package/dist/psql/describe/processNamePattern.js +28 -28
- package/dist/psql/describe/queries.js +656 -651
- package/dist/psql/index.js +436 -411
- package/dist/psql/io/history.js +36 -36
- package/dist/psql/io/input.js +15 -15
- package/dist/psql/io/lineEditor/buffer.js +27 -25
- package/dist/psql/io/lineEditor/complete.js +15 -15
- package/dist/psql/io/lineEditor/filename.js +22 -22
- package/dist/psql/io/lineEditor/index.js +65 -62
- package/dist/psql/io/lineEditor/keymap.js +325 -318
- package/dist/psql/io/lineEditor/vt100.js +60 -60
- package/dist/psql/io/pgpass.js +18 -18
- package/dist/psql/io/pgservice.js +14 -14
- package/dist/psql/io/psqlrc.js +46 -46
- package/dist/psql/print/aligned.js +175 -166
- package/dist/psql/print/asciidoc.js +51 -51
- package/dist/psql/print/crosstab.js +34 -31
- package/dist/psql/print/csv.js +25 -22
- package/dist/psql/print/html.js +54 -54
- package/dist/psql/print/json.js +12 -12
- package/dist/psql/print/latex.js +118 -118
- package/dist/psql/print/pager.js +28 -26
- package/dist/psql/print/troff.js +48 -48
- package/dist/psql/print/unaligned.js +15 -14
- package/dist/psql/print/units.js +17 -17
- package/dist/psql/scanner/slash.js +48 -46
- package/dist/psql/scanner/sql.js +88 -84
- package/dist/psql/scanner/stringutils.js +21 -17
- package/dist/psql/types/index.js +7 -7
- package/dist/psql/types/scanner.js +8 -8
- package/dist/psql/wire/connection.js +341 -327
- package/dist/psql/wire/copy.js +7 -7
- package/dist/psql/wire/pipeline.js +26 -24
- package/dist/psql/wire/protocol.js +102 -102
- package/dist/psql/wire/sasl.js +62 -62
- package/dist/psql/wire/tls.js +79 -73
- package/dist/storage_api.js +22 -23
- package/dist/test_utils/fixtures.js +74 -41
- package/dist/test_utils/oauth_server.js +5 -5
- package/dist/utils/api_enums.js +33 -0
- package/dist/utils/branch_notice.js +5 -5
- package/dist/utils/branch_picker.js +26 -26
- package/dist/utils/compute_units.js +4 -4
- package/dist/utils/enrichers.js +28 -16
- package/dist/utils/esbuild.js +28 -28
- package/dist/utils/formats.js +1 -1
- package/dist/utils/middlewares.js +3 -3
- package/dist/utils/package_manager.js +68 -0
- package/dist/utils/point_in_time.js +12 -12
- package/dist/utils/psql.js +30 -30
- package/dist/utils/string.js +2 -2
- package/dist/utils/ui.js +9 -9
- package/dist/utils/zip.js +1 -1
- package/dist/writer.js +17 -17
- package/package.json +10 -12
|
@@ -30,19 +30,19 @@
|
|
|
30
30
|
* delegates to `settings.vars`. Modes that disable substitution (`no-vars`)
|
|
31
31
|
* naturally fall through to the scanner's existing behaviour.
|
|
32
32
|
*/
|
|
33
|
-
import { scanSlashArgs } from
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import { registerMiscCommands } from
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
45
|
-
import { writeErr } from
|
|
33
|
+
import { scanSlashArgs } from "../scanner/slash.js";
|
|
34
|
+
import { registerConnectCommands } from "./cmd_connect.js";
|
|
35
|
+
import { registerCopyCommands } from "./cmd_copy.js";
|
|
36
|
+
import { registerDescribeCommands } from "./cmd_describe.js";
|
|
37
|
+
import { cmdA, cmdC, cmdEncoding, cmdF, cmdH, cmdPset, cmdT, cmdTitleAttr, cmdX, } from "./cmd_format.js";
|
|
38
|
+
import { registerIoCommands } from "./cmd_io.js";
|
|
39
|
+
import { registerLargeObjectCommands } from "./cmd_lo.js";
|
|
40
|
+
import { cmdCd, cmdCopyright, cmdEcho, cmdEdit, cmdErrverbose, cmdGetenv, cmdHelpSQL, cmdPrompt, cmdQecho, cmdQuit, cmdReset, cmdS, cmdSet, cmdSetenv, cmdShell, cmdSlashHelp, cmdTiming, cmdUnset, cmdWarn, } from "./cmd_meta.js";
|
|
41
|
+
import { registerMiscCommands } from "./cmd_misc.js";
|
|
42
|
+
import { registerPipelineCommands } from "./cmd_pipeline.js";
|
|
43
|
+
import { isCommandRestricted, registerRestrictCommands, wrapRestrictedCommands, } from "./cmd_restrict.js";
|
|
44
|
+
import { registerShowCommands } from "./cmd_show.js";
|
|
45
|
+
import { writeErr } from "./shared.js";
|
|
46
46
|
/**
|
|
47
47
|
* Concrete `BackslashRegistry`: a primary-name → spec map plus a parallel
|
|
48
48
|
* alias → primary-name map so lookups stay O(1).
|
|
@@ -103,7 +103,7 @@ export const makeContext = (opts) => {
|
|
|
103
103
|
let cursor = 0;
|
|
104
104
|
const rawArgs = opts.rawArgs;
|
|
105
105
|
const varLookup = (name) => opts.settings.vars.get(name);
|
|
106
|
-
const nextArg = (mode =
|
|
106
|
+
const nextArg = (mode = "normal") => {
|
|
107
107
|
// Find the next non-whitespace byte from the cursor; we use it both to
|
|
108
108
|
// know whether anything remains and as the basis for span tracking.
|
|
109
109
|
let i = cursor;
|
|
@@ -111,7 +111,7 @@ export const makeContext = (opts) => {
|
|
|
111
111
|
i++;
|
|
112
112
|
if (i >= rawArgs.length)
|
|
113
113
|
return null;
|
|
114
|
-
if (mode ===
|
|
114
|
+
if (mode === "whole-line") {
|
|
115
115
|
const tail = rawArgs.slice(i);
|
|
116
116
|
cursor = rawArgs.length;
|
|
117
117
|
return tail;
|
|
@@ -162,7 +162,7 @@ export const makeContext = (opts) => {
|
|
|
162
162
|
* past the original `:name` form regardless of expansion size.
|
|
163
163
|
*/
|
|
164
164
|
const consumedSpan = (tail, mode, varLookup) => {
|
|
165
|
-
if (mode ===
|
|
165
|
+
if (mode === "whole-line")
|
|
166
166
|
return tail.length;
|
|
167
167
|
let i = 0;
|
|
168
168
|
// Skip leading whitespace inside the tail (already trimmed by caller, but
|
|
@@ -170,16 +170,16 @@ const consumedSpan = (tail, mode, varLookup) => {
|
|
|
170
170
|
while (i < tail.length && /[\s]/.test(tail[i]))
|
|
171
171
|
i++;
|
|
172
172
|
// filepipe special: a leading `|` slurps to EOL.
|
|
173
|
-
if (mode ===
|
|
173
|
+
if (mode === "filepipe" && tail[i] === "|")
|
|
174
174
|
return tail.length;
|
|
175
175
|
while (i < tail.length) {
|
|
176
176
|
const c = tail[i];
|
|
177
|
-
if (/[\s]/.test(c) || c ===
|
|
177
|
+
if (/[\s]/.test(c) || c === "\\")
|
|
178
178
|
break;
|
|
179
179
|
if (c === "'") {
|
|
180
180
|
i++;
|
|
181
181
|
while (i < tail.length) {
|
|
182
|
-
if (tail[i] ===
|
|
182
|
+
if (tail[i] === "\\" && i + 1 < tail.length) {
|
|
183
183
|
i += 2;
|
|
184
184
|
continue;
|
|
185
185
|
}
|
|
@@ -203,15 +203,15 @@ const consumedSpan = (tail, mode, varLookup) => {
|
|
|
203
203
|
i++;
|
|
204
204
|
continue;
|
|
205
205
|
}
|
|
206
|
-
if (c ===
|
|
206
|
+
if (c === "`") {
|
|
207
207
|
i++;
|
|
208
|
-
while (i < tail.length && tail[i] !==
|
|
208
|
+
while (i < tail.length && tail[i] !== "`")
|
|
209
209
|
i++;
|
|
210
210
|
if (i < tail.length)
|
|
211
211
|
i++;
|
|
212
212
|
continue;
|
|
213
213
|
}
|
|
214
|
-
if (c ===
|
|
214
|
+
if (c === ":" && mode !== "no-vars") {
|
|
215
215
|
// :"name" / :'name' / :name — advance past the source form. We don't
|
|
216
216
|
// actually call varLookup here; we just measure the lexical span.
|
|
217
217
|
void varLookup;
|
|
@@ -248,14 +248,14 @@ const consumedSpan = (tail, mode, varLookup) => {
|
|
|
248
248
|
export const dispatchBackslash = async (registry, cmdName, ctx) => {
|
|
249
249
|
const spec = registry.lookup(cmdName);
|
|
250
250
|
if (!spec)
|
|
251
|
-
return { status:
|
|
251
|
+
return { status: "error" };
|
|
252
252
|
// PG 18: refuse shell/filesystem-touching commands while restricted.
|
|
253
253
|
// We check against the resolved *primary* name so aliases like
|
|
254
254
|
// `\write` → `w` are caught.
|
|
255
255
|
if (isCommandRestricted(ctx.settings, spec.name)) {
|
|
256
256
|
writeErr(`\\${cmdName}: command is not allowed in restricted mode; ` +
|
|
257
257
|
`use \\unrestrict to leave restricted mode\n`);
|
|
258
|
-
return { status:
|
|
258
|
+
return { status: "error" };
|
|
259
259
|
}
|
|
260
260
|
return spec.run(ctx);
|
|
261
261
|
};
|
|
@@ -310,8 +310,8 @@ export const defaultRegistry = () => {
|
|
|
310
310
|
// shim. (In an active branch this makes `\html` a silent no-op rather than
|
|
311
311
|
// upstream's "invalid command", but no test exercises that path.)
|
|
312
312
|
r.register({
|
|
313
|
-
name:
|
|
314
|
-
run: () => Promise.resolve({ status:
|
|
313
|
+
name: "html",
|
|
314
|
+
run: () => Promise.resolve({ status: "ok" }),
|
|
315
315
|
});
|
|
316
316
|
// Format.
|
|
317
317
|
r.register(cmdA);
|
|
@@ -31,23 +31,23 @@ export const parseBool = (raw) => {
|
|
|
31
31
|
return null;
|
|
32
32
|
const lower = raw.toLowerCase();
|
|
33
33
|
const startsWith = (target) => lower.length <= target.length && target.startsWith(lower);
|
|
34
|
-
if (startsWith(
|
|
34
|
+
if (startsWith("true"))
|
|
35
35
|
return true;
|
|
36
|
-
if (startsWith(
|
|
36
|
+
if (startsWith("false"))
|
|
37
37
|
return false;
|
|
38
|
-
if (startsWith(
|
|
38
|
+
if (startsWith("yes"))
|
|
39
39
|
return true;
|
|
40
|
-
if (startsWith(
|
|
40
|
+
if (startsWith("no"))
|
|
41
41
|
return false;
|
|
42
42
|
if (lower.length >= 2) {
|
|
43
|
-
if (
|
|
43
|
+
if ("on".startsWith(lower))
|
|
44
44
|
return true;
|
|
45
|
-
if (
|
|
45
|
+
if ("off".startsWith(lower))
|
|
46
46
|
return false;
|
|
47
47
|
}
|
|
48
|
-
if (raw ===
|
|
48
|
+
if (raw === "1")
|
|
49
49
|
return true;
|
|
50
|
-
if (raw ===
|
|
50
|
+
if (raw === "0")
|
|
51
51
|
return false;
|
|
52
52
|
return null;
|
|
53
53
|
};
|
|
@@ -60,12 +60,12 @@ export const parseTriple = (raw) => {
|
|
|
60
60
|
// bool prefixes were inverted (review: minor divergences).
|
|
61
61
|
const b = parseBool(raw);
|
|
62
62
|
if (b === true)
|
|
63
|
-
return
|
|
63
|
+
return "on";
|
|
64
64
|
if (b === false)
|
|
65
|
-
return
|
|
66
|
-
if (
|
|
67
|
-
return
|
|
68
|
-
if (
|
|
69
|
-
return
|
|
65
|
+
return "off";
|
|
66
|
+
if ("auto".startsWith(lower))
|
|
67
|
+
return "auto";
|
|
68
|
+
if ("toggle".startsWith(lower))
|
|
69
|
+
return "toggle";
|
|
70
70
|
return null;
|
|
71
71
|
};
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* fires; partial multi-candidate prefixes leave the closing quote off so
|
|
20
20
|
* the user can keep typing.
|
|
21
21
|
*/
|
|
22
|
-
import { readdirSync, statSync } from
|
|
23
|
-
import {
|
|
22
|
+
import { readdirSync, statSync } from "node:fs";
|
|
23
|
+
import { basename, dirname, join } from "node:path";
|
|
24
24
|
/**
|
|
25
25
|
* Enumerate filesystem entries matching the partial path the user typed.
|
|
26
26
|
*
|
|
@@ -45,14 +45,14 @@ export const completeFilenames = (currentWord, quoteCtx, cwd = process.cwd()) =>
|
|
|
45
45
|
}
|
|
46
46
|
// Split into dir + basename prefix. A trailing `/` means "enumerate this
|
|
47
47
|
// dir" and basename prefix is empty.
|
|
48
|
-
const lastSlash = raw.lastIndexOf(
|
|
49
|
-
const dirPart = lastSlash === -1 ?
|
|
48
|
+
const lastSlash = raw.lastIndexOf("/");
|
|
49
|
+
const dirPart = lastSlash === -1 ? "" : raw.slice(0, lastSlash + 1);
|
|
50
50
|
const basePrefix = lastSlash === -1 ? raw : raw.slice(lastSlash + 1);
|
|
51
51
|
// Resolve the directory to scan. Empty `dirPart` → cwd; otherwise it's
|
|
52
52
|
// taken relative to cwd (or absolute if starts with `/`).
|
|
53
|
-
const scanDir = dirPart ===
|
|
53
|
+
const scanDir = dirPart === ""
|
|
54
54
|
? cwd
|
|
55
|
-
: dirPart.startsWith(
|
|
55
|
+
: dirPart.startsWith("/")
|
|
56
56
|
? dirPart
|
|
57
57
|
: join(cwd, dirPart);
|
|
58
58
|
let entries;
|
|
@@ -80,10 +80,10 @@ export const completeFilenames = (currentWord, quoteCtx, cwd = process.cwd()) =>
|
|
|
80
80
|
catch {
|
|
81
81
|
// Broken symlink etc. — treat as regular file.
|
|
82
82
|
}
|
|
83
|
-
const full = dirPart + entry + (isDir ?
|
|
83
|
+
const full = dirPart + entry + (isDir ? "/" : "");
|
|
84
84
|
candidates.push(full);
|
|
85
85
|
}
|
|
86
|
-
if (quoteCtx ===
|
|
86
|
+
if (quoteCtx === "none") {
|
|
87
87
|
// Bare paths. Preserve any opening single quote the user already typed
|
|
88
88
|
// (rare for the no-quote contexts, but harmless to mirror).
|
|
89
89
|
if (hadOpeningSingleQuote) {
|
|
@@ -95,7 +95,7 @@ export const completeFilenames = (currentWord, quoteCtx, cwd = process.cwd()) =>
|
|
|
95
95
|
// line editor's `shouldAppendSpace` checks quote balance — unique
|
|
96
96
|
// candidates close the quote (so `'...'` balances → trailing space
|
|
97
97
|
// fires), multi-candidate common prefixes leave the closing quote off.
|
|
98
|
-
if (candidates.length === 1 && !candidates[0].endsWith(
|
|
98
|
+
if (candidates.length === 1 && !candidates[0].endsWith("/")) {
|
|
99
99
|
// Unique file (not directory): close the quote so the trailing space
|
|
100
100
|
// fires. Opening quote: re-add if user typed it, else add ourselves.
|
|
101
101
|
return ["'" + candidates[0] + "'"];
|
|
@@ -118,18 +118,18 @@ export const isCopyFromOrTo = (prevWords) => {
|
|
|
118
118
|
// Walk from the end backward: the immediate prev word must be FROM or TO,
|
|
119
119
|
// and somewhere earlier must be COPY (case-insensitive).
|
|
120
120
|
const last = prevWords[prevWords.length - 1].toUpperCase();
|
|
121
|
-
if (last !==
|
|
121
|
+
if (last !== "FROM" && last !== "TO")
|
|
122
122
|
return false;
|
|
123
123
|
for (let i = prevWords.length - 2; i >= 0; i--) {
|
|
124
|
-
if (prevWords[i].toUpperCase() ===
|
|
124
|
+
if (prevWords[i].toUpperCase() === "COPY")
|
|
125
125
|
return true;
|
|
126
126
|
// If we walk past the start of statement (e.g. another keyword like
|
|
127
127
|
// SELECT) we abort — only the SQL `COPY` form should match.
|
|
128
|
-
if (prevWords[i].toUpperCase() ===
|
|
129
|
-
prevWords[i].toUpperCase() ===
|
|
130
|
-
prevWords[i].toUpperCase() ===
|
|
131
|
-
prevWords[i].toUpperCase() ===
|
|
132
|
-
prevWords[i].toUpperCase() ===
|
|
128
|
+
if (prevWords[i].toUpperCase() === "SELECT" ||
|
|
129
|
+
prevWords[i].toUpperCase() === "INSERT" ||
|
|
130
|
+
prevWords[i].toUpperCase() === "UPDATE" ||
|
|
131
|
+
prevWords[i].toUpperCase() === "DELETE" ||
|
|
132
|
+
prevWords[i].toUpperCase() === "WITH") {
|
|
133
133
|
return false;
|
|
134
134
|
}
|
|
135
135
|
}
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
* the cursor was sitting on (`currentWord`), which we already compute in
|
|
21
21
|
* `splitForCompletion` during tokenization.
|
|
22
22
|
*/
|
|
23
|
-
import { splitForCompletion } from
|
|
24
|
-
import { findCompletions } from
|
|
23
|
+
import { splitForCompletion } from "./matcher.js";
|
|
24
|
+
import { findCompletions } from "./rules.js";
|
|
25
25
|
/**
|
|
26
26
|
* Build a completer bound to the given settings. The settings reference is
|
|
27
27
|
* captured by closure; we never snapshot, so changes to `settings.db` (via
|
|
@@ -32,7 +32,7 @@ export const psqlCompleter = (ctx) => {
|
|
|
32
32
|
const { prevWords, currentWord, replaceLength } = splitForCompletion(input, cursor);
|
|
33
33
|
const ruleCtx = {
|
|
34
34
|
settings: ctx.settings,
|
|
35
|
-
queryBuf: ctx.getQueryBuf?.() ??
|
|
35
|
+
queryBuf: ctx.getQueryBuf?.() ?? "",
|
|
36
36
|
};
|
|
37
37
|
const { candidates } = await findCompletions(prevWords, currentWord, ruleCtx);
|
|
38
38
|
// De-duplicate while preserving order.
|
|
@@ -97,7 +97,7 @@ const longestCommonPrefix = (candidates, fallback) => {
|
|
|
97
97
|
out.push(first);
|
|
98
98
|
}
|
|
99
99
|
// Common prefix should not be shorter than what's already typed.
|
|
100
|
-
const candidatePrefix = out.join(
|
|
100
|
+
const candidatePrefix = out.join("");
|
|
101
101
|
if (candidatePrefix.length >= fallback.length)
|
|
102
102
|
return candidatePrefix;
|
|
103
103
|
return fallback;
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* needs to do the same word-split-then-match dance.
|
|
26
26
|
*/
|
|
27
27
|
export const MatchAny = null;
|
|
28
|
-
export const MatchAnyExcept = (pattern) =>
|
|
28
|
+
export const MatchAnyExcept = (pattern) => "!" + pattern;
|
|
29
29
|
const cimatch = (s1, s2, n, caseSensitive) => {
|
|
30
30
|
if (s1.length < n || s2.length < n)
|
|
31
31
|
return false;
|
|
@@ -43,7 +43,7 @@ const cimatch = (s1, s2, n, caseSensitive) => {
|
|
|
43
43
|
export const wordMatches = (pattern, word, caseSensitive = false) => {
|
|
44
44
|
if (pattern === null)
|
|
45
45
|
return true;
|
|
46
|
-
if (pattern.startsWith(
|
|
46
|
+
if (pattern.startsWith("!")) {
|
|
47
47
|
return !wordMatches(pattern.slice(1), word, caseSensitive);
|
|
48
48
|
}
|
|
49
49
|
const wordlen = word.length;
|
|
@@ -51,8 +51,8 @@ export const wordMatches = (pattern, word, caseSensitive = false) => {
|
|
|
51
51
|
for (;;) {
|
|
52
52
|
let starIdx = -1;
|
|
53
53
|
let i = 0;
|
|
54
|
-
while (i < cursor.length && cursor[i] !==
|
|
55
|
-
if (cursor[i] ===
|
|
54
|
+
while (i < cursor.length && cursor[i] !== "|") {
|
|
55
|
+
if (cursor[i] === "*")
|
|
56
56
|
starIdx = i;
|
|
57
57
|
i++;
|
|
58
58
|
}
|
|
@@ -66,7 +66,8 @@ export const wordMatches = (pattern, word, caseSensitive = false) => {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
else {
|
|
69
|
-
if (wordlen === i &&
|
|
69
|
+
if (wordlen === i &&
|
|
70
|
+
cimatch(word, cursor, wordlen, caseSensitive)) {
|
|
70
71
|
return true;
|
|
71
72
|
}
|
|
72
73
|
}
|
|
@@ -147,12 +148,12 @@ export const tokenize = (input) => {
|
|
|
147
148
|
while (i < input.length) {
|
|
148
149
|
const ch = input[i];
|
|
149
150
|
// Whitespace.
|
|
150
|
-
if (ch ===
|
|
151
|
+
if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r") {
|
|
151
152
|
i++;
|
|
152
153
|
continue;
|
|
153
154
|
}
|
|
154
155
|
// Backslash command. Capture the entire backslash word as one token.
|
|
155
|
-
if (ch ===
|
|
156
|
+
if (ch === "\\") {
|
|
156
157
|
const start = i;
|
|
157
158
|
i++;
|
|
158
159
|
// Single-char commands like `\!` and `\?` are valid; otherwise read
|
|
@@ -168,7 +169,7 @@ export const tokenize = (input) => {
|
|
|
168
169
|
// that key on `prevWords.length === 1` still fire. The
|
|
169
170
|
// `S` (system-objects) suffix is already a letter, so it's consumed
|
|
170
171
|
// above; only `+` needs explicit handling here.
|
|
171
|
-
while (i < input.length && input[i] ===
|
|
172
|
+
while (i < input.length && input[i] === "+")
|
|
172
173
|
i++;
|
|
173
174
|
}
|
|
174
175
|
out.push({
|
|
@@ -208,7 +209,7 @@ export const tokenize = (input) => {
|
|
|
208
209
|
const start = i;
|
|
209
210
|
i++;
|
|
210
211
|
while (i < input.length) {
|
|
211
|
-
if (input[i] ===
|
|
212
|
+
if (input[i] === "\\" && i + 1 < input.length) {
|
|
212
213
|
i += 2;
|
|
213
214
|
continue;
|
|
214
215
|
}
|
|
@@ -231,12 +232,12 @@ export const tokenize = (input) => {
|
|
|
231
232
|
continue;
|
|
232
233
|
}
|
|
233
234
|
// Punctuation that splits words.
|
|
234
|
-
if (ch ===
|
|
235
|
-
ch ===
|
|
236
|
-
ch ===
|
|
237
|
-
ch ===
|
|
238
|
-
ch ===
|
|
239
|
-
ch ===
|
|
235
|
+
if (ch === "," ||
|
|
236
|
+
ch === ";" ||
|
|
237
|
+
ch === "(" ||
|
|
238
|
+
ch === ")" ||
|
|
239
|
+
ch === "[" ||
|
|
240
|
+
ch === "]") {
|
|
240
241
|
out.push({ text: ch, raw: ch, start: i, end: i + 1 });
|
|
241
242
|
i++;
|
|
242
243
|
continue;
|
|
@@ -246,19 +247,19 @@ export const tokenize = (input) => {
|
|
|
246
247
|
const start = i;
|
|
247
248
|
while (i < input.length) {
|
|
248
249
|
const c = input[i];
|
|
249
|
-
if (c ===
|
|
250
|
-
c ===
|
|
251
|
-
c ===
|
|
252
|
-
c ===
|
|
253
|
-
c ===
|
|
254
|
-
c ===
|
|
255
|
-
c ===
|
|
256
|
-
c ===
|
|
257
|
-
c ===
|
|
258
|
-
c ===
|
|
250
|
+
if (c === " " ||
|
|
251
|
+
c === "\t" ||
|
|
252
|
+
c === "\n" ||
|
|
253
|
+
c === "\r" ||
|
|
254
|
+
c === "," ||
|
|
255
|
+
c === ";" ||
|
|
256
|
+
c === "(" ||
|
|
257
|
+
c === ")" ||
|
|
258
|
+
c === "[" ||
|
|
259
|
+
c === "]" ||
|
|
259
260
|
c === '"' ||
|
|
260
261
|
c === "'" ||
|
|
261
|
-
c ===
|
|
262
|
+
c === "\\") {
|
|
262
263
|
break;
|
|
263
264
|
}
|
|
264
265
|
i++;
|
|
@@ -289,16 +290,16 @@ export const splitForCompletion = (input, cursor) => {
|
|
|
289
290
|
const head = input.slice(0, cursor);
|
|
290
291
|
const tokens = tokenize(head);
|
|
291
292
|
if (tokens.length === 0) {
|
|
292
|
-
return { prevWords: [], currentWord:
|
|
293
|
+
return { prevWords: [], currentWord: "", replaceLength: 0 };
|
|
293
294
|
}
|
|
294
295
|
const last = tokens[tokens.length - 1];
|
|
295
296
|
// The cursor is sitting inside the last token if (a) it ends exactly at
|
|
296
297
|
// cursor AND (b) the last char before cursor isn't whitespace.
|
|
297
298
|
const charBefore = head[head.length - 1];
|
|
298
|
-
const inWhitespace = charBefore ===
|
|
299
|
-
charBefore ===
|
|
300
|
-
charBefore ===
|
|
301
|
-
charBefore ===
|
|
299
|
+
const inWhitespace = charBefore === " " ||
|
|
300
|
+
charBefore === "\t" ||
|
|
301
|
+
charBefore === "\n" ||
|
|
302
|
+
charBefore === "\r";
|
|
302
303
|
if (last.end === head.length && !inWhitespace) {
|
|
303
304
|
return {
|
|
304
305
|
prevWords: tokens.slice(0, -1).map((t) => t.text),
|
|
@@ -308,7 +309,7 @@ export const splitForCompletion = (input, cursor) => {
|
|
|
308
309
|
}
|
|
309
310
|
return {
|
|
310
311
|
prevWords: tokens.map((t) => t.text),
|
|
311
|
-
currentWord:
|
|
312
|
+
currentWord: "",
|
|
312
313
|
replaceLength: 0,
|
|
313
314
|
};
|
|
314
315
|
};
|