aboutsummaryrefslogtreecommitdiff
path: root/mingling_core/src
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-08-14 03:26:54 +0800
committer魏曹先生 <1992414357@qq.com>2026-08-14 03:26:54 +0800
commitb138dff3517e8cb793d431af6ad4a577491e21b0 (patch)
tree7ff88d23f76caf62f39dfd2a7e3285fc8e186e63 /mingling_core/src
parenta14b9bbb96e025a60abeb492ba043b0db806289a (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.rs92
-rw-r--r--mingling_core/src/comp/comp_ctx.rs20
-rw-r--r--mingling_core/src/comp/flags.rs18
-rw-r--r--mingling_core/src/comp/shell_ctx.rs132
-rw-r--r--mingling_core/src/comp/suggest.rs420
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,