hint: show the matching usage template for multi-form commands
A parse error in `add index …` showed the `add column` usage: `add` and `drop` are multi-form commands, and both the ambient hint and the submit-time usage block picked the first-listed form unconditionally. New `grammar::usage_key_for_input` disambiguates by the form word after the entry keyword — `column` / `index` / `table` / `relationship`, or the leading digit of `add 1:n …`. The ambient hint now shows that one form; `render_usage_block` shows the committed form's usage and falls back to the whole family only for a bare `add` / `drop` with no form chosen.
This commit is contained in:
@@ -414,6 +414,42 @@ pub fn usage_keys_for_input(source: &str) -> Option<(&'static str, &'static [&'s
|
||||
Some((node.entry.primary, node.usage_ids))
|
||||
}
|
||||
|
||||
/// The single usage template most relevant to `source`, when
|
||||
/// one is determinable.
|
||||
///
|
||||
/// A single-form command resolves to its one usage key. A
|
||||
/// multi-form command (`add`, `drop`) disambiguates by the
|
||||
/// form word after the entry keyword — so a parse error in
|
||||
/// `add index …` resolves to the `add index` usage rather than
|
||||
/// the first-listed `add column`. Returns `None` for a bare
|
||||
/// multi-form entry word (`add` with nothing after it), where
|
||||
/// no form has been chosen — the caller decides whether to
|
||||
/// show the whole family or nothing.
|
||||
#[must_use]
|
||||
pub fn usage_key_for_input(source: &str) -> Option<&'static str> {
|
||||
use crate::dsl::walker::lex_helpers::{consume_ident, skip_whitespace};
|
||||
let (_entry, keys) = usage_keys_for_input(source)?;
|
||||
let first = *keys.first()?;
|
||||
if keys.len() == 1 {
|
||||
return Some(first);
|
||||
}
|
||||
// Multi-form: the form is named by the token right after
|
||||
// the entry keyword.
|
||||
let start = skip_whitespace(source, 0);
|
||||
let (_, entry_end) = consume_ident(source, start)?;
|
||||
let after = skip_whitespace(source, entry_end);
|
||||
// The `add 1:n relationship` form opens with a digit.
|
||||
if source.as_bytes().get(after).is_some_and(u8::is_ascii_digit) {
|
||||
return keys.iter().copied().find(|k| k.ends_with("relationship"));
|
||||
}
|
||||
// Otherwise the form word is an identifier — `column`,
|
||||
// `index`, `table`, `relationship` — matched against the
|
||||
// usage key's suffix.
|
||||
let (s, e) = consume_ident(source, after)?;
|
||||
let form = source[s..e].to_ascii_lowercase();
|
||||
keys.iter().copied().find(|k| k.ends_with(form.as_str()))
|
||||
}
|
||||
|
||||
/// Every command-entry word in the registry, sorted alphabetically
|
||||
/// by primary literal. Replaces `dsl::usage::entry_keywords_alphabetised`
|
||||
/// which read the same data through the legacy `usage::REGISTRY`.
|
||||
|
||||
Reference in New Issue
Block a user