neon 2.47.0 → 3.1.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 +54 -0
- package/dist/_shared/auth_selection.js +76 -79
- package/dist/_shared/credentials.js +166 -187
- package/dist/_shared/env-core/env.js +395 -0
- package/dist/_shared/env-core/reuse-secrets.js +179 -0
- package/dist/_shared/paths.js +129 -126
- package/dist/_shared/profiles.js +192 -242
- package/dist/_shared/secure_file.js +36 -38
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/analytics.js +163 -207
- package/dist/api.js +815 -758
- package/dist/auth.js +121 -141
- package/dist/auth_context.js +39 -53
- package/dist/cli.js +4 -7
- package/dist/commands/api.js +220 -250
- package/dist/commands/api_keys.js +251 -314
- package/dist/commands/auth.js +283 -328
- package/dist/commands/bootstrap.js +372 -437
- package/dist/commands/branches.js +304 -455
- package/dist/commands/bucket.js +374 -514
- package/dist/commands/checkout.js +213 -298
- package/dist/commands/config.js +575 -658
- package/dist/commands/connection_string.js +137 -165
- package/dist/commands/data_api.js +238 -260
- package/dist/commands/databases.js +67 -76
- package/dist/commands/deploy.js +31 -25
- package/dist/commands/dev.js +642 -681
- package/dist/commands/diff.js +156 -200
- package/dist/commands/env.js +243 -303
- package/dist/commands/functions.js +275 -341
- package/dist/commands/index.js +70 -65
- package/dist/commands/init.js +84 -119
- package/dist/commands/inspect.js +55 -55
- package/dist/commands/ip_allow.js +88 -120
- package/dist/commands/link.js +874 -1019
- package/dist/commands/logs.js +291 -0
- package/dist/commands/neon_auth.js +725 -933
- package/dist/commands/operations.js +34 -25
- package/dist/commands/orgs.js +28 -18
- package/dist/commands/profile.js +614 -845
- package/dist/commands/projects.js +313 -373
- package/dist/commands/psql.js +60 -58
- package/dist/commands/roles.js +55 -58
- package/dist/commands/schema_diff.js +87 -131
- package/dist/commands/set_context.js +34 -26
- package/dist/commands/snapshots.js +288 -413
- package/dist/commands/status.js +41 -37
- package/dist/commands/user.js +21 -10
- package/dist/commands/vpc_endpoints.js +85 -113
- package/dist/config.js +7 -6
- package/dist/config_format.js +50 -66
- package/dist/config_template.js +128 -157
- package/dist/context.js +183 -235
- package/dist/current_branch_fast_path.js +40 -49
- package/dist/dev/env.js +313 -394
- package/dist/dev/functions.js +54 -64
- package/dist/dev/inputs.js +46 -58
- package/dist/dev/runtime.js +135 -164
- package/dist/dev/websocket.js +766 -959
- package/dist/env.js +27 -33
- package/dist/env_file.js +118 -132
- package/dist/env_services.js +36 -38
- package/dist/errors.js +57 -68
- package/dist/functions_api.js +45 -43
- package/dist/help.js +189 -140
- package/dist/index.js +182 -257
- package/dist/init/agents.js +137 -118
- package/dist/init/auth.js +58 -68
- package/dist/init/bootstrap.js +325 -396
- package/dist/init/build_config.js +4 -2
- package/dist/init/detect_agent.js +56 -101
- package/dist/init/editors.js +35 -52
- package/dist/init/enrich_output.js +51 -66
- package/dist/init/extension.js +134 -171
- package/dist/init/inspect.js +179 -266
- package/dist/init/interactive.js +510 -622
- package/dist/init/neonctl.js +117 -168
- package/dist/init/orchestrate.js +157 -173
- package/dist/init/phases/auth.js +188 -202
- package/dist/init/phases/cleanup.js +23 -23
- package/dist/init/phases/db.js +251 -277
- package/dist/init/phases/getting_started.js +213 -223
- package/dist/init/phases/mcp.js +174 -224
- package/dist/init/phases/migrations.js +247 -248
- package/dist/init/phases/neon_auth.js +114 -133
- package/dist/init/phases/setup.js +546 -703
- package/dist/init/phases/skills.js +75 -86
- package/dist/init/phases/status.js +72 -67
- package/dist/init/resolve_context.js +102 -99
- package/dist/init/route_command.js +91 -98
- package/dist/init/skills.js +174 -218
- package/dist/init/vsix.js +77 -99
- package/dist/log.js +17 -16
- package/dist/neon_services.js +104 -129
- package/dist/parameters.gen.js +481 -471
- package/dist/pkg.js +17 -19
- package/dist/profile_keys.js +44 -47
- package/dist/psql/cli.js +44 -47
- package/dist/psql/command/cmd_cond.js +231 -406
- package/dist/psql/command/cmd_connect.js +557 -764
- package/dist/psql/command/cmd_copy.js +727 -983
- package/dist/psql/command/cmd_describe.js +1499 -1688
- package/dist/psql/command/cmd_format.js +733 -905
- package/dist/psql/command/cmd_io.js +1293 -2082
- package/dist/psql/command/cmd_lo.js +297 -359
- package/dist/psql/command/cmd_meta.js +727 -878
- package/dist/psql/command/cmd_misc.js +138 -172
- package/dist/psql/command/cmd_pipeline.js +547 -1099
- package/dist/psql/command/cmd_restrict.js +119 -155
- package/dist/psql/command/cmd_show.js +529 -688
- package/dist/psql/command/dispatch.js +261 -325
- package/dist/psql/command/inputQueue.js +35 -33
- package/dist/psql/command/shared.js +49 -63
- package/dist/psql/complete/filenames.js +90 -133
- package/dist/psql/complete/index.js +59 -97
- package/dist/psql/complete/matcher.js +236 -300
- package/dist/psql/complete/psqlVars.js +218 -223
- package/dist/psql/complete/queries.js +159 -177
- package/dist/psql/complete/rules.js +1493 -2299
- package/dist/psql/core/common.js +762 -1180
- package/dist/psql/core/help.js +456 -546
- package/dist/psql/core/mainloop.js +692 -1302
- package/dist/psql/core/prompt.js +391 -408
- package/dist/psql/core/settings.js +429 -644
- package/dist/psql/core/sqlHelp.js +480 -554
- package/dist/psql/core/startup.js +626 -815
- package/dist/psql/core/syncVars.js +67 -110
- package/dist/psql/core/variables.js +156 -278
- package/dist/psql/describe/formatters.js +884 -1285
- package/dist/psql/describe/processNamePattern.js +173 -260
- package/dist/psql/describe/queries.js +1368 -2403
- package/dist/psql/describe/versionGate.js +32 -41
- package/dist/psql/index.js +1414 -1927
- package/dist/psql/io/history.js +232 -271
- package/dist/psql/io/input.js +103 -108
- package/dist/psql/io/lineEditor/buffer.js +238 -319
- package/dist/psql/io/lineEditor/complete.js +135 -213
- package/dist/psql/io/lineEditor/filename.js +139 -148
- package/dist/psql/io/lineEditor/index.js +653 -870
- package/dist/psql/io/lineEditor/keymap.js +544 -702
- package/dist/psql/io/lineEditor/vt100.js +294 -341
- package/dist/psql/io/pgpass.js +158 -187
- package/dist/psql/io/pgservice.js +146 -183
- package/dist/psql/io/psqlrc.js +328 -403
- package/dist/psql/print/aligned.js +1020 -1683
- package/dist/psql/print/asciidoc.js +180 -214
- package/dist/psql/print/crosstab.js +281 -442
- package/dist/psql/print/csv.js +48 -70
- package/dist/psql/print/html.js +195 -226
- package/dist/psql/print/json.js +75 -88
- package/dist/psql/print/latex.js +291 -364
- package/dist/psql/print/pager.js +171 -242
- package/dist/psql/print/troff.js +194 -226
- package/dist/psql/print/unaligned.js +69 -95
- package/dist/psql/print/units.js +167 -169
- package/dist/psql/scanner/slash.js +428 -483
- package/dist/psql/scanner/sql.js +445 -889
- package/dist/psql/scanner/stringutils.js +309 -379
- package/dist/psql/types/index.js +2 -7
- package/dist/psql/types/scanner.js +25 -22
- package/dist/psql/wire/connection.js +2042 -2803
- package/dist/psql/wire/copy.js +84 -100
- package/dist/psql/wire/notify.js +39 -59
- package/dist/psql/wire/pipeline.js +305 -518
- package/dist/psql/wire/protocol.js +349 -417
- package/dist/psql/wire/sasl.js +180 -265
- package/dist/psql/wire/tls.js +400 -561
- package/dist/storage_api.js +115 -129
- package/dist/test_utils/fixtures.js +94 -113
- package/dist/test_utils/oauth_server.js +10 -7
- package/dist/test_utils/project_dir.js +33 -0
- package/dist/utils/ai_gateway_notice.js +131 -162
- package/dist/utils/api_enums.js +21 -28
- package/dist/utils/auth.js +10 -4
- package/dist/utils/branch_notice.js +20 -19
- package/dist/utils/branch_picker.js +83 -89
- package/dist/utils/cli_name.js +15 -12
- package/dist/utils/compute_units.js +20 -27
- package/dist/utils/config_diff.js +127 -158
- package/dist/utils/enrichers.js +95 -148
- package/dist/utils/esbuild.js +133 -147
- package/dist/utils/flags.js +35 -47
- package/dist/utils/formats.js +8 -15
- package/dist/utils/git_diff.js +69 -80
- package/dist/utils/inspect_db.js +101 -143
- package/dist/utils/inspect_queries.js +179 -142
- package/dist/utils/middlewares.js +37 -44
- package/dist/utils/openapi.js +87 -99
- package/dist/utils/package_manager.js +312 -110
- package/dist/utils/point_in_time.js +49 -53
- package/dist/utils/psql.js +89 -106
- package/dist/utils/service_picker.js +55 -58
- package/dist/utils/string.js +5 -5
- package/dist/utils/ui.js +38 -55
- package/dist/utils/write_sync.js +26 -35
- package/dist/utils/zip.js +4 -3
- package/dist/writer.js +67 -87
- package/package.json +9 -7
|
@@ -1,948 +1,776 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Formatting backslash commands.
|
|
3
|
-
*
|
|
4
|
-
* TypeScript port of the `exec_command_a/C/f/H/t/T/x/pset/encoding`
|
|
5
|
-
* implementations in upstream PostgreSQL's `src/bin/psql/command.c` and
|
|
6
|
-
* their backing `do_pset()` / `printPsetInfo()` helpers in the same file.
|
|
7
|
-
*
|
|
8
|
-
* All commands mutate `settings.popt.topt` (a {@link PrintTableOpts}) in
|
|
9
|
-
* place. Several are thin wrappers over `\pset <option>` — for instance
|
|
10
|
-
* `\a` is equivalent to `\pset format aligned|unaligned`. We keep the
|
|
11
|
-
* separate exports so the registry can advertise them under their public
|
|
12
|
-
* names without aliasing oddities.
|
|
13
|
-
*
|
|
14
|
-
* Encoding & connection coupling: `\encoding NAME` propagates the client
|
|
15
|
-
* encoding to the live connection via {@link Connection.setClientEncoding}
|
|
16
|
-
* (libpq's `PQsetClientEncoding`). Following upstream `do_encoding`, we
|
|
17
|
-
* validate the name client-side first — an unrecognised name prints
|
|
18
|
-
* `invalid encoding name "<name>"` and leaves the encoding unchanged,
|
|
19
|
-
* never touching the connection.
|
|
20
|
-
*
|
|
21
|
-
* Error format: upstream uses `\\<cmd>: <message>` for diagnostics. We
|
|
22
|
-
* mirror that exactly, writing to stderr and returning
|
|
23
|
-
* `{ status: 'error' }`.
|
|
24
|
-
*/
|
|
25
|
-
import { scanSlashArgs } from "../scanner/slash.js";
|
|
26
1
|
import { parseBool, parseTriple, writeErr, writeOut } from "./shared.js";
|
|
2
|
+
import { scanSlashArgs } from "../scanner/slash.js";
|
|
3
|
+
//#region src/psql/command/cmd_format.ts
|
|
4
|
+
/**
|
|
5
|
+
* Formatting backslash commands.
|
|
6
|
+
*
|
|
7
|
+
* TypeScript port of the `exec_command_a/C/f/H/t/T/x/pset/encoding`
|
|
8
|
+
* implementations in upstream PostgreSQL's `src/bin/psql/command.c` and
|
|
9
|
+
* their backing `do_pset()` / `printPsetInfo()` helpers in the same file.
|
|
10
|
+
*
|
|
11
|
+
* All commands mutate `settings.popt.topt` (a {@link PrintTableOpts}) in
|
|
12
|
+
* place. Several are thin wrappers over `\pset <option>` — for instance
|
|
13
|
+
* `\a` is equivalent to `\pset format aligned|unaligned`. We keep the
|
|
14
|
+
* separate exports so the registry can advertise them under their public
|
|
15
|
+
* names without aliasing oddities.
|
|
16
|
+
*
|
|
17
|
+
* Encoding & connection coupling: `\encoding NAME` propagates the client
|
|
18
|
+
* encoding to the live connection via {@link Connection.setClientEncoding}
|
|
19
|
+
* (libpq's `PQsetClientEncoding`). Following upstream `do_encoding`, we
|
|
20
|
+
* validate the name client-side first — an unrecognised name prints
|
|
21
|
+
* `invalid encoding name "<name>"` and leaves the encoding unchanged,
|
|
22
|
+
* never touching the connection.
|
|
23
|
+
*
|
|
24
|
+
* Error format: upstream uses `\\<cmd>: <message>` for diagnostics. We
|
|
25
|
+
* mirror that exactly, writing to stderr and returning
|
|
26
|
+
* `{ status: 'error' }`.
|
|
27
|
+
*/
|
|
27
28
|
/** Recognised output-format names accepted by `\pset format`. */
|
|
28
29
|
const OUTPUT_FORMATS = [
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
"aligned",
|
|
31
|
+
"unaligned",
|
|
32
|
+
"wrapped",
|
|
33
|
+
"html",
|
|
34
|
+
"asciidoc",
|
|
35
|
+
"latex",
|
|
36
|
+
"latex-longtable",
|
|
37
|
+
"troff-ms",
|
|
38
|
+
"csv",
|
|
39
|
+
"json"
|
|
39
40
|
];
|
|
40
41
|
/** Convert OutputFormat to its human-readable display string. */
|
|
41
42
|
const formatName = (f) => f;
|
|
42
43
|
/**
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
* Stringify a triple-state for status lines (`\x`, `\pset expanded`).
|
|
45
|
+
* Matches upstream psql phrasing:
|
|
46
|
+
* on → "Expanded display is on."
|
|
47
|
+
* off → "Expanded display is off."
|
|
48
|
+
* auto → "Expanded display is used automatically."
|
|
49
|
+
*/
|
|
49
50
|
const tripleLabel = (value) => value === "auto" ? "used automatically" : value;
|
|
50
51
|
/**
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return Promise.resolve({ status: "ok" });
|
|
68
|
-
},
|
|
52
|
+
* `\a` — toggle aligned/unaligned.
|
|
53
|
+
*
|
|
54
|
+
* Upstream `exec_command_a` flips the format via `do_pset("format", …)`
|
|
55
|
+
* and, when not quiet, prints `Output format is <aligned|unaligned>.` —
|
|
56
|
+
* the same `printPsetInfo("format")` line `\pset format` emits. We mirror
|
|
57
|
+
* that here, gating the status write on QUIET like the sibling toggles.
|
|
58
|
+
*/
|
|
59
|
+
const cmdA = {
|
|
60
|
+
name: "a",
|
|
61
|
+
helpKey: "a",
|
|
62
|
+
run: (ctx) => {
|
|
63
|
+
const topt = ctx.settings.popt.topt;
|
|
64
|
+
topt.format = topt.format === "aligned" ? "unaligned" : "aligned";
|
|
65
|
+
if (!ctx.settings.quiet) writeOut(`Output format is ${formatName(topt.format)}.\n`);
|
|
66
|
+
return Promise.resolve({ status: "ok" });
|
|
67
|
+
}
|
|
69
68
|
};
|
|
70
69
|
/**
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
70
|
+
* `\C [title]` — set or clear `topt.title`. No arg clears, any arg sets to
|
|
71
|
+
* that string verbatim. Equivalent to `\pset title [value]`; upstream
|
|
72
|
+
* `exec_command_C` dispatches via `do_pset("title", value, …)` so the
|
|
73
|
+
* status line (`Title is "…".` / `Title is unset.`) is emitted by
|
|
74
|
+
* `printPsetInfo`.
|
|
75
|
+
*/
|
|
76
|
+
const cmdC = {
|
|
77
|
+
name: "C",
|
|
78
|
+
helpKey: "C",
|
|
79
|
+
run: (ctx) => {
|
|
80
|
+
const arg = ctx.nextArg("normal");
|
|
81
|
+
return Promise.resolve(applyPset(ctx.settings.popt.topt, "title", arg, ctx.cmdName, ctx.settings.quiet));
|
|
82
|
+
}
|
|
84
83
|
};
|
|
85
84
|
/**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
writeOut(`Field separator is "${arg}".\n`);
|
|
103
|
-
}
|
|
104
|
-
return Promise.resolve({ status: "ok" });
|
|
105
|
-
},
|
|
85
|
+
* `\f [sep]` — set or show the unaligned field separator. With no arg, we
|
|
86
|
+
* print the current value (upstream prints `Field separator is "%s".`).
|
|
87
|
+
*/
|
|
88
|
+
const cmdF = {
|
|
89
|
+
name: "f",
|
|
90
|
+
helpKey: "f",
|
|
91
|
+
run: (ctx) => {
|
|
92
|
+
const arg = ctx.nextArg("normal");
|
|
93
|
+
if (arg === null) {
|
|
94
|
+
writeOut(`Field separator is "${ctx.settings.popt.topt.fieldSep}".\n`);
|
|
95
|
+
return Promise.resolve({ status: "ok" });
|
|
96
|
+
}
|
|
97
|
+
ctx.settings.popt.topt.fieldSep = arg;
|
|
98
|
+
if (!ctx.settings.quiet) writeOut(`Field separator is "${arg}".\n`);
|
|
99
|
+
return Promise.resolve({ status: "ok" });
|
|
100
|
+
}
|
|
106
101
|
};
|
|
107
102
|
/**
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
103
|
+
* `\H` — toggle html on/off. If currently `html`, flip back to `aligned`;
|
|
104
|
+
* otherwise flip to `html` (upstream remembers the prior format only
|
|
105
|
+
* loosely — we always restore `aligned` to match the documented behaviour).
|
|
106
|
+
*/
|
|
107
|
+
const cmdH = {
|
|
108
|
+
name: "H",
|
|
109
|
+
helpKey: "H",
|
|
110
|
+
run: (ctx) => {
|
|
111
|
+
const topt = ctx.settings.popt.topt;
|
|
112
|
+
topt.format = topt.format === "html" ? "aligned" : "html";
|
|
113
|
+
return Promise.resolve({ status: "ok" });
|
|
114
|
+
}
|
|
120
115
|
};
|
|
121
116
|
/**
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
117
|
+
* `\t [on|off|toggle]` — tuples-only. No arg → toggle.
|
|
118
|
+
*
|
|
119
|
+
* Equivalent to `\pset tuples_only [value]`; upstream `exec_command_t`
|
|
120
|
+
* dispatches via `do_pset("tuples_only", opt, …)`. The
|
|
121
|
+
* `printPsetInfo("tuples_only")` confirmation line is only emitted when
|
|
122
|
+
* `opt` is NULL (the toggle path) — when a value is supplied,
|
|
123
|
+
* `do_pset` returns early via `ParseVariableBool` and skips
|
|
124
|
+
* `printPsetInfo`, so the status line is suppressed.
|
|
125
|
+
*/
|
|
126
|
+
const cmdT = {
|
|
127
|
+
name: "t",
|
|
128
|
+
helpKey: "t",
|
|
129
|
+
run: (ctx) => {
|
|
130
|
+
const arg = ctx.nextArg("normal");
|
|
131
|
+
return Promise.resolve(applyPset(ctx.settings.popt.topt, "tuples_only", arg, ctx.cmdName, ctx.settings.quiet));
|
|
132
|
+
}
|
|
138
133
|
};
|
|
139
134
|
/**
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
135
|
+
* `\T [attr]` — set HTML table attributes. No arg clears. Equivalent to
|
|
136
|
+
* `\pset tableattr [value]`; upstream `exec_command_T` dispatches via
|
|
137
|
+
* `do_pset("tableattr", value, …)` so the status line
|
|
138
|
+
* (`Table attributes are "…".` / `Table attributes unset.`) is emitted
|
|
139
|
+
* by `printPsetInfo`.
|
|
140
|
+
*/
|
|
141
|
+
const cmdTitleAttr = {
|
|
142
|
+
name: "T",
|
|
143
|
+
helpKey: "T",
|
|
144
|
+
run: (ctx) => {
|
|
145
|
+
const arg = ctx.nextArg("normal");
|
|
146
|
+
return Promise.resolve(applyPset(ctx.settings.popt.topt, "tableattr", arg, ctx.cmdName, ctx.settings.quiet));
|
|
147
|
+
}
|
|
153
148
|
};
|
|
154
149
|
/** `\x [on|off|auto|toggle]` — expanded output. No arg → toggle. */
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
writeOut(`Expanded display is ${tripleLabel(next)}.\n`);
|
|
180
|
-
return Promise.resolve({ status: "ok" });
|
|
181
|
-
},
|
|
150
|
+
const cmdX = {
|
|
151
|
+
name: "x",
|
|
152
|
+
helpKey: "x",
|
|
153
|
+
run: (ctx) => {
|
|
154
|
+
const arg = ctx.nextArg("normal");
|
|
155
|
+
const topt = ctx.settings.popt.topt;
|
|
156
|
+
let next;
|
|
157
|
+
if (arg === null) next = topt.expanded === "on" ? "off" : "on";
|
|
158
|
+
else {
|
|
159
|
+
const parsed = parseTriple(arg);
|
|
160
|
+
if (parsed === null) {
|
|
161
|
+
writeErr(`\\${ctx.cmdName}: unrecognized value "${arg}": Boolean expected\n`);
|
|
162
|
+
return Promise.resolve({
|
|
163
|
+
status: "error",
|
|
164
|
+
errorWritten: true
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (parsed === "toggle") next = topt.expanded === "on" ? "off" : "on";
|
|
168
|
+
else next = parsed;
|
|
169
|
+
}
|
|
170
|
+
topt.expanded = next;
|
|
171
|
+
writeOut(`Expanded display is ${tripleLabel(next)}.\n`);
|
|
172
|
+
return Promise.resolve({ status: "ok" });
|
|
173
|
+
}
|
|
182
174
|
};
|
|
183
175
|
/**
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const NORMALISED_ENCODINGS = new Set([
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
"abc", // → WIN1258
|
|
176
|
+
* Canonical PostgreSQL encoding names, normalised the way upstream
|
|
177
|
+
* `pg_char_to_encoding` (`src/common/encnames.c`) normalises before
|
|
178
|
+
* comparing: lowercased with every `-` and `_` removed. The set covers the
|
|
179
|
+
* full `pg_enc2name` table — both the backend-usable encodings and the
|
|
180
|
+
* client-only ones (SJIS, BIG5, …) — because upstream `\encoding` validates
|
|
181
|
+
* via `pg_char_to_encoding`, which accepts any recognised name and lets the
|
|
182
|
+
* server reject genuinely unusable ones.
|
|
183
|
+
*/
|
|
184
|
+
const NORMALISED_ENCODINGS = /* @__PURE__ */ new Set([
|
|
185
|
+
"sqlascii",
|
|
186
|
+
"eucjp",
|
|
187
|
+
"euccn",
|
|
188
|
+
"euckr",
|
|
189
|
+
"euctw",
|
|
190
|
+
"eucjis2004",
|
|
191
|
+
"utf8",
|
|
192
|
+
"muleinternal",
|
|
193
|
+
"latin1",
|
|
194
|
+
"latin2",
|
|
195
|
+
"latin3",
|
|
196
|
+
"latin4",
|
|
197
|
+
"latin5",
|
|
198
|
+
"latin6",
|
|
199
|
+
"latin7",
|
|
200
|
+
"latin8",
|
|
201
|
+
"latin9",
|
|
202
|
+
"latin10",
|
|
203
|
+
"win1256",
|
|
204
|
+
"win1258",
|
|
205
|
+
"win866",
|
|
206
|
+
"win874",
|
|
207
|
+
"koi8r",
|
|
208
|
+
"win1251",
|
|
209
|
+
"win1252",
|
|
210
|
+
"iso88595",
|
|
211
|
+
"iso88596",
|
|
212
|
+
"iso88597",
|
|
213
|
+
"iso88598",
|
|
214
|
+
"win1250",
|
|
215
|
+
"win1253",
|
|
216
|
+
"win1254",
|
|
217
|
+
"win1255",
|
|
218
|
+
"win1257",
|
|
219
|
+
"koi8u",
|
|
220
|
+
"sjis",
|
|
221
|
+
"big5",
|
|
222
|
+
"gbk",
|
|
223
|
+
"uhc",
|
|
224
|
+
"gb18030",
|
|
225
|
+
"johab",
|
|
226
|
+
"shiftjis2004",
|
|
227
|
+
"unicode",
|
|
228
|
+
"mskanji",
|
|
229
|
+
"shiftjis",
|
|
230
|
+
"windows949",
|
|
231
|
+
"windows950",
|
|
232
|
+
"windows936",
|
|
233
|
+
"tcvn",
|
|
234
|
+
"tcvn5712",
|
|
235
|
+
"vscii",
|
|
236
|
+
"alt",
|
|
237
|
+
"win",
|
|
238
|
+
"koi8",
|
|
239
|
+
"abc"
|
|
249
240
|
]);
|
|
250
241
|
/**
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
242
|
+
* Mirror upstream `pg_char_to_encoding`'s name normalisation: drop every
|
|
243
|
+
* `-` and `_`, lowercase, and look the result up in the canonical set.
|
|
244
|
+
* Used to reproduce `\encoding`'s `invalid encoding name` guard without a
|
|
245
|
+
* server round-trip.
|
|
246
|
+
*/
|
|
256
247
|
const isValidEncodingName = (name) => NORMALISED_ENCODINGS.has(name.replace(/[-_]/g, "").toLowerCase());
|
|
257
248
|
/**
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
ctx.settings.popt.topt.encoding = arg;
|
|
301
|
-
return { status: "ok" };
|
|
302
|
-
},
|
|
249
|
+
* `\encoding [name]` — show or set the client encoding.
|
|
250
|
+
*
|
|
251
|
+
* No arg: print the current `topt.encoding`. With an arg: validate the name
|
|
252
|
+
* the way upstream `do_encoding` does (`pg_char_to_encoding` >= 0) — an
|
|
253
|
+
* unrecognised name prints `\encoding: invalid encoding name "<name>"`,
|
|
254
|
+
* leaves `topt.encoding` untouched, and never calls the connection.
|
|
255
|
+
* Otherwise push it to the live connection via
|
|
256
|
+
* {@link Connection.setClientEncoding} (libpq `PQsetClientEncoding`) and
|
|
257
|
+
* mirror it into `topt.encoding` so prompts/printer see the new value.
|
|
258
|
+
*/
|
|
259
|
+
const cmdEncoding = {
|
|
260
|
+
name: "encoding",
|
|
261
|
+
helpKey: "encoding",
|
|
262
|
+
run: async (ctx) => {
|
|
263
|
+
const arg = ctx.nextArg("normal");
|
|
264
|
+
if (arg === null) {
|
|
265
|
+
writeOut(`${ctx.settings.popt.topt.encoding}\n`);
|
|
266
|
+
return { status: "ok" };
|
|
267
|
+
}
|
|
268
|
+
if (!isValidEncodingName(arg)) {
|
|
269
|
+
writeErr(`\\${ctx.cmdName}: invalid encoding name "${arg}"\n`);
|
|
270
|
+
return {
|
|
271
|
+
status: "error",
|
|
272
|
+
errorWritten: true
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
const { db } = ctx.settings;
|
|
276
|
+
if (db?.setClientEncoding) try {
|
|
277
|
+
await db.setClientEncoding(arg);
|
|
278
|
+
} catch (err) {
|
|
279
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
280
|
+
writeErr(`\\${ctx.cmdName}: ${msg}\n`);
|
|
281
|
+
return {
|
|
282
|
+
status: "error",
|
|
283
|
+
errorWritten: true
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
ctx.settings.popt.topt.encoding = arg;
|
|
287
|
+
return { status: "ok" };
|
|
288
|
+
}
|
|
303
289
|
};
|
|
304
290
|
/**
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
const lower = value.toLowerCase();
|
|
625
|
-
// Preserve 'old-ascii' verbatim so the bulk-view (`\pset` with no
|
|
626
|
-
// args) round-trips and the printer can pick the matching glyph
|
|
627
|
-
// table. Upstream `do_pset("linestyle", "old-ascii", …)` flips
|
|
628
|
-
// `popt.topt.line_style = &pg_asciiformat_old`; we carry the same
|
|
629
|
-
// three-way distinction on `unicodeBorderLineStyle`.
|
|
630
|
-
if (lower === "ascii" ||
|
|
631
|
-
lower === "old-ascii" ||
|
|
632
|
-
lower === "unicode") {
|
|
633
|
-
const ls = lower;
|
|
634
|
-
topt.unicodeBorderLineStyle = ls;
|
|
635
|
-
topt.unicodeColumnLineStyle = ls;
|
|
636
|
-
topt.unicodeHeaderLineStyle = ls;
|
|
637
|
-
writeOutMaybe(`Line style is ${ls}.\n`);
|
|
638
|
-
return { status: "ok" };
|
|
639
|
-
}
|
|
640
|
-
writeErr(`\\pset: allowed line styles are ascii, old-ascii, unicode\n`);
|
|
641
|
-
return { status: "error", errorWritten: true };
|
|
642
|
-
}
|
|
643
|
-
case "columns": {
|
|
644
|
-
if (value !== null) {
|
|
645
|
-
const n = parseInt(value, 10);
|
|
646
|
-
if (!Number.isFinite(n) || n < 0) {
|
|
647
|
-
writeErr(`\\pset: invalid columns "${value}"\n`);
|
|
648
|
-
return { status: "error", errorWritten: true };
|
|
649
|
-
}
|
|
650
|
-
topt.columns = n;
|
|
651
|
-
}
|
|
652
|
-
// Upstream `printPsetInfo` reports `0` as the special "unset"
|
|
653
|
-
// sentinel — see `command.c:5433`.
|
|
654
|
-
if (topt.columns === 0) {
|
|
655
|
-
writeOutMaybe("Target width is unset.\n");
|
|
656
|
-
}
|
|
657
|
-
else {
|
|
658
|
-
writeOutMaybe(`Target width is ${topt.columns}.\n`);
|
|
659
|
-
}
|
|
660
|
-
return { status: "ok" };
|
|
661
|
-
}
|
|
662
|
-
case "xheader_width": {
|
|
663
|
-
if (value !== null) {
|
|
664
|
-
const lower = value.toLowerCase();
|
|
665
|
-
if (lower === "full" ||
|
|
666
|
-
lower === "column" ||
|
|
667
|
-
lower === "page") {
|
|
668
|
-
topt.xheaderWidth = lower;
|
|
669
|
-
}
|
|
670
|
-
else {
|
|
671
|
-
const n = parseInt(value, 10);
|
|
672
|
-
if (!Number.isFinite(n) ||
|
|
673
|
-
n <= 0 ||
|
|
674
|
-
!/^[+]?\d+$/.test(value.trim())) {
|
|
675
|
-
writeErr(`\\pset: allowed xheader_width values are "full" (default), "column", "page", or a number specifying the exact width\n`);
|
|
676
|
-
return { status: "error", errorWritten: true };
|
|
677
|
-
}
|
|
678
|
-
topt.xheaderWidth = n;
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
// Upstream `printPsetInfo` quotes the three named widths
|
|
682
|
-
// ("full" / "column" / "page") but renders the numeric form
|
|
683
|
-
// unquoted as `Expanded header width is 33.`.
|
|
684
|
-
const current = topt.xheaderWidth ?? "full";
|
|
685
|
-
if (typeof current === "number") {
|
|
686
|
-
writeOutMaybe(`Expanded header width is ${current}.\n`);
|
|
687
|
-
}
|
|
688
|
-
else {
|
|
689
|
-
writeOutMaybe(`Expanded header width is "${current}".\n`);
|
|
690
|
-
}
|
|
691
|
-
return { status: "ok" };
|
|
692
|
-
}
|
|
693
|
-
case "unicode_border_linestyle":
|
|
694
|
-
case "unicode_column_linestyle":
|
|
695
|
-
case "unicode_header_linestyle": {
|
|
696
|
-
// Upstream `printPsetInfo` renders these as
|
|
697
|
-
// `Unicode border line style is "single".` etc. — note the space
|
|
698
|
-
// between "line" and "style" in the message (the option name
|
|
699
|
-
// itself is one token, `linestyle`).
|
|
700
|
-
const which = opt === "unicode_border_linestyle"
|
|
701
|
-
? "border"
|
|
702
|
-
: opt === "unicode_column_linestyle"
|
|
703
|
-
? "column"
|
|
704
|
-
: "header";
|
|
705
|
-
if (value !== null) {
|
|
706
|
-
const lower = value.toLowerCase();
|
|
707
|
-
if (lower !== "single" && lower !== "double") {
|
|
708
|
-
writeErr(`\\pset: ${opt} must be single or double\n`);
|
|
709
|
-
return { status: "error", errorWritten: true };
|
|
710
|
-
}
|
|
711
|
-
const style = lower;
|
|
712
|
-
if (opt === "unicode_border_linestyle") {
|
|
713
|
-
topt.unicodeBorderStyle = style;
|
|
714
|
-
}
|
|
715
|
-
else if (opt === "unicode_column_linestyle") {
|
|
716
|
-
topt.unicodeColumnStyle = style;
|
|
717
|
-
}
|
|
718
|
-
else {
|
|
719
|
-
topt.unicodeHeaderStyle = style;
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
const current = opt === "unicode_border_linestyle"
|
|
723
|
-
? (topt.unicodeBorderStyle ?? "single")
|
|
724
|
-
: opt === "unicode_column_linestyle"
|
|
725
|
-
? (topt.unicodeColumnStyle ?? "single")
|
|
726
|
-
: (topt.unicodeHeaderStyle ?? "single");
|
|
727
|
-
writeOutMaybe(`Unicode ${which} line style is "${current}".\n`);
|
|
728
|
-
return { status: "ok" };
|
|
729
|
-
}
|
|
730
|
-
default: {
|
|
731
|
-
writeErr(`\\pset: unknown option "${option}"\n`);
|
|
732
|
-
return { status: "error", errorWritten: true };
|
|
733
|
-
}
|
|
734
|
-
}
|
|
291
|
+
* The heart of `\pset`. Given a parsed `option` and optional `value`,
|
|
292
|
+
* mutates `topt` in place and emits the upstream-style status line.
|
|
293
|
+
*
|
|
294
|
+
* Returns `{ status: 'error' }` and writes an error if the value is
|
|
295
|
+
* unrecognised; `{ status: 'ok' }` otherwise.
|
|
296
|
+
*
|
|
297
|
+
* Wording reference: every status line is byte-matched against the
|
|
298
|
+
* `printPsetInfo` table in upstream `src/bin/psql/command.c`. Notable
|
|
299
|
+
* subtleties:
|
|
300
|
+
*
|
|
301
|
+
* - `tuples_only`, `footer`, and `numericlocale` are silenced when a
|
|
302
|
+
* value is supplied — upstream `do_pset` returns directly out of
|
|
303
|
+
* `ParseVariableBool`, never reaching `printPsetInfo`. The toggle
|
|
304
|
+
* paths still print.
|
|
305
|
+
* - `recordsep` renders the literal `\n` as the `<newline>` sentinel.
|
|
306
|
+
* - `columns` reports `0` as `Target width is unset.`.
|
|
307
|
+
* - `unicode_*_linestyle` uses the multi-word "line style" phrasing
|
|
308
|
+
* even though the option name itself is a single token.
|
|
309
|
+
* - `pager_min_lines` pluralizes via `ngettext` — singular at 1, plural
|
|
310
|
+
* everywhere else (including 0).
|
|
311
|
+
* - `csv_fieldsep` reports as `Field separator for CSV is "…".`.
|
|
312
|
+
* - `xheader_width` quotes the named enum values (`"full"`/`"column"`/
|
|
313
|
+
* `"page"`) and prints the numeric form unquoted.
|
|
314
|
+
*/
|
|
315
|
+
const applyPset = (topt, option, value, cmdName, silent = false) => {
|
|
316
|
+
const writeOutMaybe = silent ? () => void 0 : writeOut;
|
|
317
|
+
const opt = option.toLowerCase();
|
|
318
|
+
switch (opt) {
|
|
319
|
+
case "format": {
|
|
320
|
+
if (value === null) {
|
|
321
|
+
writeOutMaybe(`Output format is ${formatName(topt.format)}.\n`);
|
|
322
|
+
return { status: "ok" };
|
|
323
|
+
}
|
|
324
|
+
const v = value.toLowerCase();
|
|
325
|
+
if ("aligned".startsWith(v) && "asciidoc".startsWith(v)) {
|
|
326
|
+
writeErr(`\\pset: ambiguous abbreviation "${value}" matches both "aligned" and "asciidoc"\n`);
|
|
327
|
+
return {
|
|
328
|
+
status: "error",
|
|
329
|
+
errorWritten: true
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
const match = OUTPUT_FORMATS.find((f) => f.startsWith(v));
|
|
333
|
+
if (!match) {
|
|
334
|
+
writeErr(`\\pset: allowed formats are aligned, asciidoc, csv, html, json, latex, latex-longtable, troff-ms, unaligned, wrapped\n`);
|
|
335
|
+
return {
|
|
336
|
+
status: "error",
|
|
337
|
+
errorWritten: true
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
topt.format = match;
|
|
341
|
+
writeOutMaybe(`Output format is ${formatName(match)}.\n`);
|
|
342
|
+
return { status: "ok" };
|
|
343
|
+
}
|
|
344
|
+
case "border": {
|
|
345
|
+
if (value === null) {
|
|
346
|
+
writeOutMaybe(`Border style is ${topt.border}.\n`);
|
|
347
|
+
return { status: "ok" };
|
|
348
|
+
}
|
|
349
|
+
const n = parseInt(value, 10);
|
|
350
|
+
if (!Number.isFinite(n) || n < 0 || n > 3) {
|
|
351
|
+
writeErr(`\\pset: invalid border "${value}"\n`);
|
|
352
|
+
return {
|
|
353
|
+
status: "error",
|
|
354
|
+
errorWritten: true
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
topt.border = n;
|
|
358
|
+
writeOutMaybe(`Border style is ${topt.border}.\n`);
|
|
359
|
+
return { status: "ok" };
|
|
360
|
+
}
|
|
361
|
+
case "expanded":
|
|
362
|
+
case "x":
|
|
363
|
+
if (value === null) topt.expanded = topt.expanded === "on" ? "off" : "on";
|
|
364
|
+
else {
|
|
365
|
+
const p = parseTriple(value);
|
|
366
|
+
if (p === null) {
|
|
367
|
+
writeErr(`\\pset: unrecognized value "${value}" for "expanded": Boolean expected\n`);
|
|
368
|
+
return {
|
|
369
|
+
status: "error",
|
|
370
|
+
errorWritten: true
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
topt.expanded = p === "toggle" ? topt.expanded === "on" ? "off" : "on" : p;
|
|
374
|
+
}
|
|
375
|
+
writeOutMaybe(`Expanded display is ${tripleLabel(topt.expanded)}.\n`);
|
|
376
|
+
return { status: "ok" };
|
|
377
|
+
case "fieldsep":
|
|
378
|
+
if (value === null) {
|
|
379
|
+
writeOutMaybe(`Field separator is "${topt.fieldSep}".\n`);
|
|
380
|
+
return { status: "ok" };
|
|
381
|
+
}
|
|
382
|
+
topt.fieldSep = value;
|
|
383
|
+
writeOutMaybe(`Field separator is "${topt.fieldSep}".\n`);
|
|
384
|
+
return { status: "ok" };
|
|
385
|
+
case "fieldsep_zero":
|
|
386
|
+
topt.fieldSep = "\0";
|
|
387
|
+
writeOutMaybe("Field separator is zero byte.\n");
|
|
388
|
+
return { status: "ok" };
|
|
389
|
+
case "footer":
|
|
390
|
+
if (value !== null) {
|
|
391
|
+
const b = parseBool(value);
|
|
392
|
+
if (b === null) {
|
|
393
|
+
writeErr(`\\pset: unrecognized value "${value}" for "footer": Boolean expected\n`);
|
|
394
|
+
return {
|
|
395
|
+
status: "error",
|
|
396
|
+
errorWritten: true
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
topt.defaultFooter = b;
|
|
400
|
+
return { status: "ok" };
|
|
401
|
+
}
|
|
402
|
+
topt.defaultFooter = !topt.defaultFooter;
|
|
403
|
+
writeOutMaybe(topt.defaultFooter ? "Default footer is on.\n" : "Default footer is off.\n");
|
|
404
|
+
return { status: "ok" };
|
|
405
|
+
case "recordsep":
|
|
406
|
+
if (value !== null) topt.recordSep = value;
|
|
407
|
+
if (topt.recordSep === "\n") writeOutMaybe("Record separator is <newline>.\n");
|
|
408
|
+
else writeOutMaybe(`Record separator is "${topt.recordSep}".\n`);
|
|
409
|
+
return { status: "ok" };
|
|
410
|
+
case "recordsep_zero":
|
|
411
|
+
topt.recordSep = "\0";
|
|
412
|
+
writeOutMaybe("Record separator is zero byte.\n");
|
|
413
|
+
return { status: "ok" };
|
|
414
|
+
case "tuples_only":
|
|
415
|
+
case "t":
|
|
416
|
+
if (value !== null) {
|
|
417
|
+
const b = parseBool(value);
|
|
418
|
+
if (b === null) {
|
|
419
|
+
writeErr(`\\pset: unrecognized value "${value}": Boolean expected\n`);
|
|
420
|
+
return {
|
|
421
|
+
status: "error",
|
|
422
|
+
errorWritten: true
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
topt.tuplesOnly = b;
|
|
426
|
+
return { status: "ok" };
|
|
427
|
+
}
|
|
428
|
+
topt.tuplesOnly = !topt.tuplesOnly;
|
|
429
|
+
writeOutMaybe(topt.tuplesOnly ? "Tuples only is on.\n" : "Tuples only is off.\n");
|
|
430
|
+
return { status: "ok" };
|
|
431
|
+
case "title":
|
|
432
|
+
topt.title = value;
|
|
433
|
+
if (value === null) writeOutMaybe("Title is unset.\n");
|
|
434
|
+
else writeOutMaybe(`Title is "${value}".\n`);
|
|
435
|
+
return { status: "ok" };
|
|
436
|
+
case "tableattr":
|
|
437
|
+
case "t_a":
|
|
438
|
+
topt.tableAttr = value;
|
|
439
|
+
if (value === null) writeOutMaybe("Table attributes unset.\n");
|
|
440
|
+
else writeOutMaybe(`Table attributes are "${value}".\n`);
|
|
441
|
+
return { status: "ok" };
|
|
442
|
+
case "pager":
|
|
443
|
+
if (value === null) topt.pager = topt.pager === "off" ? "on" : "off";
|
|
444
|
+
else {
|
|
445
|
+
const lower = value.toLowerCase();
|
|
446
|
+
if (lower === "always") topt.pager = "always";
|
|
447
|
+
else if (lower === "on" || lower === "off") topt.pager = lower;
|
|
448
|
+
else {
|
|
449
|
+
const b = parseBool(value);
|
|
450
|
+
if (b === null) {
|
|
451
|
+
writeErr(`\\pset: unrecognized value "${value}" for "pager"\n`);
|
|
452
|
+
return {
|
|
453
|
+
status: "error",
|
|
454
|
+
errorWritten: true
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
topt.pager = b ? "on" : "off";
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
writeOutMaybe(topt.pager === "always" ? "Pager is always used.\n" : topt.pager === "on" ? "Pager is used for long output.\n" : "Pager usage is off.\n");
|
|
461
|
+
return { status: "ok" };
|
|
462
|
+
case "pager_min_lines": {
|
|
463
|
+
if (value !== null) {
|
|
464
|
+
const n = parseInt(value, 10);
|
|
465
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
466
|
+
writeErr(`\\pset: invalid pager_min_lines "${value}"\n`);
|
|
467
|
+
return {
|
|
468
|
+
status: "error",
|
|
469
|
+
errorWritten: true
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
topt.pagerMinLines = n;
|
|
473
|
+
}
|
|
474
|
+
const lines = topt.pagerMinLines;
|
|
475
|
+
writeOutMaybe(`Pager won't be used for less than ${lines} ${lines === 1 ? "line" : "lines"}.\n`);
|
|
476
|
+
return { status: "ok" };
|
|
477
|
+
}
|
|
478
|
+
case "null":
|
|
479
|
+
if (value !== null) topt.nullPrint = value;
|
|
480
|
+
writeOutMaybe(`Null display is "${topt.nullPrint}".\n`);
|
|
481
|
+
return { status: "ok" };
|
|
482
|
+
case "display_true":
|
|
483
|
+
if (value !== null) topt.truePrint = value;
|
|
484
|
+
writeOutMaybe(`Boolean true display is "${topt.truePrint ?? "t"}".\n`);
|
|
485
|
+
return { status: "ok" };
|
|
486
|
+
case "display_false":
|
|
487
|
+
if (value !== null) topt.falsePrint = value;
|
|
488
|
+
writeOutMaybe(`Boolean false display is "${topt.falsePrint ?? "f"}".\n`);
|
|
489
|
+
return { status: "ok" };
|
|
490
|
+
case "csv_fieldsep":
|
|
491
|
+
if (value !== null) {
|
|
492
|
+
if (value.length !== 1 || value === "\0") {
|
|
493
|
+
writeErr(`\\pset: csv_fieldsep must be a single one-byte character\n`);
|
|
494
|
+
return {
|
|
495
|
+
status: "error",
|
|
496
|
+
errorWritten: true
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
if (value === "\"" || value === "\n" || value === "\r") {
|
|
500
|
+
writeErr(`\\pset: csv_fieldsep cannot be a double quote, a newline, or a carriage return\n`);
|
|
501
|
+
return {
|
|
502
|
+
status: "error",
|
|
503
|
+
errorWritten: true
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
topt.csvFieldSep = value;
|
|
507
|
+
}
|
|
508
|
+
writeOutMaybe(`Field separator for CSV is "${topt.csvFieldSep}".\n`);
|
|
509
|
+
return { status: "ok" };
|
|
510
|
+
case "numericlocale":
|
|
511
|
+
if (value !== null) {
|
|
512
|
+
const p = parseTriple(value);
|
|
513
|
+
if (p === null || p === "auto") {
|
|
514
|
+
writeErr(`\\pset: unrecognized value "${value}" for "numericlocale": Boolean expected\n`);
|
|
515
|
+
return {
|
|
516
|
+
status: "error",
|
|
517
|
+
errorWritten: true
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
topt.numericLocale = p === "toggle" ? !topt.numericLocale : p === "on";
|
|
521
|
+
return { status: "ok" };
|
|
522
|
+
}
|
|
523
|
+
topt.numericLocale = !topt.numericLocale;
|
|
524
|
+
writeOutMaybe(topt.numericLocale ? "Locale-adjusted numeric output is on.\n" : "Locale-adjusted numeric output is off.\n");
|
|
525
|
+
return { status: "ok" };
|
|
526
|
+
case "linestyle": {
|
|
527
|
+
if (value === null) {
|
|
528
|
+
writeOutMaybe(`Line style is ${topt.unicodeBorderLineStyle}.\n`);
|
|
529
|
+
return { status: "ok" };
|
|
530
|
+
}
|
|
531
|
+
const lower = value.toLowerCase();
|
|
532
|
+
if (lower === "ascii" || lower === "old-ascii" || lower === "unicode") {
|
|
533
|
+
const ls = lower;
|
|
534
|
+
topt.unicodeBorderLineStyle = ls;
|
|
535
|
+
topt.unicodeColumnLineStyle = ls;
|
|
536
|
+
topt.unicodeHeaderLineStyle = ls;
|
|
537
|
+
writeOutMaybe(`Line style is ${ls}.\n`);
|
|
538
|
+
return { status: "ok" };
|
|
539
|
+
}
|
|
540
|
+
writeErr(`\\pset: allowed line styles are ascii, old-ascii, unicode\n`);
|
|
541
|
+
return {
|
|
542
|
+
status: "error",
|
|
543
|
+
errorWritten: true
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
case "columns":
|
|
547
|
+
if (value !== null) {
|
|
548
|
+
const n = parseInt(value, 10);
|
|
549
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
550
|
+
writeErr(`\\pset: invalid columns "${value}"\n`);
|
|
551
|
+
return {
|
|
552
|
+
status: "error",
|
|
553
|
+
errorWritten: true
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
topt.columns = n;
|
|
557
|
+
}
|
|
558
|
+
if (topt.columns === 0) writeOutMaybe("Target width is unset.\n");
|
|
559
|
+
else writeOutMaybe(`Target width is ${topt.columns}.\n`);
|
|
560
|
+
return { status: "ok" };
|
|
561
|
+
case "xheader_width": {
|
|
562
|
+
if (value !== null) {
|
|
563
|
+
const lower = value.toLowerCase();
|
|
564
|
+
if (lower === "full" || lower === "column" || lower === "page") topt.xheaderWidth = lower;
|
|
565
|
+
else {
|
|
566
|
+
const n = parseInt(value, 10);
|
|
567
|
+
if (!Number.isFinite(n) || n <= 0 || !/^[+]?\d+$/.test(value.trim())) {
|
|
568
|
+
writeErr(`\\pset: allowed xheader_width values are "full" (default), "column", "page", or a number specifying the exact width\n`);
|
|
569
|
+
return {
|
|
570
|
+
status: "error",
|
|
571
|
+
errorWritten: true
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
topt.xheaderWidth = n;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
const current = topt.xheaderWidth ?? "full";
|
|
578
|
+
if (typeof current === "number") writeOutMaybe(`Expanded header width is ${current}.\n`);
|
|
579
|
+
else writeOutMaybe(`Expanded header width is "${current}".\n`);
|
|
580
|
+
return { status: "ok" };
|
|
581
|
+
}
|
|
582
|
+
case "unicode_border_linestyle":
|
|
583
|
+
case "unicode_column_linestyle":
|
|
584
|
+
case "unicode_header_linestyle": {
|
|
585
|
+
const which = opt === "unicode_border_linestyle" ? "border" : opt === "unicode_column_linestyle" ? "column" : "header";
|
|
586
|
+
if (value !== null) {
|
|
587
|
+
const lower = value.toLowerCase();
|
|
588
|
+
if (lower !== "single" && lower !== "double") {
|
|
589
|
+
writeErr(`\\pset: ${opt} must be single or double\n`);
|
|
590
|
+
return {
|
|
591
|
+
status: "error",
|
|
592
|
+
errorWritten: true
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
const style = lower;
|
|
596
|
+
if (opt === "unicode_border_linestyle") topt.unicodeBorderStyle = style;
|
|
597
|
+
else if (opt === "unicode_column_linestyle") topt.unicodeColumnStyle = style;
|
|
598
|
+
else topt.unicodeHeaderStyle = style;
|
|
599
|
+
}
|
|
600
|
+
writeOutMaybe(`Unicode ${which} line style is "${opt === "unicode_border_linestyle" ? topt.unicodeBorderStyle ?? "single" : opt === "unicode_column_linestyle" ? topt.unicodeColumnStyle ?? "single" : topt.unicodeHeaderStyle ?? "single"}".\n`);
|
|
601
|
+
return { status: "ok" };
|
|
602
|
+
}
|
|
603
|
+
default:
|
|
604
|
+
writeErr(`\\pset: unknown option "${option}"\n`);
|
|
605
|
+
return {
|
|
606
|
+
status: "error",
|
|
607
|
+
errorWritten: true
|
|
608
|
+
};
|
|
609
|
+
}
|
|
735
610
|
};
|
|
736
611
|
/**
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
612
|
+
* Wrap a string value in single quotes, escaping embedded newlines and
|
|
613
|
+
* single quotes. Mirrors upstream `pset_quoted_string` in
|
|
614
|
+
* `src/bin/psql/command.c` — used by the bulk-view formatter so the
|
|
615
|
+
* emitted line can be fed back into `\pset NAME VALUE`.
|
|
616
|
+
*/
|
|
742
617
|
const psetQuotedString = (str) => {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
else
|
|
750
|
-
out += ch;
|
|
751
|
-
}
|
|
752
|
-
out += "'";
|
|
753
|
-
return out;
|
|
618
|
+
let out = "'";
|
|
619
|
+
for (const ch of str) if (ch === "\n") out += "\\n";
|
|
620
|
+
else if (ch === "'") out += "\\'";
|
|
621
|
+
else out += ch;
|
|
622
|
+
out += "'";
|
|
623
|
+
return out;
|
|
754
624
|
};
|
|
755
625
|
/**
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
626
|
+
* Render the numeric pager encoding upstream uses in `printPsetInfo`:
|
|
627
|
+
* 0 = never, 1 = "if needed" (our `'on'`), 2 = always. We keep
|
|
628
|
+
* `topt.pager` as the upstream-style triple ('off'|'on'|'always') for
|
|
629
|
+
* `applyPset`'s state machine; this is only the bulk-view conversion.
|
|
630
|
+
*/
|
|
761
631
|
const pagerNumeric = (pager) => pager === "off" ? 0 : pager === "on" ? 1 : 2;
|
|
762
632
|
/**
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
const xheaderWidthDisplay = (w) =>
|
|
633
|
+
* Render `xheader_width` for the bulk view. Enum values print verbatim;
|
|
634
|
+
* numeric values print as the integer.
|
|
635
|
+
*/
|
|
636
|
+
const xheaderWidthDisplay = (w) => typeof w === "number" ? String(w) : w;
|
|
767
637
|
/**
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
638
|
+
* Print the full current `\pset` state, one option per line, to stdout.
|
|
639
|
+
* Used when `\pset` is invoked with no arguments. String-valued settings
|
|
640
|
+
* are single-quoted (matching upstream `pset_value_string`); `tableattr`
|
|
641
|
+
* and `title` are unquoted-empty when unset. The set, ordering, and
|
|
642
|
+
* column-spacing mirror `printPsetInfo` in `src/bin/psql/command.c`.
|
|
643
|
+
*/
|
|
774
644
|
const printAllPset = (topt) => {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
writeOut(`unicode_column_linestyle ${topt.unicodeColumnStyle ?? "single"}\n`);
|
|
800
|
-
writeOut(`unicode_header_linestyle ${topt.unicodeHeaderStyle ?? "single"}\n`);
|
|
801
|
-
writeOut(`xheader_width ${xheaderWidthDisplay(topt.xheaderWidth ?? "full")}\n`);
|
|
645
|
+
writeOut(`border ${topt.border}\n`);
|
|
646
|
+
writeOut(`columns ${topt.columns}\n`);
|
|
647
|
+
writeOut(`csv_fieldsep ${psetQuotedString(topt.csvFieldSep)}\n`);
|
|
648
|
+
writeOut(`display_false ${psetQuotedString(topt.falsePrint ?? "f")}\n`);
|
|
649
|
+
writeOut(`display_true ${psetQuotedString(topt.truePrint ?? "t")}\n`);
|
|
650
|
+
writeOut(`expanded ${topt.expanded}\n`);
|
|
651
|
+
writeOut(`fieldsep ${psetQuotedString(topt.fieldSep)}\n`);
|
|
652
|
+
writeOut(`fieldsep_zero ${topt.fieldSep === "\0" ? "on" : "off"}\n`);
|
|
653
|
+
writeOut(`footer ${topt.defaultFooter ? "on" : "off"}\n`);
|
|
654
|
+
writeOut(`format ${topt.format}\n`);
|
|
655
|
+
writeOut(`linestyle ${topt.unicodeBorderLineStyle}\n`);
|
|
656
|
+
writeOut(`null ${psetQuotedString(topt.nullPrint)}\n`);
|
|
657
|
+
writeOut(`numericlocale ${topt.numericLocale ? "on" : "off"}\n`);
|
|
658
|
+
writeOut(`pager ${pagerNumeric(topt.pager)}\n`);
|
|
659
|
+
writeOut(`pager_min_lines ${topt.pagerMinLines}\n`);
|
|
660
|
+
writeOut(`recordsep ${psetQuotedString(topt.recordSep)}\n`);
|
|
661
|
+
writeOut(`recordsep_zero ${topt.recordSep === "\0" ? "on" : "off"}\n`);
|
|
662
|
+
writeOut(`tableattr ${topt.tableAttr === null ? "" : psetQuotedString(topt.tableAttr)}\n`);
|
|
663
|
+
writeOut(`title ${topt.title === null ? "" : psetQuotedString(topt.title)}\n`);
|
|
664
|
+
writeOut(`tuples_only ${topt.tuplesOnly ? "on" : "off"}\n`);
|
|
665
|
+
writeOut(`unicode_border_linestyle ${topt.unicodeBorderStyle ?? "single"}\n`);
|
|
666
|
+
writeOut(`unicode_column_linestyle ${topt.unicodeColumnStyle ?? "single"}\n`);
|
|
667
|
+
writeOut(`unicode_header_linestyle ${topt.unicodeHeaderStyle ?? "single"}\n`);
|
|
668
|
+
writeOut(`xheader_width ${xheaderWidthDisplay(topt.xheaderWidth ?? "full")}\n`);
|
|
802
669
|
};
|
|
803
670
|
const lexRawArgs = (tail) => {
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
continue;
|
|
862
|
-
}
|
|
863
|
-
arg += c;
|
|
864
|
-
i++;
|
|
865
|
-
}
|
|
866
|
-
out.push({ arg, endIdx: i });
|
|
867
|
-
}
|
|
868
|
-
return out;
|
|
671
|
+
const out = [];
|
|
672
|
+
let i = 0;
|
|
673
|
+
const isSpace = (c) => c === " " || c === " " || c === "\n" || c === "\r" || c === "\f" || c === "\v";
|
|
674
|
+
while (i < tail.length) {
|
|
675
|
+
while (i < tail.length && isSpace(tail[i])) i++;
|
|
676
|
+
if (i >= tail.length) break;
|
|
677
|
+
if (tail[i] === "\\") break;
|
|
678
|
+
let arg = "";
|
|
679
|
+
while (i < tail.length && !isSpace(tail[i]) && tail[i] !== "\\") {
|
|
680
|
+
const c = tail[i];
|
|
681
|
+
if (c === "'") {
|
|
682
|
+
let j = i + 1;
|
|
683
|
+
let inner = "";
|
|
684
|
+
while (j < tail.length && tail[j] !== "'") {
|
|
685
|
+
if (tail[j] === "\\" && j + 1 < tail.length) {
|
|
686
|
+
inner += tail[j] + tail[j + 1];
|
|
687
|
+
j += 2;
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
inner += tail[j];
|
|
691
|
+
j++;
|
|
692
|
+
}
|
|
693
|
+
arg += `'${inner}'`;
|
|
694
|
+
i = j < tail.length ? j + 1 : j;
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
if (c === "\"") {
|
|
698
|
+
let j = i + 1;
|
|
699
|
+
let inner = "";
|
|
700
|
+
while (j < tail.length && tail[j] !== "\"") {
|
|
701
|
+
inner += tail[j];
|
|
702
|
+
j++;
|
|
703
|
+
}
|
|
704
|
+
arg += `"${inner}"`;
|
|
705
|
+
i = j < tail.length ? j + 1 : j;
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
if (c === "`") {
|
|
709
|
+
let j = i + 1;
|
|
710
|
+
let inner = "";
|
|
711
|
+
while (j < tail.length && tail[j] !== "`") {
|
|
712
|
+
inner += tail[j];
|
|
713
|
+
j++;
|
|
714
|
+
}
|
|
715
|
+
arg += inner;
|
|
716
|
+
i = j < tail.length ? j + 1 : j;
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
arg += c;
|
|
720
|
+
i++;
|
|
721
|
+
}
|
|
722
|
+
out.push({
|
|
723
|
+
arg,
|
|
724
|
+
endIdx: i
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
return out;
|
|
869
728
|
};
|
|
870
729
|
/**
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
const rawEntries = lexRawArgs(ctx.rawArgs);
|
|
916
|
-
if (rawEntries.length === 0) {
|
|
917
|
-
printAllPset(ctx.settings.popt.topt);
|
|
918
|
-
return Promise.resolve({ status: "ok" });
|
|
919
|
-
}
|
|
920
|
-
// Slice rawArgs up to the end of arg #2 (or arg #1 if only one was
|
|
921
|
-
// provided) and feed THAT to the eval-mode scanner. Anything past
|
|
922
|
-
// that boundary lives in the no-eval extras zone.
|
|
923
|
-
const headEndIdx = rawEntries.length >= 2
|
|
924
|
-
? rawEntries[1].endIdx
|
|
925
|
-
: rawEntries[0].endIdx;
|
|
926
|
-
const headSlice = ctx.rawArgs.slice(0, headEndIdx);
|
|
927
|
-
const varLookup = (name) => ctx.settings.vars.get(name);
|
|
928
|
-
const headArgs = scanSlashArgs(headSlice, "normal", varLookup);
|
|
929
|
-
const option = headArgs[0] ?? null;
|
|
930
|
-
if (option === null) {
|
|
931
|
-
printAllPset(ctx.settings.popt.topt);
|
|
932
|
-
return Promise.resolve({ status: "ok" });
|
|
933
|
-
}
|
|
934
|
-
const value = headArgs[1] ?? null;
|
|
935
|
-
// Under `--quiet` / `\set QUIET on`, upstream `exec_command_pset`
|
|
936
|
-
// (and the printPsetInfo helper it delegates to) suppresses the
|
|
937
|
-
// confirmation lines like `Null display is "…".` and `Tuples only
|
|
938
|
-
// is on.`. Pass `silent=true` so applyPset skips the writes —
|
|
939
|
-
// errors (invalid option / bad value) still go to stderr.
|
|
940
|
-
const result = applyPset(ctx.settings.popt.topt, option, value, ctx.cmdName, ctx.settings.quiet);
|
|
941
|
-
// Drain extras. Index 2+ in the no-eval lex are the tokens past
|
|
942
|
-
// option+value; emit one warning per raw token.
|
|
943
|
-
for (let i = 2; i < rawEntries.length; i++) {
|
|
944
|
-
writeErr(`\\${ctx.cmdName}: extra argument "${rawEntries[i].arg}" ignored\n`);
|
|
945
|
-
}
|
|
946
|
-
return Promise.resolve(result);
|
|
947
|
-
},
|
|
730
|
+
* `\pset [option [value]]` — the master print-options setter.
|
|
731
|
+
*
|
|
732
|
+
* - No args: print all options.
|
|
733
|
+
* - Option only: toggle (for booleans) or show current value.
|
|
734
|
+
* - Option + value: set.
|
|
735
|
+
* - Option + value + extra: set, then emit one `extra argument "X" ignored`
|
|
736
|
+
* per extra arg (matches upstream `exec_command_pset`'s post-`do_pset`
|
|
737
|
+
* call to `ignore_slash_options`).
|
|
738
|
+
*
|
|
739
|
+
* Implementation note: the surrounding REPL's `BackslashContext` (built
|
|
740
|
+
* in `core/mainloop.ts::makeBackslashContext`) returns the full
|
|
741
|
+
* `rawArgs` verbatim from `restOfLine()` — there's no cursor we can
|
|
742
|
+
* read to find the unconsumed tail. We therefore lex `rawArgs` a second
|
|
743
|
+
* time with {@link lexExtraArgs} to recover the *raw* (unexpanded)
|
|
744
|
+
* token text, while still calling `ctx.nextArg('normal')` for
|
|
745
|
+
* option/value so variable substitution and backtick execution behave
|
|
746
|
+
* exactly like the rest of the slash-command layer. The drain pulls
|
|
747
|
+
* the raw token text from index 2+ so the warning preserves the
|
|
748
|
+
* upstream `OT_NO_EVAL` semantics — `:foo` stays as `:foo`, `` `cmd` ``
|
|
749
|
+
* collapses to `cmd` without running.
|
|
750
|
+
*/
|
|
751
|
+
const cmdPset = {
|
|
752
|
+
name: "pset",
|
|
753
|
+
helpKey: "pset",
|
|
754
|
+
run: (ctx) => {
|
|
755
|
+
const rawEntries = lexRawArgs(ctx.rawArgs);
|
|
756
|
+
if (rawEntries.length === 0) {
|
|
757
|
+
printAllPset(ctx.settings.popt.topt);
|
|
758
|
+
return Promise.resolve({ status: "ok" });
|
|
759
|
+
}
|
|
760
|
+
const headEndIdx = rawEntries.length >= 2 ? rawEntries[1].endIdx : rawEntries[0].endIdx;
|
|
761
|
+
const headSlice = ctx.rawArgs.slice(0, headEndIdx);
|
|
762
|
+
const varLookup = (name) => ctx.settings.vars.get(name);
|
|
763
|
+
const headArgs = scanSlashArgs(headSlice, "normal", varLookup);
|
|
764
|
+
const option = headArgs[0] ?? null;
|
|
765
|
+
if (option === null) {
|
|
766
|
+
printAllPset(ctx.settings.popt.topt);
|
|
767
|
+
return Promise.resolve({ status: "ok" });
|
|
768
|
+
}
|
|
769
|
+
const value = headArgs[1] ?? null;
|
|
770
|
+
const result = applyPset(ctx.settings.popt.topt, option, value, ctx.cmdName, ctx.settings.quiet);
|
|
771
|
+
for (let i = 2; i < rawEntries.length; i++) writeErr(`\\${ctx.cmdName}: extra argument "${rawEntries[i].arg}" ignored\n`);
|
|
772
|
+
return Promise.resolve(result);
|
|
773
|
+
}
|
|
948
774
|
};
|
|
775
|
+
//#endregion
|
|
776
|
+
export { applyPset, cmdA, cmdC, cmdEncoding, cmdF, cmdH, cmdPset, cmdT, cmdTitleAttr, cmdX };
|