fix(hint): labelled tier-3 block format + snapshot (ADR-0053 /runda)
Final /runda found the rendered block was three bare unlabelled lines, deviating from the approved exemplar format. Fix: - emit_tier3_block now renders a `Hint` heading + aligned `What:` / `Example:` / `Concept:` lines (hint.block.* labels); concept stays muted - lock the format with an insta snapshot (hint_block_insert) - amend ADR-0053 D2/D4 + exemplars: drop the `Next:` line (tier-2 ambient already owns live position-awareness — user-confirmed), align exemplars to the shipped format 2499 pass / 1 ignored, clippy clean.
This commit is contained in:
@@ -113,7 +113,7 @@ into the output journal. (It must therefore be handled in `handle_key`
|
|||||||
|
|
||||||
| Trigger | Buffer / state | Result |
|
| Trigger | Buffer / state | Result |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **F1** | non-empty input | tier-3 hint for the command being typed, plus the live "expected next" (from the walker's `tail_expected` / parser `expected`) |
|
| **F1** | non-empty input | tier-3 hint for the command being typed. (No "expected next" line — the always-on tier-2 ambient panel already shows it live; tier-2 owns position-awareness.) |
|
||||||
| **F1** | empty input, a recent error exists | tier-3 expansion of that error |
|
| **F1** | empty input, a recent error exists | tier-3 expansion of that error |
|
||||||
| **F1** | empty input, no recent error | a short "getting started" pointer (press F1 while typing a command; `help` for the full list) |
|
| **F1** | empty input, no recent error | a short "getting started" pointer (press F1 while typing a command; `help` for the full list) |
|
||||||
| **`hint`** (submitted) | a recent error exists | tier-3 expansion of that error (primary use) |
|
| **`hint`** (submitted) | a recent error exists | tier-3 expansion of that error (primary use) |
|
||||||
@@ -205,14 +205,17 @@ mechanics (e.g. `quit`); `what` + `example` are always present.
|
|||||||
|
|
||||||
### D4 — Rendering
|
### D4 — Rendering
|
||||||
|
|
||||||
Both surfaces render through one new renderer, `App::note_hint*` (sibling
|
Both surfaces render through the `App::note_hint*` family (sibling of
|
||||||
of `note_help`/`note_help_topic`, `src/app.rs`), emitting a small framed
|
`note_help`/`note_help_topic`, `src/app.rs`) via `emit_tier3_block`,
|
||||||
block into the `output` buffer as `OutputKind::System` with
|
emitting into the `output` buffer as `OutputKind::System`: a **`Hint`
|
||||||
`OutputStyleClass::Hint` on the `what`/`concept` prose and `Neutral` on
|
heading** followed by aligned **`What:` / `Example:` / `Concept:`** lines
|
||||||
the `example` line. The block is **persistent** (scrolls in the journal),
|
(labels + heading from `hint.block.*`). The `concept` line is muted
|
||||||
unlike the transient ambient panel — pressing F1 is an explicit request
|
(`OutputStyleClass::Hint`); the rest are plain. The block is
|
||||||
to *keep* the deeper guidance on screen. The bottom keybinding strip
|
**persistent** (scrolls in the journal), unlike the transient ambient
|
||||||
(ADR-0051) advertises F1 in the editing/typing state.
|
panel — pressing F1 is an explicit request to *keep* the deeper guidance
|
||||||
|
on screen. Its rendered shape is locked by an `insta` snapshot
|
||||||
|
(`hint_block_insert`). The bottom keybinding strip (ADR-0051) advertises
|
||||||
|
F1 in the editing (leading) and default states.
|
||||||
|
|
||||||
### D5 — "Most recent (runtime) error" state
|
### D5 — "Most recent (runtime) error" state
|
||||||
|
|
||||||
@@ -278,32 +281,31 @@ maintainer owns, content is produced in two stages:
|
|||||||
**reviewable batches** (grouped by area: DDL, DML, app commands,
|
**reviewable batches** (grouped by area: DDL, DML, app commands,
|
||||||
error classes), not one monolithic drop.
|
error classes), not one monolithic drop.
|
||||||
|
|
||||||
### Exemplars (the style reference to approve)
|
### Exemplars (the style reference; shipped as the rendered format)
|
||||||
|
|
||||||
**Command (F1 live-input), `insert`:**
|
**Command (F1 live-input), `insert`** (the rendered shape, locked by the
|
||||||
|
`hint_block_insert` snapshot — a `Hint` heading + aligned labels, no
|
||||||
|
`Next:` line since tier-2 owns position-awareness):
|
||||||
|
|
||||||
```
|
```
|
||||||
Hint — insert
|
Hint
|
||||||
What: Add one or more rows to a table.
|
What: Add one or more rows to a table.
|
||||||
Example: insert into Customers values ('Ann', 'ann@x.io')
|
Example: insert into Customers values ('Ann', 'ann@example.io')
|
||||||
Concept: A row is one record; each value lines up with a column, in
|
Concept: A row is one record; each value lines up with a column, in
|
||||||
order. Columns typed serial/shortid fill themselves — leave
|
order. Columns typed serial/shortid fill themselves — leave
|
||||||
them out.
|
them out.
|
||||||
Next: a value list `(...)`, or `(col, ...) values (...)` to name columns
|
|
||||||
```
|
```
|
||||||
(The "Next:" line is the live expected-set from the walker, shown only on
|
|
||||||
the non-empty-input F1 path.)
|
|
||||||
|
|
||||||
**Error (`hint` command), foreign-key child-side violation:**
|
**Error (`hint` command), foreign-key child-side violation:**
|
||||||
|
|
||||||
```
|
```
|
||||||
Hint — no parent row to point at
|
Hint
|
||||||
What: The value you inserted into Orders.customer_id doesn't match
|
What: The value you gave for the child column doesn't match any
|
||||||
any Customers row, so the foreign key has nothing to point at.
|
parent row, so the foreign key has nothing to point at.
|
||||||
Example: First insert into Customers values ('Ann', ...)
|
Example: First insert the parent (insert into Customers …), then the
|
||||||
Then insert into Orders values (..., 'Ann')
|
child that references it.
|
||||||
Concept: A foreign key is a promise that every child points at a real
|
Concept: A foreign key is a promise that every child points at a real
|
||||||
parent. The parent must exist first. To allow orphans on
|
parent, so the parent must exist first. To allow orphans on
|
||||||
delete instead, set the relationship's `on delete` to
|
delete instead, set the relationship's `on delete` to
|
||||||
`set null` or `cascade`.
|
`set null` or `cascade`.
|
||||||
```
|
```
|
||||||
@@ -311,7 +313,7 @@ Hint — no parent row to point at
|
|||||||
**Command (F1 live-input), `add 1:n relationship`:**
|
**Command (F1 live-input), `add 1:n relationship`:**
|
||||||
|
|
||||||
```
|
```
|
||||||
Hint — add relationship
|
Hint
|
||||||
What: Link two tables so a parent row can own many child rows.
|
What: Link two tables so a parent row can own many child rows.
|
||||||
Example: add 1:n relationship from Customers.id to Orders.customer_id
|
Example: add 1:n relationship from Customers.id to Orders.customer_id
|
||||||
Concept: The "1:n" means one parent, many children. The child column
|
Concept: The "1:n" means one parent, many children. The child column
|
||||||
|
|||||||
+37
-6
@@ -3205,17 +3205,32 @@ impl App {
|
|||||||
/// polish (the framed block) lands with the corpus.
|
/// polish (the framed block) lands with the corpus.
|
||||||
fn emit_tier3_block(&mut self, stem: &str) -> bool {
|
fn emit_tier3_block(&mut self, stem: &str) -> bool {
|
||||||
let cat = crate::friendly::catalog();
|
let cat = crate::friendly::catalog();
|
||||||
if cat.get(&format!("{stem}.what")).is_none() {
|
let what_key = format!("{stem}.what");
|
||||||
|
if cat.get(&what_key).is_none() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
self.note_system(crate::friendly::translate(&format!("{stem}.what"), &[]));
|
// Labelled block (ADR-0053 D4): a `Hint` heading, then aligned
|
||||||
|
// `What:` / `Example:` / `Concept:` lines. `concept` renders
|
||||||
|
// muted (`OutputStyleClass::Hint`); the rest are plain system.
|
||||||
|
let labelled = |label: &str, value: &str| {
|
||||||
|
// Pad `<Label>:` to a common width so the values align.
|
||||||
|
format!(" {:<9}{value}", format!("{label}:"))
|
||||||
|
};
|
||||||
|
self.note_system(crate::t!("hint.block.heading"));
|
||||||
|
self.note_system(labelled(
|
||||||
|
&crate::t!("hint.block.what"),
|
||||||
|
&crate::friendly::translate(&what_key, &[]),
|
||||||
|
));
|
||||||
if cat.get(&format!("{stem}.example")).is_some() {
|
if cat.get(&format!("{stem}.example")).is_some() {
|
||||||
self.note_system(crate::friendly::translate(&format!("{stem}.example"), &[]));
|
self.note_system(labelled(
|
||||||
|
&crate::t!("hint.block.example"),
|
||||||
|
&crate::friendly::translate(&format!("{stem}.example"), &[]),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if cat.get(&format!("{stem}.concept")).is_some() {
|
if cat.get(&format!("{stem}.concept")).is_some() {
|
||||||
self.push_category_three_prose(crate::friendly::translate(
|
self.push_category_three_prose(labelled(
|
||||||
&format!("{stem}.concept"),
|
&crate::t!("hint.block.concept"),
|
||||||
&[],
|
&crate::friendly::translate(&format!("{stem}.concept"), &[]),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@@ -5813,6 +5828,22 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Locks the rendered tier-3 block format (ADR-0053 D4): a `Hint`
|
||||||
|
/// heading + aligned `What:` / `Example:` / `Concept:` lines.
|
||||||
|
#[test]
|
||||||
|
fn insert_hint_block_renders_in_the_labelled_format() {
|
||||||
|
let mut app = App::new();
|
||||||
|
type_str(&mut app, "insert into Customers ");
|
||||||
|
f1(&mut app);
|
||||||
|
let block = app
|
||||||
|
.output
|
||||||
|
.iter()
|
||||||
|
.map(|l| l.text.as_str())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n");
|
||||||
|
insta::assert_snapshot!("hint_block_insert", block);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn f1_on_add_relationship_renders_the_relationship_block() {
|
fn f1_on_add_relationship_renders_the_relationship_block() {
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
|
|||||||
@@ -224,6 +224,10 @@ pub const KEYS_AND_PLACEHOLDERS: &[(&str, &[&str])] = &[
|
|||||||
),
|
),
|
||||||
("hint.ambient_expected", &["expected"]),
|
("hint.ambient_expected", &["expected"]),
|
||||||
("hint.getting_started", &[]),
|
("hint.getting_started", &[]),
|
||||||
|
("hint.block.heading", &[]),
|
||||||
|
("hint.block.what", &[]),
|
||||||
|
("hint.block.example", &[]),
|
||||||
|
("hint.block.concept", &[]),
|
||||||
// Tier-3 teaching blocks (ADR-0053 D3) — Phase-B exemplars.
|
// Tier-3 teaching blocks (ADR-0053 D3) — Phase-B exemplars.
|
||||||
("hint.cmd.insert.what", &[]),
|
("hint.cmd.insert.what", &[]),
|
||||||
("hint.cmd.insert.example", &[]),
|
("hint.cmd.insert.example", &[]),
|
||||||
|
|||||||
@@ -391,6 +391,13 @@ hint:
|
|||||||
# H2 / ADR-0053: shown by `hint` / F1 when there is nothing specific
|
# H2 / ADR-0053: shown by `hint` / F1 when there is nothing specific
|
||||||
# to expand on (no recent error, empty input).
|
# to expand on (no recent error, empty input).
|
||||||
getting_started: "Start typing a command and press F1 for a hint, or type `help` for the full command list."
|
getting_started: "Start typing a command and press F1 for a hint, or type `help` for the full command list."
|
||||||
|
# Tier-3 block scaffolding (ADR-0053 D4): the heading + the labels the
|
||||||
|
# `what` / `example` / `concept` parts render under.
|
||||||
|
block:
|
||||||
|
heading: "Hint"
|
||||||
|
what: "What"
|
||||||
|
example: "Example"
|
||||||
|
concept: "Concept"
|
||||||
# ── Tier-3 teaching blocks (ADR-0053 D3) ──────────────────────────
|
# ── Tier-3 teaching blocks (ADR-0053 D3) ──────────────────────────
|
||||||
# Per-form command hints (`hint.cmd.<form>`) and per-class error
|
# Per-form command hints (`hint.cmd.<form>`) and per-class error
|
||||||
# hints (`hint.err.<class>`), each a `what` (1–2 sentences) / `example`
|
# hints (`hint.err.<class>`), each a `what` (1–2 sentences) / `example`
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
source: src/app.rs
|
||||||
|
assertion_line: 5844
|
||||||
|
expression: block
|
||||||
|
---
|
||||||
|
Hint
|
||||||
|
What: Add one or more rows to a table.
|
||||||
|
Example: insert into Customers values ('Ann', 'ann@example.io')
|
||||||
|
Concept: A row is one record; each value lines up with a column, in order. Columns typed `serial`/`shortid` fill themselves — leave them out.
|
||||||
Reference in New Issue
Block a user