feat(help): distinct help for advanced-mode SQL forms; split list by mode (#36)
The six advanced SQL DML/query forms (SELECT, WITH, SQL_INSERT, SQL_UPDATE, SQL_DELETE, EXPLAIN_SQL) carried help_id: None, so `help select`/`help with` resolved to nothing and `help insert` showed only the simple form. Give each its own distinct help_id (data.select, data.sql_insert, …) with a hand-curated help.data.* page. Distinct strings keep the dedup invariant intact, and note_help_topic needs no change — `help insert` now shows the simple block and the sql_insert block (like `help create` already did), and the advanced-only forms resolve. note_help now groups the list by CommandCategory: app-lifecycle commands first (unlabelled), then "Simple-mode commands:" and "Advanced-mode (SQL) commands:" sections — replacing the old single "DSL data commands (in simple mode):" header, which used the banned "DSL" term and mis-labelled the advanced SQL forms it already contained. Four new help_command tests (red→green). Recorded as ADR-0024 Amendment 1; CHANGELOG updated.
This commit is contained in:
@@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- `help` now covers the advanced-mode SQL commands: `help select`, `help with`,
|
||||||
|
and the SQL forms of `insert` / `update` / `delete` / `explain` show their own
|
||||||
|
syntax, and the full command list is grouped into "Simple-mode commands" and
|
||||||
|
"Advanced-mode (SQL) commands" sections.
|
||||||
- Install via **Scoop**, **Homebrew**, and **winget** in addition to the
|
- Install via **Scoop**, **Homebrew**, and **winget** in addition to the
|
||||||
existing channels.
|
existing channels.
|
||||||
- Tier-4 end-to-end test suite that exercises the real application in a
|
- Tier-4 end-to-end test suite that exercises the real application in a
|
||||||
|
|||||||
@@ -706,6 +706,56 @@ documentation is still hand-curated for round 1.
|
|||||||
table-ident from new-name-ident visually is a future
|
table-ident from new-name-ident visually is a future
|
||||||
enhancement.
|
enhancement.
|
||||||
|
|
||||||
|
## Amendment 1 — advanced-mode SQL forms get their own `help` pages, and the list groups by mode (2026-06-22, issue #36)
|
||||||
|
|
||||||
|
`help_id` (§Node taxonomy) drives two surfaces: the `help` **list**
|
||||||
|
(`note_help` emits one `help.<id>` block per `Some` id) and the
|
||||||
|
`help <topic>` **lookup** (`note_help_topic` shows the block of every
|
||||||
|
node whose entry word matches the topic). The dedup rule is that
|
||||||
|
`help_id` *strings* are unique (one block ⇒ printed once).
|
||||||
|
|
||||||
|
Originally the six advanced-mode SQL **DML/query** forms — `SELECT`,
|
||||||
|
`WITH`, `SQL_INSERT`, `SQL_UPDATE`, `SQL_DELETE`, `EXPLAIN_SQL` —
|
||||||
|
carried `help_id: None`. That was a list-formatting shortcut (avoid a
|
||||||
|
second `insert` entry), but it had a side effect: `help select` /
|
||||||
|
`help with` resolved to **nothing** (the unknown-topic note), and
|
||||||
|
`help insert` showed only the simple form — even though the SQL surface
|
||||||
|
is genuinely different and advanced mode exists precisely for learners
|
||||||
|
moving to raw SQL. (The advanced SQL **DDL** forms — `sql_create_table`
|
||||||
|
etc. — already had their own `help.ddl.sql_*` pages, so the gap was
|
||||||
|
inconsistent as well as a pedagogy hole.)
|
||||||
|
|
||||||
|
**Decision.** Give every advanced SQL form its **own** `help_id`
|
||||||
|
(`data.select`, `data.with`, `data.sql_insert`, `data.sql_update`,
|
||||||
|
`data.sql_delete`, `data.explain_sql`) with a hand-curated
|
||||||
|
`help.data.*` page (the catalog body stays hand-written, per "What's
|
||||||
|
out of scope" above — this amendment doesn't change that). Because the
|
||||||
|
ids are **distinct strings**, the dedup invariant
|
||||||
|
(`no_two_registered_commands_share_a_help_id`) is untouched, and:
|
||||||
|
|
||||||
|
- **`help <topic>` shows every form sharing the entry word** — so
|
||||||
|
`help insert` shows the simple block *and* the `sql_insert` block
|
||||||
|
(exactly as `help create` already showed the simple + SQL create
|
||||||
|
forms). Advanced-only `help select` / `help with` now resolve.
|
||||||
|
- **The list groups by mode.** `note_help` now splits the REGISTRY by
|
||||||
|
[`CommandCategory`]: app-lifecycle commands (ids in the `app.*`
|
||||||
|
namespace, usable in either mode) list first, unlabelled, under the
|
||||||
|
intro; then a **`help.simple_section`** ("Simple-mode commands:")
|
||||||
|
group and a **`help.advanced_section`** ("Advanced-mode (SQL)
|
||||||
|
commands:") group. This replaces the single `help.dsl_section` header
|
||||||
|
("DSL data commands (in simple mode):"), which both used the banned
|
||||||
|
"DSL" term (ADR-0002 user-facing posture) and wrongly claimed simple
|
||||||
|
mode for the advanced SQL forms the section already held.
|
||||||
|
|
||||||
|
This partially realises ADR-0030 §6's "Polish" item (a `help sql`
|
||||||
|
page): rather than one combined page, each form has its own, reached
|
||||||
|
through the normal `help <topic>` surface and discoverable in the
|
||||||
|
advanced-mode list section. `note_help_topic` needed **no** change —
|
||||||
|
the new `help_id`s make the forms resolve automatically. Covered by
|
||||||
|
`help_command::{help_select_renders_the_sql_select_block,
|
||||||
|
help_with_renders_the_cte_block, help_insert_shows_both_simple_and_sql_forms,
|
||||||
|
help_list_splits_simple_and_advanced_sections}`.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- ADR-0023 — Unified declarative grammar tree (Proposed direction). Superseded by this ADR for execution detail.
|
- ADR-0023 — Unified declarative grammar tree (Proposed direction). Superseded by this ADR for execution detail.
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+35
-17
@@ -3047,34 +3047,52 @@ impl App {
|
|||||||
/// output panel.
|
/// output panel.
|
||||||
///
|
///
|
||||||
/// Assembled from the command REGISTRY (ADR-0024 §help_id):
|
/// Assembled from the command REGISTRY (ADR-0024 §help_id):
|
||||||
/// the framing (`help.intro`, `help.dsl_section`,
|
/// the framing (`help.intro`, `help.simple_section`,
|
||||||
/// `help.types_reference`) comes from the catalog, and each
|
/// `help.advanced_section`, `help.types_reference`) comes from
|
||||||
/// command's body is the catalog entry named by its
|
/// the catalog, and each command's body is the catalog entry
|
||||||
/// `help_id`. A newly-registered command appears here
|
/// named by its `help_id`. A newly-registered command appears
|
||||||
/// automatically — no edit to this function or a hand-kept
|
/// here automatically — no edit to this function or a hand-kept
|
||||||
/// list. Each catalog line becomes its own `OutputLine` so
|
/// list. Each catalog line becomes its own `OutputLine` so
|
||||||
/// the scroll-position math (one logical line = one display
|
/// the scroll-position math (one logical line = one display
|
||||||
/// row) stays accurate per the renderer's invariant.
|
/// row) stays accurate per the renderer's invariant.
|
||||||
|
///
|
||||||
|
/// Issue #36: the commands group by mode. App-lifecycle commands
|
||||||
|
/// (`help_id` in the `app.*` namespace) work in either mode and
|
||||||
|
/// list first, unlabelled, under the intro. The rest split by
|
||||||
|
/// [`CommandCategory`] into a simple-mode group and an
|
||||||
|
/// advanced-mode (SQL) group — replacing the old single "DSL data
|
||||||
|
/// commands (in simple mode)" header, which both used the banned
|
||||||
|
/// "DSL" term and wrongly claimed simple mode for the advanced SQL
|
||||||
|
/// forms the section already contained.
|
||||||
fn note_help(&mut self) {
|
fn note_help(&mut self) {
|
||||||
use crate::dsl::grammar::REGISTRY;
|
use crate::dsl::grammar::{CommandCategory, REGISTRY};
|
||||||
|
|
||||||
let mut lines: Vec<String> = Vec::new();
|
let mut lines: Vec<String> = Vec::new();
|
||||||
lines.push(crate::t!("help.intro"));
|
lines.push(crate::t!("help.intro"));
|
||||||
// REGISTRY is ordered app-commands first; emit the
|
|
||||||
// "DSL data commands" sub-header at the first command
|
let mut simple: Vec<String> = Vec::new();
|
||||||
// whose help_id leaves the `app.` namespace.
|
let mut advanced: Vec<String> = Vec::new();
|
||||||
let mut dsl_header_done = false;
|
for (command, category) in REGISTRY {
|
||||||
for (command, _category) in REGISTRY {
|
|
||||||
let Some(help_id) = command.help_id else {
|
let Some(help_id) = command.help_id else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if !dsl_header_done && !help_id.starts_with("app.") {
|
let body = crate::friendly::translate(&format!("help.{help_id}"), &[]);
|
||||||
lines.push(crate::t!("help.dsl_section"));
|
let block: Vec<String> = body.lines().map(str::to_string).collect();
|
||||||
dsl_header_done = true;
|
if help_id.starts_with("app.") {
|
||||||
|
lines.extend(block);
|
||||||
|
} else if matches!(category, CommandCategory::Advanced) {
|
||||||
|
advanced.extend(block);
|
||||||
|
} else {
|
||||||
|
simple.extend(block);
|
||||||
}
|
}
|
||||||
let key = format!("help.{help_id}");
|
}
|
||||||
let body = crate::friendly::translate(&key, &[]);
|
if !simple.is_empty() {
|
||||||
lines.extend(body.lines().map(str::to_string));
|
lines.push(crate::t!("help.simple_section"));
|
||||||
|
lines.extend(simple);
|
||||||
|
}
|
||||||
|
if !advanced.is_empty() {
|
||||||
|
lines.push(crate::t!("help.advanced_section"));
|
||||||
|
lines.extend(advanced);
|
||||||
}
|
}
|
||||||
lines.extend(
|
lines.extend(
|
||||||
crate::t!("help.types_reference")
|
crate::t!("help.types_reference")
|
||||||
|
|||||||
+13
-13
@@ -1886,12 +1886,12 @@ pub static EXPLAIN_SQL: CommandNode = CommandNode {
|
|||||||
entry: Word::keyword("explain"),
|
entry: Word::keyword("explain"),
|
||||||
shape: EXPLAIN_SQL_SHAPE,
|
shape: EXPLAIN_SQL_SHAPE,
|
||||||
ast_builder: build_explain_sql,
|
ast_builder: build_explain_sql,
|
||||||
// No `help_id` / `usage_ids` — this is the `Advanced` half of the
|
// Issue #36: its own `help` page (`data.explain_sql`), listed under the
|
||||||
// shared `explain` entry word, so it defers to the `Simple`
|
// advanced-mode (SQL) section and shown by `help explain` next to the
|
||||||
// `EXPLAIN` node's help/usage (which now covers the SQL forms
|
// simple `EXPLAIN` form (distinct help_id ⇒ no dedup clash). `usage_ids`
|
||||||
// too). Mirrors the `SQL_INSERT`/`SQL_UPDATE`/`SQL_DELETE`
|
// stays empty — the `Simple` `EXPLAIN` node's usage block already covers
|
||||||
// precedent; otherwise `note_help` would print `explain` twice.
|
// the shared `explain` entry word.
|
||||||
help_id: None,
|
help_id: Some("data.explain_sql"),
|
||||||
hint_ids: &["explain_sql"],
|
hint_ids: &["explain_sql"],
|
||||||
usage_ids: &[],
|
usage_ids: &[],
|
||||||
};
|
};
|
||||||
@@ -1902,13 +1902,13 @@ pub static EXPLAIN_SQL: CommandNode = CommandNode {
|
|||||||
/// The shape is the post-`SELECT` portion of a top-level
|
/// The shape is the post-`SELECT` portion of a top-level
|
||||||
/// statement; the registry's entry-word dispatch consumes the
|
/// statement; the registry's entry-word dispatch consumes the
|
||||||
/// leading `SELECT` keyword before the shape walks (sub-phase
|
/// leading `SELECT` keyword before the shape walks (sub-phase
|
||||||
/// 2c migration). `help_id` is `None` until the `help sql`
|
/// 2c migration). Carries its own `help` page (`data.select`),
|
||||||
/// page lands (ADR-0030 Phase 6).
|
/// listed under the advanced-mode (SQL) section (issue #36).
|
||||||
pub static SELECT: CommandNode = CommandNode {
|
pub static SELECT: CommandNode = CommandNode {
|
||||||
entry: Word::keyword("select"),
|
entry: Word::keyword("select"),
|
||||||
shape: Node::Subgrammar(&sql_select::SQL_SELECT_TAIL),
|
shape: Node::Subgrammar(&sql_select::SQL_SELECT_TAIL),
|
||||||
ast_builder: build_select,
|
ast_builder: build_select,
|
||||||
help_id: None,
|
help_id: Some("data.select"),
|
||||||
hint_ids: &["select"],
|
hint_ids: &["select"],
|
||||||
usage_ids: &["parse.usage.select"],
|
usage_ids: &["parse.usage.select"],
|
||||||
};
|
};
|
||||||
@@ -1924,7 +1924,7 @@ pub static WITH: CommandNode = CommandNode {
|
|||||||
entry: Word::keyword("with"),
|
entry: Word::keyword("with"),
|
||||||
shape: Node::Subgrammar(&sql_select::SQL_WITH_TAIL),
|
shape: Node::Subgrammar(&sql_select::SQL_WITH_TAIL),
|
||||||
ast_builder: build_select,
|
ast_builder: build_select,
|
||||||
help_id: None,
|
help_id: Some("data.with"), // issue #36: own help page, advanced section
|
||||||
hint_ids: &["with"],
|
hint_ids: &["with"],
|
||||||
usage_ids: &["parse.usage.with"],
|
usage_ids: &["parse.usage.with"],
|
||||||
};
|
};
|
||||||
@@ -1943,7 +1943,7 @@ pub static SQL_INSERT: CommandNode = CommandNode {
|
|||||||
entry: Word::keyword("insert"),
|
entry: Word::keyword("insert"),
|
||||||
shape: Node::Subgrammar(&sql_insert::SQL_INSERT_SHAPE),
|
shape: Node::Subgrammar(&sql_insert::SQL_INSERT_SHAPE),
|
||||||
ast_builder: build_sql_insert,
|
ast_builder: build_sql_insert,
|
||||||
help_id: None,
|
help_id: Some("data.sql_insert"), // issue #36: own help page, advanced section
|
||||||
hint_ids: &["sql_insert"],
|
hint_ids: &["sql_insert"],
|
||||||
usage_ids: &[],
|
usage_ids: &[],
|
||||||
};
|
};
|
||||||
@@ -1957,7 +1957,7 @@ pub static SQL_UPDATE: CommandNode = CommandNode {
|
|||||||
entry: Word::keyword("update"),
|
entry: Word::keyword("update"),
|
||||||
shape: Node::Subgrammar(&sql_update::SQL_UPDATE_SHAPE),
|
shape: Node::Subgrammar(&sql_update::SQL_UPDATE_SHAPE),
|
||||||
ast_builder: build_sql_update,
|
ast_builder: build_sql_update,
|
||||||
help_id: None,
|
help_id: Some("data.sql_update"), // issue #36: own help page, advanced section
|
||||||
hint_ids: &["sql_update"],
|
hint_ids: &["sql_update"],
|
||||||
usage_ids: &[],
|
usage_ids: &[],
|
||||||
};
|
};
|
||||||
@@ -1973,7 +1973,7 @@ pub static SQL_DELETE: CommandNode = CommandNode {
|
|||||||
entry: Word::keyword("delete"),
|
entry: Word::keyword("delete"),
|
||||||
shape: Node::Subgrammar(&sql_delete::SQL_DELETE_SHAPE),
|
shape: Node::Subgrammar(&sql_delete::SQL_DELETE_SHAPE),
|
||||||
ast_builder: build_sql_delete,
|
ast_builder: build_sql_delete,
|
||||||
help_id: None,
|
help_id: Some("data.sql_delete"), // issue #36: own help page, advanced section
|
||||||
hint_ids: &["sql_delete"],
|
hint_ids: &["sql_delete"],
|
||||||
usage_ids: &[],
|
usage_ids: &[],
|
||||||
};
|
};
|
||||||
|
|||||||
+11
-6
@@ -537,8 +537,11 @@ pub struct CommandNode {
|
|||||||
/// block). `hint_key_for_input_in_mode` disambiguates by the form
|
/// block). `hint_key_for_input_in_mode` disambiguates by the form
|
||||||
/// word, reusing `usage_key_for_input_in_mode`'s logic. Empty
|
/// word, reusing `usage_key_for_input_in_mode`'s logic. Empty
|
||||||
/// until a form's tier-3 block is authored (the surface falls back
|
/// until a form's tier-3 block is authored (the surface falls back
|
||||||
/// to tier-2 ambient/error text). Distinct from `help_id` (which is
|
/// to tier-2 ambient/error text). Parallel to `help_id` but
|
||||||
/// `None` on advanced-SQL forms purely to dedup the `help` list).
|
/// finer-grained: every form (simple and advanced) carries a
|
||||||
|
/// `hint_id`, whereas the `help <topic>` view groups forms by entry
|
||||||
|
/// word (so a shared-entry simple + SQL pair both surface under e.g.
|
||||||
|
/// `help insert`).
|
||||||
pub hint_ids: &'static [&'static str],
|
pub hint_ids: &'static [&'static str],
|
||||||
/// Catalog keys under `parse.usage.*` to render in the
|
/// Catalog keys under `parse.usage.*` to render in the
|
||||||
/// "usage:" block when a parse error fires for this command
|
/// "usage:" block when a parse error fires for this command
|
||||||
@@ -1156,10 +1159,12 @@ mod usage_key_tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn no_two_registered_commands_share_a_help_id() {
|
fn no_two_registered_commands_share_a_help_id() {
|
||||||
// `note_help` emits one help block per `help_id: Some(_)`
|
// `note_help` emits one help block per `help_id: Some(_)`
|
||||||
// with no dedup, so a duplicate help_id prints the same
|
// with no dedup, so a duplicate help_id string prints the same
|
||||||
// command twice in `help`. Shared-entry-word `Advanced`
|
// block twice. Distinct help_ids are fine — a shared-entry-word
|
||||||
// nodes (SQL_INSERT, …, EXPLAIN_SQL) therefore carry
|
// simple + SQL pair (e.g. `data.insert` + `data.sql_insert`,
|
||||||
// `help_id: None` and defer to their `Simple` sibling.
|
// issue #36) each get their own block, grouped under one topic
|
||||||
|
// by `help <topic>` and split across the simple/advanced
|
||||||
|
// sections of the full list.
|
||||||
let mut seen = std::collections::HashSet::new();
|
let mut seen = std::collections::HashSet::new();
|
||||||
for (command, _category) in super::REGISTRY {
|
for (command, _category) in super::REGISTRY {
|
||||||
if let Some(id) = command.help_id {
|
if let Some(id) = command.help_id {
|
||||||
|
|||||||
@@ -180,7 +180,8 @@ pub const KEYS_AND_PLACEHOLDERS: &[(&str, &[&str])] = &[
|
|||||||
// In-app `help` — framing + per-command entries keyed by
|
// In-app `help` — framing + per-command entries keyed by
|
||||||
// each CommandNode's `help_id` (ADR-0024 §help_id).
|
// each CommandNode's `help_id` (ADR-0024 §help_id).
|
||||||
("help.intro", &[]),
|
("help.intro", &[]),
|
||||||
("help.dsl_section", &[]),
|
("help.simple_section", &[]),
|
||||||
|
("help.advanced_section", &[]),
|
||||||
("help.types_reference", &[]),
|
("help.types_reference", &[]),
|
||||||
("help.detail_hint", &[]),
|
("help.detail_hint", &[]),
|
||||||
("help.unknown_topic", &["topic"]),
|
("help.unknown_topic", &["topic"]),
|
||||||
@@ -223,6 +224,13 @@ pub const KEYS_AND_PLACEHOLDERS: &[(&str, &[&str])] = &[
|
|||||||
("help.data.delete", &[]),
|
("help.data.delete", &[]),
|
||||||
("help.data.replay", &[]),
|
("help.data.replay", &[]),
|
||||||
("help.data.explain", &[]),
|
("help.data.explain", &[]),
|
||||||
|
// Issue #36: advanced-mode (SQL) help pages.
|
||||||
|
("help.data.select", &[]),
|
||||||
|
("help.data.with", &[]),
|
||||||
|
("help.data.sql_insert", &[]),
|
||||||
|
("help.data.sql_update", &[]),
|
||||||
|
("help.data.sql_delete", &[]),
|
||||||
|
("help.data.explain_sql", &[]),
|
||||||
// ---- Hint panel ambient typing assistance (ADR-0022 §6) ----
|
// ---- Hint panel ambient typing assistance (ADR-0022 §6) ----
|
||||||
("hint.ambient_complete", &[]),
|
("hint.ambient_complete", &[]),
|
||||||
("hint.ambient_error_with_usage", &["message", "usage"]),
|
("hint.ambient_error_with_usage", &["message", "usage"]),
|
||||||
|
|||||||
@@ -246,7 +246,13 @@ help:
|
|||||||
# are multi-line-capable — the renderer emits one output row
|
# are multi-line-capable — the renderer emits one output row
|
||||||
# per line so scroll math stays accurate.
|
# per line so scroll math stays accurate.
|
||||||
intro: "Supported commands:"
|
intro: "Supported commands:"
|
||||||
dsl_section: "DSL data commands (in simple mode):"
|
# Issue #36: the command list groups by mode. App-lifecycle commands list
|
||||||
|
# first (unlabelled, under the intro — they work in either mode); the rest
|
||||||
|
# split into these two sections by command category. (Replaces the old
|
||||||
|
# single "DSL data commands (in simple mode):" header, which used the banned
|
||||||
|
# "DSL" term and mis-labelled the advanced SQL forms it already contained.)
|
||||||
|
simple_section: "Simple-mode commands:"
|
||||||
|
advanced_section: "Advanced-mode (SQL) commands:"
|
||||||
# H3: footer on the full `help` list, and the not-found note
|
# H3: footer on the full `help` list, and the not-found note
|
||||||
# for `help <topic>`. `{topic}` is the word the user typed.
|
# for `help <topic>`. `{topic}` is the word the user typed.
|
||||||
detail_hint: "Type `help <command>` for detail on one command (e.g. `help insert`), or `help types` for the type reference."
|
detail_hint: "Type `help <command>` for detail on one command (e.g. `help insert`), or `help types` for the type reference."
|
||||||
@@ -368,8 +374,25 @@ help:
|
|||||||
explain show data <T> | explain update <T> ... | explain delete from <T> ...
|
explain show data <T> | explain update <T> ... | explain delete from <T> ...
|
||||||
— show how the database would run a query, without
|
— show how the database would run a query, without
|
||||||
running it (safe even for update / delete)
|
running it (safe even for update / delete)
|
||||||
explain <select|with|insert|update|delete …> (advanced mode)
|
# Issue #36: advanced-mode (SQL) forms. Each has its own help page, listed
|
||||||
— the same plan for the SQL you wrote
|
# under the "Advanced-mode (SQL) commands:" section and shown by
|
||||||
|
# `help <topic>` alongside its simple-mode sibling — so `help insert` shows
|
||||||
|
# both the simple form and `sql_insert` (like `help create` already does).
|
||||||
|
select: |-
|
||||||
|
select <cols> | * from <T> [where …] [group by …] [order by …] [limit n]
|
||||||
|
— query rows (advanced SQL)
|
||||||
|
with: |-
|
||||||
|
with <name> as (<select>) [, …] <select> — query through a named
|
||||||
|
sub-query / CTE (advanced SQL)
|
||||||
|
sql_insert: |-
|
||||||
|
insert into <T> (col, …) values (val, …) — add a row (advanced SQL)
|
||||||
|
sql_update: |-
|
||||||
|
update <T> set <col> = <val>, … where <expr> — change matching rows (advanced SQL)
|
||||||
|
sql_delete: |-
|
||||||
|
delete from <T> where <expr> — remove matching rows (advanced SQL)
|
||||||
|
explain_sql: |-
|
||||||
|
explain <select|with|insert|update|delete …> — show the plan for a SQL statement,
|
||||||
|
without running it (advanced SQL)
|
||||||
# Type reference, appended after the command list.
|
# Type reference, appended after the command list.
|
||||||
types_reference: |
|
types_reference: |
|
||||||
Types: text, int, real, decimal, bool, date, datetime, blob, serial, shortid
|
Types: text, int, real, decimal, bool, date, datetime, blob, serial, shortid
|
||||||
|
|||||||
@@ -115,6 +115,80 @@ fn help_create_covers_every_form_sharing_the_entry_word() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----- issue #36: advanced-mode SQL forms get distinct help content -----
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn help_select_renders_the_sql_select_block() {
|
||||||
|
// `select` is advanced-only (no simple sibling) and used to have
|
||||||
|
// `help_id: None`, so `help select` produced the unknown-topic note.
|
||||||
|
// It now carries its own help page.
|
||||||
|
let out = output_for("help select");
|
||||||
|
let joined = out.join("\n").to_lowercase();
|
||||||
|
assert!(
|
||||||
|
joined.contains("select") && joined.contains("from"),
|
||||||
|
"help select shows the SQL select form: {out:?}",
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!out.iter().any(|l| l.contains("No help for")),
|
||||||
|
"help select resolves to content, not the unknown-topic note: {out:?}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn help_with_renders_the_cte_block() {
|
||||||
|
let out = output_for("help with");
|
||||||
|
let joined = out.join("\n").to_lowercase();
|
||||||
|
assert!(
|
||||||
|
joined.contains("with") && joined.contains("as ("),
|
||||||
|
"help with shows the CTE form: {out:?}",
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!out.iter().any(|l| l.contains("No help for")),
|
||||||
|
"help with resolves to content: {out:?}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn help_insert_shows_both_simple_and_sql_forms() {
|
||||||
|
// `help insert` now covers the simple form AND the advanced SQL form
|
||||||
|
// (two clearly-labelled blocks, like `help create` already does).
|
||||||
|
let out = output_for("help insert");
|
||||||
|
let joined = out.join("\n").to_lowercase();
|
||||||
|
assert!(
|
||||||
|
joined.contains("insert into"),
|
||||||
|
"simple insert form shown: {out:?}",
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
joined.contains("advanced"),
|
||||||
|
"advanced SQL insert form shown alongside the simple one: {out:?}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn help_list_splits_simple_and_advanced_sections() {
|
||||||
|
let out = output_for("help");
|
||||||
|
let joined = out.join("\n");
|
||||||
|
assert!(
|
||||||
|
out.iter().any(|l| l.contains("Simple-mode commands")),
|
||||||
|
"simple-mode section header present: {out:?}",
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
out.iter()
|
||||||
|
.any(|l| l.contains("Advanced-mode") && l.contains("SQL")),
|
||||||
|
"advanced-mode (SQL) section header present: {out:?}",
|
||||||
|
);
|
||||||
|
// Copy rule: never say "DSL" in user-facing text (the old header did).
|
||||||
|
assert!(
|
||||||
|
!joined.contains("DSL"),
|
||||||
|
"help output must not contain 'DSL': {out:?}",
|
||||||
|
);
|
||||||
|
// The advanced query commands are now discoverable in the list.
|
||||||
|
assert!(
|
||||||
|
joined.to_lowercase().contains("select"),
|
||||||
|
"select is listed in help: {out:?}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn help_types_renders_the_type_reference() {
|
fn help_types_renders_the_type_reference() {
|
||||||
let out = output_for("help types");
|
let out = output_for("help types");
|
||||||
|
|||||||
Reference in New Issue
Block a user