diff --git a/src/app.rs b/src/app.rs index 4ad235f..d124b2e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -5862,6 +5862,26 @@ mod tests { ); } + // ── Phase C batch 2: DDL hints render (incl. multi-form DROP) ── + + #[test] + fn f1_on_create_table_renders_its_hint_block() { + let mut app = App::new(); + type_str(&mut app, "create table Customers with pk id(serial)"); + f1(&mut app); + assert!(output_contains(&app, "Create a new table")); + } + + #[test] + fn f1_disambiguates_drop_forms() { + let mut app = App::new(); + type_str(&mut app, "drop index idx_email"); + f1(&mut app); + // Resolves drop_index, not drop_table/column/etc. + assert!(output_contains(&app, "Remove an index by name")); + assert!(!output_contains(&app, "Remove a table")); + } + #[test] fn messages_command_toggles_verbosity_and_reports() { let mut app = App::new(); diff --git a/src/dsl/grammar/ddl.rs b/src/dsl/grammar/ddl.rs index 69e7774..d1de91c 100644 --- a/src/dsl/grammar/ddl.rs +++ b/src/dsl/grammar/ddl.rs @@ -968,7 +968,13 @@ pub static DROP: CommandNode = CommandNode { shape: DROP_SHAPE, ast_builder: build_drop, help_id: Some("ddl.drop"), - hint_ids: &[], + hint_ids: &[ + "drop_table", + "drop_column", + "drop_relationship", + "drop_index", + "drop_constraint", + ], usage_ids: &[ "parse.usage.drop_table", "parse.usage.drop_column", @@ -1004,7 +1010,7 @@ pub static RENAME: CommandNode = CommandNode { shape: RENAME_COLUMN, ast_builder: build_rename_column, help_id: Some("ddl.rename"), - hint_ids: &[], + hint_ids: &["rename_column"], usage_ids: &["parse.usage.rename_column"],}; pub static CHANGE: CommandNode = CommandNode { @@ -1012,7 +1018,7 @@ pub static CHANGE: CommandNode = CommandNode { shape: CHANGE_COLUMN, ast_builder: build_change_column, help_id: Some("ddl.change"), - hint_ids: &[], + hint_ids: &["change_column"], usage_ids: &["parse.usage.change_column"],}; // ================================================================= @@ -1373,7 +1379,7 @@ pub static CREATE: CommandNode = CommandNode { shape: CREATE_TABLE, ast_builder: build_create_table, help_id: Some("ddl.create"), - hint_ids: &[], + hint_ids: &["create_table"], usage_ids: &["parse.usage.create_table"],}; // ================================================================= @@ -1442,7 +1448,7 @@ pub static CREATE_M2N: CommandNode = CommandNode { shape: CREATE_M2N_SHAPE, ast_builder: build_create_m2n, help_id: Some("ddl.create_m2n"), - hint_ids: &[], + hint_ids: &["create_m2n"], usage_ids: &["parse.usage.create_m2n"], }; diff --git a/src/dsl/grammar/mod.rs b/src/dsl/grammar/mod.rs index 4ba3c1e..1bcdc36 100644 --- a/src/dsl/grammar/mod.rs +++ b/src/dsl/grammar/mod.rs @@ -917,9 +917,12 @@ mod hint_key_tests { hint_key_for_input_in_mode("insert into T values (1)", Mode::Simple), Some("insert") ); - // A node with no hint_ids yet → None (tier-2 fallback). - assert_eq!(hint_key_for_input_in_mode("drop table T", Mode::Simple), None); - // Unknown entry word → None. + // Multi-form DROP disambiguates to the typed form too. + assert_eq!( + hint_key_for_input_in_mode("drop table T", Mode::Simple), + Some("drop_table") + ); + // Unknown entry word → None (tier-2 fallback). assert_eq!(hint_key_for_input_in_mode("zzz", Mode::Simple), None); } } diff --git a/src/friendly/keys.rs b/src/friendly/keys.rs index 3edaaf3..c27c436 100644 --- a/src/friendly/keys.rs +++ b/src/friendly/keys.rs @@ -269,6 +269,43 @@ pub const KEYS_AND_PLACEHOLDERS: &[(&str, &[&str])] = &[ ("hint.cmd.redo.example", &[]), ("hint.cmd.copy.what", &[]), ("hint.cmd.copy.example", &[]), + // Phase C batch 2 — DDL command hints. + ("hint.cmd.create_table.what", &[]), + ("hint.cmd.create_table.example", &[]), + ("hint.cmd.create_table.concept", &[]), + ("hint.cmd.create_m2n.what", &[]), + ("hint.cmd.create_m2n.example", &[]), + ("hint.cmd.create_m2n.concept", &[]), + ("hint.cmd.add_column.what", &[]), + ("hint.cmd.add_column.example", &[]), + ("hint.cmd.add_column.concept", &[]), + ("hint.cmd.add_index.what", &[]), + ("hint.cmd.add_index.example", &[]), + ("hint.cmd.add_index.concept", &[]), + ("hint.cmd.add_constraint.what", &[]), + ("hint.cmd.add_constraint.example", &[]), + ("hint.cmd.add_constraint.concept", &[]), + ("hint.cmd.drop_table.what", &[]), + ("hint.cmd.drop_table.example", &[]), + ("hint.cmd.drop_table.concept", &[]), + ("hint.cmd.drop_column.what", &[]), + ("hint.cmd.drop_column.example", &[]), + ("hint.cmd.drop_column.concept", &[]), + ("hint.cmd.drop_relationship.what", &[]), + ("hint.cmd.drop_relationship.example", &[]), + ("hint.cmd.drop_relationship.concept", &[]), + ("hint.cmd.drop_index.what", &[]), + ("hint.cmd.drop_index.example", &[]), + ("hint.cmd.drop_index.concept", &[]), + ("hint.cmd.drop_constraint.what", &[]), + ("hint.cmd.drop_constraint.example", &[]), + ("hint.cmd.drop_constraint.concept", &[]), + ("hint.cmd.rename_column.what", &[]), + ("hint.cmd.rename_column.example", &[]), + ("hint.cmd.rename_column.concept", &[]), + ("hint.cmd.change_column.what", &[]), + ("hint.cmd.change_column.example", &[]), + ("hint.cmd.change_column.concept", &[]), ( "hint.ambient_invalid_ident", &["kind", "found"], diff --git a/src/friendly/strings/en-US.yaml b/src/friendly/strings/en-US.yaml index c785a81..9639ccb 100644 --- a/src/friendly/strings/en-US.yaml +++ b/src/friendly/strings/en-US.yaml @@ -456,6 +456,55 @@ hint: copy: what: "Copy the output panel to the clipboard — all of it, or just the last command's output." example: "copy last" + # DDL — schema-shaping commands (Phase C batch 2). + create_table: + what: "Create a new table — its columns, their types, and a primary key." + example: "create table Customers with pk id(serial), name(text), email(text)" + concept: "A table is a set of rows that share the same columns. The primary key uniquely identifies each row; a `serial` key numbers the rows for you." + create_m2n: + what: "Create a junction table linking two tables many-to-many." + example: "create m:n relationship from Students to Courses" + concept: "A many-to-many link (a student takes many courses; a course has many students) can't live in either table, so it gets its own junction table holding a foreign key to each side." + add_column: + what: "Add a new column to an existing table." + example: "add column Customers: phone (text)" + concept: "Existing rows take the column's default, or null. A `not null` column with no default can't be added to a table that already has rows — there'd be nothing to put in them." + add_index: + what: "Create an index on one or more columns to speed up lookups." + example: "add index as idx_email on Customers (email)" + concept: "An index is a sorted side-structure that makes a lookup like `where email = …` fast, at the cost of a little space and slightly slower writes." + add_constraint: + what: "Add a constraint — not null, unique, default, or check — to an existing column." + example: "add constraint not null to Customers.email" + concept: "A constraint is a rule the database enforces on every row. Adding one fails if existing rows already break it, so you fix the data first." + drop_table: + what: "Remove a table and all of its rows." + example: "drop table Customers" + concept: "If other tables reference this one through a relationship, drop those relationships (or their child rows) first — the database won't orphan them." + drop_column: + what: "Remove a column from a table." + example: "drop column Customers: phone" + concept: "The column's values are lost. You can't drop a primary-key column, or one a relationship depends on." + drop_relationship: + what: "Remove a relationship between two tables." + example: "drop relationship customer_orders" + concept: "This drops the foreign-key link and stops the database enforcing it; the tables and their rows stay. The foreign-key column itself remains unless you also drop it." + drop_index: + what: "Remove an index by name." + example: "drop index idx_email" + concept: "Only the lookup shortcut goes — the data is untouched. Queries still work, just without that speed-up." + drop_constraint: + what: "Remove a constraint from a column." + example: "drop constraint not null from Customers.email" + concept: "The rule stops being enforced from now on; rows already stored are left as they are." + rename_column: + what: "Rename a column, keeping its values and type." + example: "rename column Customers: email to contact_email" + concept: "Only the name changes — the stored data is the same. References to the column are reconciled so nothing breaks." + change_column: + what: "Change a column's type, converting the existing values." + example: "change column Customers: status (int)" + concept: "The database converts each stored value to the new type; if a value can't convert it refuses the change, so you don't silently lose data. Flags let you force or skip the conversion." err: foreign_key: child_side: