diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-08-14 03:26:54 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-08-14 03:26:54 +0800 |
| commit | b138dff3517e8cb793d431af6ad4a577491e21b0 (patch) | |
| tree | 7ff88d23f76caf62f39dfd2a7e3285fc8e186e63 /mingling_core/src | |
| parent | a14b9bbb96e025a60abeb492ba043b0db806289a (diff) | |
docs(comp): improve documentation for completion module
Add detailed doc comments, examples, and usage notes across
the completion module including ShellContext, Suggest, and
related traits.
Diffstat (limited to 'mingling_core/src')
| -rw-r--r-- | mingling_core/src/comp.rs | 92 | ||||
| -rw-r--r-- | mingling_core/src/comp/comp_ctx.rs | 20 | ||||
| -rw-r--r-- | mingling_core/src/comp/flags.rs | 18 | ||||
| -rw-r--r-- | mingling_core/src/comp/shell_ctx.rs | 132 | ||||
| -rw-r--r-- | mingling_core/src/comp/suggest.rs | 420 |
5 files changed, 632 insertions, 50 deletions
diff --git a/mingling_core/src/comp.rs b/mingling_core/src/comp.rs index 30b165a..4315854 100644 --- a/mingling_core/src/comp.rs +++ b/mingling_core/src/comp.rs @@ -1,11 +1,21 @@ -// Doc Not Optimize +use crate::{ProgramCollect, debug, metadata::Description, this, trace}; + +use std::collections::BTreeSet; +use std::fmt::Display; + mod comp_ctx; mod flags; mod shell_ctx; mod suggest; -use std::collections::BTreeSet; -use std::fmt::Display; +#[doc(hidden)] +pub use flags::*; + +#[doc(hidden)] +pub use shell_ctx::*; + +#[doc(hidden)] +pub use suggest::*; /// Constant defining the name of the completion subcommand. /// @@ -15,42 +25,81 @@ use std::fmt::Display; /// /// This value is used internally by the completion system to intercept the /// command-line input and redirect to the completion handler. +/// +/// ``` +/// # #[cfg(feature = "comp")] { +/// # use mingling_core::COMPLETION_SUBCOMMAND; +/// assert_eq!("__comp", COMPLETION_SUBCOMMAND); +/// # } +/// ``` pub const COMPLETION_SUBCOMMAND: &str = "__comp"; -#[doc(hidden)] -pub use flags::*; -#[doc(hidden)] -pub use shell_ctx::*; -#[doc(hidden)] -pub use suggest::*; - -use crate::{ProgramCollect, debug, metadata::Description, this, trace}; - #[cfg(feature = "debug")] use crate::debug::init_env_logger; #[cfg(not(feature = "dispatch_tree"))] use crate::ChainProcess; + #[cfg(not(feature = "dispatch_tree"))] use crate::exec::match_user_input; -/// Trait for implementing completion logic. +/// Mingling Completion Entry Point +/// +/// Defines the custom completion logic entry point for the program's shell +/// completion system. +/// +/// When a specific command node is matched, the `comp` method is called to +/// generate completion suggestions based on the current shell context. Types +/// implementing this trait are usually automatically generated by the +/// [`dispatcher!`](https://docs.rs/mingling/latest/mingling/macros/macro.dispatcher.html) +/// macro; users typically do not need to implement this trait manually. +/// +/// # Manual impl /// -/// This trait defines the interface for generating command-line completions. -/// Types implementing this trait can provide custom completion suggestions -/// based on the current shell context. +/// If you need to implement it manually, follow the example below: +/// +/// ``` +/// # #[cfg(feature = "comp")] { +/// # use mingling_core::{Completion, CompletionHelper, ShellContext, Suggest}; +/// struct GreetCompletion; +/// struct EntryGreet; +/// +/// impl Completion for GreetCompletion { +/// type Previous = EntryGreet; +/// +/// fn comp(ctx: &ShellContext) -> Suggest { +/// // Generate completion suggestions based on the shell context +/// # Suggest::FileCompletion +/// } +/// } +/// # } +/// ``` pub trait Completion { - /// The entry point type that the completion functionality will act on. + /// The previous type bound to this entry in the completion chain. /// - /// It marks the **previous** type, which typically represents an `EntryXXX` type - /// (unless you have specific requirements). + /// It is usually the first type generated by + /// [`Dispatcher`](https://docs.rs/mingling/latest/mingling/trait.Dispatcher.html). type Previous; /// Generates completion suggestions based on the current shell context. /// /// This method is called when the completion system needs to provide - /// custom suggestions for the current command or argument. Implementations - /// should use the provided [`ShellContext`] to determine what to suggest. + /// custom suggestions for the current command or arguments. Implementors + /// should decide what to suggest based on the provided [`ShellContext`]. + /// + /// # Parameters + /// + /// * `ctx` — The current shell context information, including the command + /// line content, cursor position, current word, previous word, etc., + /// used to determine what should be completed. + /// + /// # Returns + /// + /// Returns a [`Suggest`] enum value, which can be: + /// - [`Suggest::Suggest`] carrying a set of candidate suggestion items + /// (`BTreeSet<SuggestItem>`), where each item may include a description. + /// - [`Suggest::FileCompletion`] instructs the shell to fall back to + /// filesystem completion. fn comp(ctx: &ShellContext) -> Suggest; } @@ -81,6 +130,7 @@ pub trait CompletionEntry { /// the current shell context and rendering the resulting suggestions in a /// format appropriate for the target shell. pub struct CompletionHelper; + impl CompletionHelper { /// Executes the completion logic for the given program type (`P`). /// diff --git a/mingling_core/src/comp/comp_ctx.rs b/mingling_core/src/comp/comp_ctx.rs index f2b83e6..04ef853 100644 --- a/mingling_core/src/comp/comp_ctx.rs +++ b/mingling_core/src/comp/comp_ctx.rs @@ -1,4 +1,3 @@ -// Doc Not Optimize use crate::{COMPLETION_SUBCOMMAND, Program, ProgramCollect}; impl<C> Program<C> @@ -11,6 +10,25 @@ where /// (defined by [`COMPLETION_SUBCOMMAND`]) appears among the parsed arguments. /// When `true`, the program should generate shell completions instead of /// running its normal execution path. + /// + /// # Returns + /// + /// `true` if the program is in completion mode, `false` otherwise. + /// + /// # Example + /// + /// ``` + /// # #[cfg(feature = "comp")] { + /// # use mingling_core::MockProgramCollect as ThisProgram; + /// use mingling_core::Program; + /// + /// let completing = Program::<ThisProgram>::new_with_args(["my_prog", "__comp"]).is_completing(); + /// assert!(completing); + /// + /// let not_completing = Program::<ThisProgram>::new_with_args(["my_prog", "run"]).is_completing(); + /// assert!(!not_completing); + /// # } + /// ``` #[must_use] pub fn is_completing(&self) -> bool { // Check if the first argument (args[1]) is the completion subcommand diff --git a/mingling_core/src/comp/flags.rs b/mingling_core/src/comp/flags.rs index 6285570..490c88b 100644 --- a/mingling_core/src/comp/flags.rs +++ b/mingling_core/src/comp/flags.rs @@ -1,10 +1,26 @@ -// Doc Not Optimize use just_fmt::snake_case; /// Represents the shell environment for which the output format is intended. /// /// This enum defines the supported shell types that can be used for /// generating shell-specific command syntax, scripts, or completions. +/// +/// # Behavior under `structural_renderer` feature +/// +/// When the `structural_renderer` feature is enabled, this enum derives +/// [`serde::Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html). +/// The serialization produces shell-specific string identifiers: +/// +/// - `Bash` serializes to `"bash"` +/// - `Zsh` serializes to `"zsh"` +/// - `Fish` serializes to `"fish"` +/// - `Powershell` serializes to `"powershell"` +/// - `Other(name)` serializes to the inner string value +/// +/// This allows the shell type to be transmitted as a plain string over +/// serialization boundaries (e.g., JSON, YAML) when using structural +/// rendering, while deserialization is handled by a separate process +/// (such as the `From<String>` implementation). #[derive(Default, Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "structural_renderer", derive(serde::Serialize))] pub enum ShellFlag { diff --git a/mingling_core/src/comp/shell_ctx.rs b/mingling_core/src/comp/shell_ctx.rs index 78ccb87..552e514 100644 --- a/mingling_core/src/comp/shell_ctx.rs +++ b/mingling_core/src/comp/shell_ctx.rs @@ -1,38 +1,142 @@ -// Doc Not Optimize #![allow(deprecated)] -use std::collections::HashSet; - use crate::{Flag, ShellFlag, Suggest, special_argument}; +use std::collections::HashSet; -/// Context passed from the shell to the completion system, -/// providing information about the current command line state -/// to guide how completions should be generated. +/// The shell context description for the current user. +/// +/// It records the state of the command line input when the user is using the +/// completion feature, allowing smart completion suggestions to be provided +/// to the user based on this state. +/// +/// # Note on `^` and `-` characters +/// +/// The completion scripts auto-generated by `mingling` replace any `-` +/// characters in the user's input with `^` when passing arguments to the +/// completion engine. This is an intentional tradeoff to avoid parsing +/// conflicts: since `-` is used both as a flag prefix (e.g., `-f`, `--help`) +/// and as a value separator in the command-line arguments consumed by +/// [`TryFrom`](std::convert::TryFrom), differentiating between them would +/// otherwise be ambiguous. By substituting `-` with `^`, the completion +/// engine can cleanly parse user-input values without confusing them for +/// flags. +/// +/// When reading fields like [`command_line`](ShellContext::command_line), +/// [`current_word`](ShellContext::current_word), +/// [`previous_word`](ShellContext::previous_word), or +/// [`command_name`](ShellContext::command_name), the `^` characters are +/// converted back to `-` so that the resulting context faithfully represents +/// what the user originally typed. +/// +/// # Behavior under `structural_renderer` feature +/// +/// When the `structural_renderer` feature is enabled, this struct derives +/// [`serde::Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html). +/// This allows the shell context to be serialized and transmitted over +/// serialization boundaries (e.g., JSON, YAML) as a structured representation +/// of the current shell input state. Each public field is serialized under +/// its own key, enabling consumers to reconstruct the exact command-line +/// state for rendering purposes. +/// +/// # Usage +/// +/// A [`ShellContext`] can be constructed from a vector of command-line +/// argument strings using [`TryFrom`](std::convert::TryFrom): +/// +/// ``` +/// # #[cfg(feature = "comp")] { +/// # use mingling_core::ShellContext; +/// let args = vec![ +/// "-f".to_string(), // --command-line +/// "git commit ^m 'test'".to_string(), +/// "-C".to_string(), // --cursor-position +/// "12".to_string(), +/// "-w".to_string(), // --current-word +/// "commit".to_string(), +/// "-p".to_string(), // --previous-word +/// "git".to_string(), +/// "-c".to_string(), // --command-name +/// "git".to_string(), +/// "-i".to_string(), // --word-index +/// "1".to_string(), +/// "-F".to_string(), // --shell-flag +/// "bash".to_string(), +/// ]; +/// +/// let context = ShellContext::try_from(args).unwrap(); +/// assert_eq!(context.command_line, "git commit -m 'test'"); +/// assert_eq!(context.cursor_position, 12); +/// assert_eq!(context.current_word, "commit"); +/// assert_eq!(context.previous_word, "git"); +/// assert_eq!(context.command_name, "git"); +/// assert_eq!(context.word_index, 1); +/// assert_eq!(context.all_words, vec!["git", "commit", "-m", "'test'"]); +/// # } +/// ``` +/// +/// When only partial information is available, the remaining fields default +/// to empty values: +/// +/// ``` +/// # #[cfg(feature = "comp")] { +/// # use mingling_core::ShellContext; +/// let args = vec![ +/// "-f".to_string(), +/// "ls ^la".to_string(), +/// "-C".to_string(), +/// "5".to_string(), +/// ]; +/// +/// let context = ShellContext::try_from(args).unwrap(); +/// assert_eq!(context.command_line, "ls -la"); +/// assert_eq!(context.cursor_position, 5); +/// # } +/// ``` +/// +/// If the `-F` flag is present without a value, `shell_flag` becomes +/// `ShellFlag::Other(String::new())`. If `-F` is absent, it defaults to +/// `ShellFlag::Other("unknown".to_string())`. #[derive(Default, Debug)] #[cfg_attr(feature = "structural_renderer", derive(serde::Serialize))] pub struct ShellContext { - /// The full command line (-f / --command-line) + /// The full command line + /// + /// Flag: [`-f`, `--command-line`] pub command_line: String, - /// Cursor position (-C / --cursor-position) + /// Cursor position + /// + /// Flag: [`-C`, `--cursor-position`] pub cursor_position: usize, - /// Current word (-w / --current-word) + /// Current word + /// + /// Flag: [`-w`, `--current-word`] pub current_word: String, - /// Previous word (-p / --previous-word) + /// Previous word + /// + /// Flag: [`-p`, `--previous-word`] pub previous_word: String, - /// Command name (-c / --command-name) + /// Command name + /// + /// Flag: [`-c`, `--command-name`] pub command_name: String, - /// Word index (-i / --word-index) + /// Word index + /// + /// Flag: [`-i`, `--word-index`] pub word_index: usize, - /// All words (-a / --all-words) + /// All words + /// + /// Flag: [`-a`, `--all-words`] pub all_words: Vec<String>, - /// Flag to indicate completion context (-F / --shell-flag) + /// Flag to indicate completion context + /// + /// Flag: [`-F`, `--shell-flag`] pub shell_flag: ShellFlag, } diff --git a/mingling_core/src/comp/suggest.rs b/mingling_core/src/comp/suggest.rs index 7760340..4a26c38 100644 --- a/mingling_core/src/comp/suggest.rs +++ b/mingling_core/src/comp/suggest.rs @@ -1,37 +1,77 @@ -// Doc Not Optimize #![allow(deprecated)] use std::collections::BTreeSet; use crate::ShellContext; -/// A completion suggestion that tells the shell how to perform completion. -/// This can be either a set of specific suggestion items or a request for file completion. +/// A completion suggestion that tells the shell how to perform command completion. +/// It can be a set of concrete suggestion items, or a file completion request. +/// +/// This enum has two variants: +/// - `Suggest(BTreeSet<SuggestItem>)`: Contains a set of concrete completion suggestion items that the shell displays for the user to choose from. +/// - `FileCompletion`: Requests the shell to perform file path completion (e.g., automatically completing filenames while typing a path). #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "structural_renderer", derive(serde::Serialize))] pub enum Suggest { - /// A set of specific suggestion items for the shell to display. + /// A set of concrete completion suggestion items for the shell to display to the user. + /// Each suggestion item can be a simple string or include a description. + /// Uses a `BTreeSet` to ensure suggestions are sorted by text order and contain no duplicates. Suggest(BTreeSet<SuggestItem>), - /// A request for the shell to perform file‑path completion. + /// Requests the shell to perform file path completion. + /// This is the default completion method, used when a command has no explicit completion rules. #[default] FileCompletion, } impl Suggest { /// Creates a new `Suggest` variant containing an empty `BTreeSet` of suggestions. + /// + /// # Returns + /// + /// Returns `Suggest::Suggest(BTreeSet::new())`, i.e., an empty suggestion set. + /// + /// # Example + /// + /// ``` + /// # #[cfg(feature = "comp")] { + /// # use mingling_core::Suggest; + /// let suggest = Suggest::new(); + /// assert_eq!(suggest, Suggest::Suggest(std::collections::BTreeSet::new())); + /// # } + /// ``` #[must_use] pub const fn new() -> Self { Self::Suggest(BTreeSet::new()) } /// Creates a `FileCompletion` variant. + /// + /// # Returns + /// + /// Returns `Suggest::FileCompletion`, requesting the shell to perform file path completion. + /// + /// # Example + /// + /// ``` + /// # #[cfg(feature = "comp")] { + /// # use mingling_core::Suggest; + /// let suggest = Suggest::file_comp(); + /// assert_eq!(suggest, Suggest::FileCompletion); + /// # } + /// ``` #[must_use] pub const fn file_comp() -> Self { Self::FileCompletion } /// Filters out already typed flag arguments from suggestion results. + /// + /// # Deprecation + /// + /// When using the `picker` feature, this method does not work under all + /// `ParserStyle` settings and should be avoided in favor of alternative + /// completion filtering approaches. #[must_use] #[cfg_attr( feature = "picker", @@ -48,6 +88,37 @@ impl Suggest { /// If both values are `Suggest::Suggest`, their `BTreeSet`s are merged /// (all items from `other` are added into `self`). Otherwise, the first /// `Suggest::Suggest` (or `FileCompletion`) is returned unchanged. + /// + /// # Returns + /// + /// Returns a new `Suggest` value. If both `self` and `other` are + /// `Suggest::Suggest`, the resulting `Suggest::Suggest` contains the union + /// of both suggestion sets. If `self` is `Suggest::FileCompletion`, it is + /// returned unchanged, regardless of `other`. + /// + /// # Example + /// + /// ``` + /// # #[cfg(feature = "comp")] { + /// # use mingling_core::Suggest; + /// let a: Suggest = ["foo", "bar"].into(); + /// let b: Suggest = ["baz"].into(); + /// let combined = a.clone().combine(b); + /// match combined { + /// Suggest::Suggest(set) => { + /// assert_eq!(set.len(), 3); + /// assert!(set.contains(&"foo".to_string().into())); + /// assert!(set.contains(&"bar".to_string().into())); + /// assert!(set.contains(&"baz".to_string().into())); + /// } + /// Suggest::FileCompletion => panic!("expected Suggest variant"), + /// } + /// + /// // FileCompletion is returned unchanged. + /// let combined = Suggest::FileCompletion.combine(a); + /// assert_eq!(combined, Suggest::FileCompletion); + /// # } + /// ``` #[must_use] pub fn combine(self, other: impl Into<Self>) -> Self { let other = other.into(); @@ -67,6 +138,24 @@ impl Suggest { /// # Arguments /// /// * `items` — A collection of suggestion strings to add. + /// + /// # Example + /// + /// ``` + /// # #[cfg(feature = "comp")] { + /// # use mingling_core::Suggest; + /// let mut suggest = Suggest::new(); + /// suggest.add_suggest(vec!["foo".to_string(), "bar".to_string()]); + /// match suggest { + /// Suggest::Suggest(set) => { + /// assert_eq!(set.len(), 2); + /// assert!(set.contains(&"foo".to_string().into())); + /// assert!(set.contains(&"bar".to_string().into())); + /// } + /// Suggest::FileCompletion => panic!("expected Suggest variant"), + /// } + /// # } + /// ``` pub fn add_suggest(&mut self, items: impl Into<Vec<String>>) { for item in items.into() { self.insert(SuggestItem::Simple(item)); @@ -84,6 +173,27 @@ impl Suggest { /// * `items` — A collection of suggestion strings to add. /// * `desc` — The description to attach to each suggestion. Must implement /// `Into<String>`. + /// + /// # Example + /// + /// ``` + /// # #[cfg(feature = "comp")] { + /// # use mingling_core::{Suggest, SuggestItem}; + /// let mut suggest = Suggest::new(); + /// suggest.add_suggest_with_description( + /// vec!["--foo".to_string(), "--bar".to_string()], + /// "Sets the option", + /// ); + /// match suggest { + /// Suggest::Suggest(set) => { + /// assert_eq!(set.len(), 2); + /// assert!(set.contains(&SuggestItem::new_with_desc("--foo".to_string(), "Sets the option".to_string()))); + /// assert!(set.contains(&SuggestItem::new_with_desc("--bar".to_string(), "Sets the option".to_string()))); + /// } + /// Suggest::FileCompletion => panic!("expected Suggest variant"), + /// } + /// # } + /// ``` pub fn add_suggest_with_description( &mut self, items: impl Into<Vec<String>>, @@ -111,6 +221,28 @@ impl Suggest { /// A new `Suggest` value where each item's suggestion text is prefixed /// with the given string. For example, `["foo", "bar"]` with prefix `"--"` /// becomes `["--foo", "--bar"]`. + /// + /// # Example + /// + /// ``` + /// # #[cfg(feature = "comp")] { + /// # use mingling_core::Suggest; + /// let suggest: Suggest = ["foo", "bar"].into(); + /// let prefixed = suggest.add_prefix("--"); + /// match prefixed { + /// Suggest::Suggest(set) => { + /// assert_eq!(set.len(), 2); + /// assert!(set.contains(&"--foo".to_string().into())); + /// assert!(set.contains(&"--bar".to_string().into())); + /// } + /// Suggest::FileCompletion => panic!("expected Suggest variant"), + /// } + /// + /// // FileCompletion is returned unchanged. + /// let unchanged = Suggest::FileCompletion.add_prefix("--"); + /// assert_eq!(unchanged, Suggest::FileCompletion); + /// # } + /// ``` #[must_use] pub fn add_prefix(self, prefix: impl Into<String>) -> Self { let suggest = match self { @@ -145,6 +277,28 @@ impl Suggest { /// A new `Suggest` value where each item's suggestion text is suffixed /// with the given string. For example, `["foo", "bar"]` with suffix `"="` /// becomes `["foo=", "bar="]`. + /// + /// # Example + /// + /// ``` + /// # #[cfg(feature = "comp")] { + /// # use mingling_core::Suggest; + /// let suggest: Suggest = ["foo", "bar"].into(); + /// let suffixed = suggest.add_suffix("="); + /// match suffixed { + /// Suggest::Suggest(set) => { + /// assert_eq!(set.len(), 2); + /// assert!(set.contains(&"foo=".to_string().into())); + /// assert!(set.contains(&"bar=".to_string().into())); + /// } + /// Suggest::FileCompletion => panic!("expected Suggest variant"), + /// } + /// + /// // FileCompletion is returned unchanged. + /// let unchanged = Suggest::FileCompletion.add_suffix("="); + /// assert_eq!(unchanged, Suggest::FileCompletion); + /// # } + /// ``` #[must_use] pub fn add_suffix(self, suffix: impl Into<String>) -> Self { let suggest = match self { @@ -198,20 +352,80 @@ impl std::ops::DerefMut for Suggest { } } -/// Represents a single suggestion item for shell completion. +/// Represents a single shell completion suggestion item. /// -/// This enum has two variants: -/// - `Simple(String)`: A suggestion without any description. -/// - `WithDescription(String, String)`: A suggestion with an associated description. +/// This enum contains two variants: +/// - `Simple(String)`: Contains only the suggestion text, with no accompanying description. +/// - `WithDescription(String, String)`: Contains the suggestion text and a corresponding description. +/// +/// The meaning of the parameters in both variants is as follows: +/// - The first `String` (the only parameter in `Simple`, and the first parameter in +/// `WithDescription`) always represents the suggestion text — the string that will be +/// inserted into the command line when the user selects it. +/// - The second `String` in `WithDescription` represents the optional description for the +/// suggestion, used to show the user the purpose or meaning of the option, helping them +/// make a choice from the completion list. +/// +/// ## Ordering behavior +/// +/// `SuggestItem` implements `Ord` and `PartialOrd`, ordering solely by the suggestion +/// text (`suggest()`) in lexicographic order; the `description` does not participate in +/// the ordering comparison. This allows `BTreeSet<SuggestItem>` to ensure suggestions are +/// de-duplicated and sorted by text order. +/// +/// ## Behavior under the `structural_renderer` feature +/// +/// When the `structural_renderer` feature is enabled, `SuggestItem` derives +/// `serde::Serialize`, allowing completion suggestion items to be serialized into JSON or +/// other supported structured formats. The serialized structure depends on the variant: +/// +/// - `Simple(text)` serializes as a string containing the `text` field (or an object, +/// depending on the serialization context). +/// - `WithDescription(text, desc)` serializes as an object containing both `text` and +/// `desc` fields, allowing front-end renderers to display both the text and description +/// when presenting the completion list. +/// +/// This feature is primarily used for graphical or rich-text shell interfaces (such as +/// web-based terminal emulators), in order to transmit completion suggestions as +/// structured data to the rendering layer. +/// +/// # Examples /// -/// The first `String` always holds the suggestion text, and the second `String` (if present) -/// holds an optional description providing additional context. +/// ``` +/// # use mingling_core::SuggestItem; +/// // Simple suggestion item, no description +/// let simple = SuggestItem::new("--help".to_string()); +/// +/// // Suggestion with a description +/// let with_desc = SuggestItem::new_with_desc( +/// "--verbose".to_string(), +/// "Output detailed log information".to_string(), +/// ); +/// +/// assert_eq!(simple.suggest(), &"--help".to_string()); +/// assert_eq!(with_desc.suggest(), &"--verbose".to_string()); +/// assert_eq!(with_desc.description(), Some(&"Output detailed log information".to_string())); +/// ``` #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "structural_renderer", derive(serde::Serialize))] pub enum SuggestItem { - /// A simple suggestion with only the suggestion text. + /// A simple suggestion item containing only the suggestion text. + /// + /// The `String` parameter represents the suggestion text — the string that will be + /// inserted into the command line when the user selects it. + /// This variant has no description, and is suitable for completion options that do + /// not require additional explanation (such as file names or simple commands). Simple(String), - /// A suggestion with both text and a description. + + /// A suggestion item containing both suggestion text and a description. + /// + /// - The first `String`: the suggestion text — the string that will be inserted into + /// the command line when the user selects it. + /// - The second `String`: the description for this suggestion, used to show the user + /// the purpose or meaning of the option. + /// + /// This variant is suitable for completion options that need to provide additional + /// context to the user (such as long options with explanations like `--flag`). WithDescription(String, String), } @@ -235,18 +449,77 @@ impl Ord for SuggestItem { impl SuggestItem { /// Creates a new simple suggestion without description. + /// + /// # Arguments + /// + /// * `suggest` — The suggestion text to store in this `SuggestItem`. + /// + /// # Returns + /// + /// Returns a [`SuggestItem::Simple`] containing the given suggestion text. + /// + /// # Example + /// + /// ``` + /// # use mingling_core::SuggestItem; + /// let item = SuggestItem::new("--help".to_string()); + /// assert_eq!(item.suggest(), &"--help".to_string()); + /// ``` #[must_use] pub const fn new(suggest: String) -> Self { Self::Simple(suggest) } /// Creates a new suggestion with a description. + /// + /// # Arguments + /// + /// * `suggest` — The suggestion text to store in this `SuggestItem`. + /// * `description` — The description for this suggestion, used to show the user the + /// purpose or meaning of the option. + /// + /// # Returns + /// + /// Returns a [`SuggestItem::WithDescription`] containing the given suggestion text + /// and description. + /// + /// # Example + /// + /// ``` + /// # use mingling_core::SuggestItem; + /// let item = SuggestItem::new_with_desc( + /// "--verbose".to_string(), + /// "Output detailed log information".to_string(), + /// ); + /// assert_eq!(item.suggest(), &"--verbose".to_string()); + /// assert_eq!(item.description(), Some(&"Output detailed log information".to_string())); + /// ``` #[must_use] pub const fn new_with_desc(suggest: String, description: String) -> Self { Self::WithDescription(suggest, description) } /// Adds a description to this suggestion, replacing any existing description. + /// + /// # Arguments + /// + /// * `description` — The new description to attach to this suggestion. Must implement + /// `Into<String>`. + /// + /// # Returns + /// + /// Returns a new `SuggestItem` with the given description. If this was previously a + /// [`SuggestItem::Simple`] variant, it is converted to + /// [`SuggestItem::WithDescription`] with the original suggestion text preserved. + /// + /// # Example + /// + /// ``` + /// # use mingling_core::SuggestItem; + /// let item = SuggestItem::new("--help".to_string()).with_desc("Show help message".to_string()); + /// assert_eq!(item.suggest(), &"--help".to_string()); + /// assert_eq!(item.description(), Some(&"Show help message".to_string())); + /// ``` #[must_use] pub fn with_desc(self, description: String) -> Self { match self { @@ -257,6 +530,31 @@ impl SuggestItem { } /// Returns the suggestion text. + /// + /// The suggestion text is the string that will be inserted into the command line when + /// the user selects this completion item. Both the [`SuggestItem::Simple`] and + /// [`SuggestItem::WithDescription`] variants contain a suggestion text, so this method + /// works uniformly on both variants. + /// + /// # Returns + /// + /// Returns `&String` referencing the suggestion text contained in this `SuggestItem`. + /// + /// # Example + /// + /// ``` + /// # use mingling_core::SuggestItem; + /// // Simple item + /// let simple = SuggestItem::new("--help".to_string()); + /// assert_eq!(simple.suggest(), &"--help".to_string()); + /// + /// // Item with description + /// let with_desc = SuggestItem::new_with_desc( + /// "--verbose".to_string(), + /// "Output detailed log information".to_string(), + /// ); + /// assert_eq!(with_desc.suggest(), &"--verbose".to_string()); + /// ``` #[must_use] pub const fn suggest(&self) -> &String { match self { @@ -265,6 +563,24 @@ impl SuggestItem { } /// Updates the suggestion text. + /// + /// This method replaces the suggestion text of the [`SuggestItem`] with the provided + /// string. It works uniformly on both the [`SuggestItem::Simple`] and + /// [`SuggestItem::WithDescription`] variants, updating only the suggestion text and + /// leaving any existing description unchanged. + /// + /// # Arguments + /// + /// * `new_suggest` — The new suggestion text to set. Must implement `Into<String>`. + /// + /// # Example + /// + /// ``` + /// # use mingling_core::SuggestItem; + /// let mut item = SuggestItem::new("--help".to_string()); + /// item.set_suggest("--verbose".to_string()); + /// assert_eq!(item.suggest(), &"--verbose".to_string()); + /// ``` pub fn set_suggest(&mut self, new_suggest: String) { match self { Self::Simple(suggest) | Self::WithDescription(suggest, _) => *suggest = new_suggest, @@ -272,6 +588,28 @@ impl SuggestItem { } /// Returns the description if present. + /// + /// # Returns + /// + /// Returns `Some(&String)` containing the description if this item is a + /// [`SuggestItem::WithDescription`] variant; returns `None` if this item + /// is a [`SuggestItem::Simple`] variant (i.e. no description is attached). + /// + /// # Example + /// + /// ``` + /// # use mingling_core::SuggestItem; + /// // Simple item has no description. + /// let simple = SuggestItem::new("--help".to_string()); + /// assert_eq!(simple.description(), None); + /// + /// // Item with description returns it. + /// let with_desc = SuggestItem::new_with_desc( + /// "--verbose".to_string(), + /// "Output detailed log information".to_string(), + /// ); + /// assert_eq!(with_desc.description(), Some(&"Output detailed log information".to_string())); + /// ``` #[must_use] pub const fn description(&self) -> Option<&String> { match self { @@ -281,6 +619,34 @@ impl SuggestItem { } /// Sets or replaces the description. + /// + /// This method sets the description of the [`SuggestItem`]. If this item is a + /// [`SuggestItem::Simple`] variant, it is converted to + /// [`SuggestItem::WithDescription`] with the original suggestion text preserved. + /// If this item is already a [`SuggestItem::WithDescription`] variant, the + /// existing description is replaced. + /// + /// # Arguments + /// + /// * `description` — The new description to set. Must implement `Into<String>`. + /// + /// # Example + /// + /// ``` + /// # use mingling_core::SuggestItem; + /// // On a simple item + /// let mut item = SuggestItem::new("--help".to_string()); + /// item.set_description("Show help message".to_string()); + /// assert_eq!(item.description(), Some(&"Show help message".to_string())); + /// + /// // Replacing an existing description + /// let mut item = SuggestItem::new_with_desc( + /// "--verbose".to_string(), + /// "Old description".to_string(), + /// ); + /// item.set_description("New description".to_string()); + /// assert_eq!(item.description(), Some(&"New description".to_string())); + /// ``` pub fn set_description(&mut self, description: String) { match self { Self::Simple(suggest) => *self = Self::WithDescription(suggest.clone(), description), @@ -289,6 +655,34 @@ impl SuggestItem { } /// Removes and returns the description if present. + /// + /// If this item is a [`SuggestItem::WithDescription`] variant, the description + /// is removed and returned, and the item is converted to a + /// [`SuggestItem::Simple`] variant containing the same suggestion text. If + /// this item is already a [`SuggestItem::Simple`] variant, `None` is returned + /// and the item is left unchanged. + /// + /// # Returns + /// + /// Returns `Some(String)` containing the removed description if this item + /// had a description; returns `None` if this item had no description. + /// + /// # Example + /// + /// ``` + /// # use mingling_core::SuggestItem; + /// // Item with a description + /// let mut item = SuggestItem::new_with_desc( + /// "--verbose".to_string(), + /// "Output detailed log information".to_string(), + /// ); + /// assert_eq!(item.remove_desc(), Some("Output detailed log information".to_string())); + /// assert!(matches!(item, SuggestItem::Simple(ref s) if s == "--verbose")); + /// + /// // Item without a description + /// let mut item = SuggestItem::new("--help".to_string()); + /// assert_eq!(item.remove_desc(), None); + /// ``` pub fn remove_desc(&mut self) -> Option<String> { match self { Self::Simple(_) => None, |
