diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/_zh_CN/pages/1-getting-started.md | 4 | ||||
| -rw-r--r-- | docs/dev/_sidebar.md | 10 | ||||
| -rw-r--r-- | docs/dev/pages/issues/0.5.0-roadmap.md | 138 | ||||
| -rw-r--r-- | docs/dev/pages/issues/t0_generalize-repl-system.md | 36 | ||||
| -rw-r--r-- | docs/dev/pages/issues/t0_remove-pack-macros.md | 49 | ||||
| -rw-r--r-- | docs/dev/pages/issues/t0_remove-parser-feature.md | 36 | ||||
| -rw-r--r-- | docs/dev/pages/issues/t0_remove-with-dispatcher.md | 35 | ||||
| -rw-r--r-- | docs/dev/pages/issues/t1_completion-higher-level-abstractions.md | 38 | ||||
| -rw-r--r-- | docs/dev/pages/issues/t1_modify-dispatcher-syntax.md | 44 | ||||
| -rw-r--r-- | docs/dev/pages/issues/t1_move-structural-renderer.md | 38 | ||||
| -rw-r--r-- | docs/dev/pages/issues/t1_pathf-export-macro.md | 53 | ||||
| -rw-r--r-- | docs/dev/pages/issues/t2_automated-dispatch-tree-optimization.md | 57 | ||||
| -rw-r--r-- | docs/example-viewer.html | 263 | ||||
| -rw-r--r-- | docs/examples.html | 140 | ||||
| -rw-r--r-- | docs/pages/1-getting-started.md | 4 | ||||
| -rw-r--r-- | docs/res/guide.txt | 2 | ||||
| -rw-r--r-- | docs/scripts/display-parse.js | 269 |
17 files changed, 1068 insertions, 148 deletions
diff --git a/docs/_zh_CN/pages/1-getting-started.md b/docs/_zh_CN/pages/1-getting-started.md index 6aa6229..47fdda8 100644 --- a/docs/_zh_CN/pages/1-getting-started.md +++ b/docs/_zh_CN/pages/1-getting-started.md @@ -13,7 +13,7 @@ cd my-cli ```toml [dependencies.mingling] -version = "0.4.0" +version = "0.5.0" features = [] ``` @@ -25,7 +25,7 @@ features = [] ```toml [dependencies.mingling] -version = "0.4.0" +version = "0.5.0" features = [ "parser", "comp", diff --git a/docs/dev/_sidebar.md b/docs/dev/_sidebar.md index 03a0edf..cfb482a 100644 --- a/docs/dev/_sidebar.md +++ b/docs/dev/_sidebar.md @@ -1,10 +1,18 @@ - [Welcome!](README) * ❓ Issues - * [The Mingling 0.5.0 Roadmap](pages/issues/0.5.0-roadmap) * [[Solved] The Picker2 Arguments Parser](pages/issues/_add-picker2) * [[Solved] Remove r_print! and r_println! Macros](pages/issues/_remove-r-print-macro) * [[Solved] The Command Macro](pages/issues/_the-command-macro) * [[Solved] The Mod Pathfinder](pages/issues/_the-mod-pathfinder) + * [[T0] Generalize the REPL System](pages/issues/t0_generalize-repl-system) + * [[T0] Remove the pack! Family of Macros](pages/issues/t0_remove-pack-macros) + * [[T0] Remove the parser Feature](pages/issues/t0_remove-parser-feature) + * [[T0] Remove with_dispatcher and with_dispatchers](pages/issues/t0_remove-with-dispatcher) + * [[T1] Higher-Level Abstractions for the Completion System](pages/issues/t1_completion-higher-level-abstractions) + * [[T1] Modify the dispatcher! Syntax](pages/issues/t1_modify-dispatcher-syntax) + * [[T1] Move structural_renderer from mingling_core to mingling](pages/issues/t1_move-structural-renderer) + * [[T1] The pathf_export Attribute Macro](pages/issues/t1_pathf-export-macro) + * [[T2] Automated dispatcher_tree Optimization Decisions](pages/issues/t2_automated-dispatch-tree-optimization) * [The Next-Gen Mingling Pipeline](pages/issues/the-next-pipeline) * [Some Situations Where You'd Be Like "Shit!"](pages/issues/the-shit-time) * 💡 Abouts diff --git a/docs/dev/pages/issues/0.5.0-roadmap.md b/docs/dev/pages/issues/0.5.0-roadmap.md deleted file mode 100644 index 5238cae..0000000 --- a/docs/dev/pages/issues/0.5.0-roadmap.md +++ /dev/null @@ -1,138 +0,0 @@ -<h1 align="center">The Mingling 0.5.0 Roadmap</h1> - -Mingling 0.5.0 is going to be a significant release, planned as follows: - -1. **Breaking:** Remove the `pack!` macro: - -Since the very first version of Mingling, the `pack!` macro has been around. -Its purpose has gradually narrowed from "creating a type and registering it -to Mingling" to "creating a newtype that derives Grouped". In other words, -the functionality of `pack!` is gradually being replaced by the `Grouped derive`. -Furthermore, in 0.2.0, in order to accommodate `StructuralData derive`, Mingling -introduced `pack_structural!` and `pack_err_structural!` variants all at once, -which greatly increases the maintenance cost of the project. - -So I plan to introduce a Breaking Change in 0.5.0: remove the entire `pack!` family of macros. - -All future type creation will be done as follows: - -```rust -// Before -pack!(ResultNames = Vec<String>); - -// After -#[derive(Grouped)] -pub struct ResultNames { - names: Vec<String> -} -``` - -2. **Breaking:** Generalize the REPL system - -The current REPL is merely _usable_, but far from _user-friendly_. Mingling plans -to remove the `repl` feature in 0.5 and by default expose more execution-related -interfaces for the Program, so that users can extend functionality beyond the REPL -by leveraging Mingling's execution model. - -3. **Breaking:** Remove the `parser` feature - -In 0.3.0, Mingling introduced the `picker` feature, which provides more powerful -parameter parsing capabilities. At that point, the original `parser` feature became -inadequate. Mingling plans to completely remove it, which will directly affect -downstream users of the `parser` feature. - -4. **Breaking:** Remove `with_dispatcher` and `with_dispatchers` - -Mingling's commands must be registered through `with_dispatcher` in order to be usable -when `dispatcher_tree` is disabled. This has always been a strange semantic: `chain`, -`renderer`, `help`, `completion`, and `metadata` are all collected at compile time, -so why is `Dispatcher` the exception? - -In fact, during my usage of `dispatcher` from 0.1.0 to 0.4.0, I have never encountered -a scenario where **dynamic registration** was necessary. I consider it unnecessary. - -Therefore, I plan to make `Dispatcher` registration also compile-time collected in -non-`dispatcher_tree` states starting from 0.5.0. - -5. **Breaking:** Modify the `dispatcher!` syntax - -After completing item #4, `dispatcher!` will be simplified, because the `CMD*` struct will no longer need to be created - -```rust -// Before -dispatcher!("command", CMDCommand => EntryCommand); - -// After -dispatcher!("command", EntryCommand); - -// NOTE: The implicit mode is not affected -``` - -6. **Feature:** Higher-level abstractions for the completion system - -Mingling's completion system filled a number of behavioral gaps in 0.4 and fixed many -edge cases. It's now time to introduce more powerful higher-level abstractions. - -First, this feature will add a set of utility functions to `ShellContext`, enabling a -smarter description of user state, rather than simply relying on manually identifying -user behavior through fields like `previous_word`. - -Additionally, when the `picker` feature introduced in 0.3.0 is enabled together with -the `comp` feature, a module named `picker_comp` will be activated to enable more -completion behaviors. - -7. **Feature:** Automated `dispatcher_tree` optimization decisions (under consideration) - -After completing item #4, this Feature becomes implementable: Mingling can automatically -decide whether to use `dispatcher_tree` to optimize dispatch efficiency based on the current -number and depth of registered commands, so users no longer need to manually enable the -`dispatch_tree` feature. - -Conditions: `dispatch_tree` has an advantage in cases where command depth is too high and -the number of commands is too large. However, if the number of commands is too small, the -increased CPU prediction failure rate will inevitably make it less efficient than linear -lookup; specifics need to be tuned during implementation. - -Additionally, the issue where `pathf` + `dispatch_tree` must be explicitly specified in -`[build-dependencies]` will be resolved: - -```toml -# Before -[build-dependencies.mingling] -version = "0.4.0" -features = [ "build", "pathf", "dispatch_tree" ] # `dispatch_tree` must be explicitly specified for `pathf` to recognize it - -# After -[build-dependencies.mingling] -version = "0.4.0" -features = [ "build", "pathf" ] # No `dispatch_tree` feature; `pathf` no longer needs to consider its branches -``` - -8. **Feature:** A new macro designed for `pathf`: `#[pathf_export(type::TypePath)]` - -`pathf` has been around since 0.2.0 and has worked well for a long time, with many edge cases resolved. However, it still lacks an escape hatch — "when certain indirect expansions cannot be recognized by `pathf`, how can we assist its inference?" - -I plan to introduce a new attribute macro to supplement `pathf`'s path inference. - -```rust -#[macro_export] -macro_rules! repack { - ($name:ident) => { - // Ignored! This section cannot be parsed by pathf. - #[mingling::macros::pathf_ignore] - #[derive(mingling::Grouped)] - pub struct $name; - }; -} - -// The expansion contains macros that need to be parsed by pathf -#[pathf_export(MyType)] // Explicitly specified to assist pathf's inference -repack!(MyType); -``` - -> [!Note] -> Haha, hopefully we'll never have to use it. - -<p align="center" style="font-size: 0.85em; color: gray;"> - Written by @Weicao-CatilGrass -</p> diff --git a/docs/dev/pages/issues/t0_generalize-repl-system.md b/docs/dev/pages/issues/t0_generalize-repl-system.md new file mode 100644 index 0000000..73c9541 --- /dev/null +++ b/docs/dev/pages/issues/t0_generalize-repl-system.md @@ -0,0 +1,36 @@ +<h1 align="center">[T0] Generalize the REPL System</h1> +<p align="center"> + Breaking: remove the <code>repl</code> feature and expose execution interfaces on the Program +</p> + +> [!NOTE] +> +> This is a **Breaking Change** planned for Mingling 0.5.0. + +## Background + +The current REPL is merely _usable_, but far from _user-friendly_. The `repl` feature locks a specific interactive front-end into the framework, while the underlying execution model is what actually provides value. + +## Plan + +- Remove the `repl` feature in 0.5.0. +- By default, expose more execution-related interfaces for the `Program`, so that users can extend functionality beyond the REPL by leveraging Mingling's execution model. + +The goal is to separate the execution model from any particular interactive front-end, letting users build their own REPL (or other execution drivers) on top of Mingling's public interfaces. + +## Tasks + +- [ ] Audit the current `repl` implementation and identify which behaviors belong to the execution model vs. the interactive front-end +- [ ] Design and expose the execution-related interfaces on `Program` (e.g. per-input execution, result handling, exit semantics) +- [ ] Remove the `repl` feature from `mingling`, `mingling_core`, and `mingling_macros` +- [ ] Remove or migrate the built-in REPL front-end +- [ ] Update examples and docs that enable `repl` + +## 🕘 Progress + +- [ ] In Progress +- [ ] Complete + +<p align="center" style="font-size: 0.85em; color: gray;"> + Written by @Weicao-CatilGrass +</p> diff --git a/docs/dev/pages/issues/t0_remove-pack-macros.md b/docs/dev/pages/issues/t0_remove-pack-macros.md new file mode 100644 index 0000000..517646a --- /dev/null +++ b/docs/dev/pages/issues/t0_remove-pack-macros.md @@ -0,0 +1,49 @@ +`<h1 align="center">[T0] Remove the pack! Family of Macros</h1> +<p align="center"> + Breaking: retire the entire <code>pack!</code> family in favor of the <code>Grouped</code> derive +</p> + +> [!NOTE] +> +> This is a **Breaking Change** planned for Mingling 0.5.0. + +## Background + +Since the very first version of Mingling, the `pack!` macro has been around. Its purpose has gradually narrowed from "creating a type and registering it to Mingling" to "creating a newtype that derives Grouped". In other words, the functionality of `pack!` is gradually being replaced by the `Grouped derive`. + +Furthermore, in 0.2.0, in order to accommodate `StructuralData derive`, Mingling introduced `pack_structural!` and `pack_err_structural!` variants all at once, which greatly increases the maintenance cost of the project. + +## Plan + +Remove the entire `pack!` family of macros (`pack!`, `pack_structural!`, `pack_err_structural!`) as a Breaking Change in 0.5.0. + +All future type creation will be done as follows: + +```rust +// Before +pack!(ResultNames = Vec<String>); + +// After +#[derive(Grouped)] +pub struct ResultNames { + names: Vec<String> +} +``` + +## Tasks + +- [ ] Identify all usages of `pack!` / `pack_structural!` / `pack_err_structural!` across the codebase, examples, and docs +- [ ] Migrate internal usages to `#[derive(Grouped)]` +- [ ] Remove the macro definitions and their re-exports +- [ ] Update the docs / helpdoc examples that reference `pack!` +- [ ] Update downstream feature docs (`structural_renderer` etc.) where `pack_structural!` was involved +- [ ] Verify all tests pass + +## 🕘 Progress + +- [ ] In Progress +- [ ] Complete + +<p align="center" style="font-size: 0.85em; color: gray;"> + Written by @Weicao-CatilGrass +</p> diff --git a/docs/dev/pages/issues/t0_remove-parser-feature.md b/docs/dev/pages/issues/t0_remove-parser-feature.md new file mode 100644 index 0000000..5c1cffd --- /dev/null +++ b/docs/dev/pages/issues/t0_remove-parser-feature.md @@ -0,0 +1,36 @@ +<h1 align="center">[T0] Remove the parser Feature</h1> +<p align="center"> + Breaking: retire the legacy argument parsing in favor of <code>picker</code> +</p> + +> [!NOTE] +> +> This is a **Breaking Change** planned for Mingling 0.5.0. + +## Background + +In 0.3.0, Mingling introduced the `picker` feature, which provides more powerful parameter parsing capabilities. At that point, the original `parser` feature became inadequate. + +The `parser` feature was a temporary argument parsing solution created in the early stages of the project. While it can handle basic argument parsing tasks, its functionality is incomplete and has many limitations (see the [Picker2 issue](_add-picker2) for the full list). + +## Plan + +Completely remove the `parser` feature in 0.5.0. This will directly affect downstream users of the `parser` feature — they must migrate to `picker` (or handle parsing manually). + +## Tasks + +- [ ] Identify all usages of the `parser` feature across the codebase, examples, and docs +- [ ] Migrate internal usages (tests, examples, dev-dependencies) to `picker` +- [ ] Remove the `parser` feature from `mingling` and its dependency (`size`) +- [ ] Remove parser-related modules and public API +- [ ] Update docs and helpdoc examples +- [ ] Note the downstream migration path in the changelog + +## 🕘 Progress + +- [ ] In Progress +- [ ] Complete + +<p align="center" style="font-size: 0.85em; color: gray;"> + Written by @Weicao-CatilGrass +</p> diff --git a/docs/dev/pages/issues/t0_remove-with-dispatcher.md b/docs/dev/pages/issues/t0_remove-with-dispatcher.md new file mode 100644 index 0000000..8779d74 --- /dev/null +++ b/docs/dev/pages/issues/t0_remove-with-dispatcher.md @@ -0,0 +1,35 @@ +<h1 align="center">[T0] Remove with_dispatcher and with_dispatchers</h1> +<p align="center"> + Breaking: make <code>Dispatcher</code> registration compile-time collected in all modes +</p> + +> [!NOTE] +> +> This is a **Breaking Change** planned for Mingling 0.5.0. + +## Background + +Mingling's commands must be registered through `with_dispatcher` in order to be usable when `dispatcher_tree` is disabled. This has always been a strange semantic: `chain`, `renderer`, `help`, `completion`, and `metadata` are all collected at compile time, so why is `Dispatcher` the exception? + +In fact, during usage of `dispatcher` from 0.1.0 to 0.4.0, no scenario has ever been encountered where **dynamic registration** was necessary. It is considered unnecessary. + +## Plan + +Make `Dispatcher` registration also compile-time collected in non-`dispatcher_tree` states starting from 0.5.0, and remove `with_dispatcher` / `with_dispatchers` (and the related `#[program_setup]` registration path if it becomes obsolete). + +## Tasks + +- [ ] Design how dispatchers are collected at compile time when `dispatcher_tree` is disabled (consistent with how `chain` / `renderer` / `completion` / `metadata` are collected) +- [ ] Remove `with_dispatcher` and `with_dispatchers` from the `Program` API +- [ ] Update `gen_program!` and the macros so registration happens automatically +- [ ] Migrate examples, tests, and docs that call `with_dispatcher` / `with_dispatchers` +- [ ] Verify both `dispatcher_tree`-enabled and disabled modes behave identically + +## 🕘 Progress + +- [ ] In Progress +- [ ] Complete + +<p align="center" style="font-size: 0.85em; color: gray;"> + Written by @Weicao-CatilGrass +</p> diff --git a/docs/dev/pages/issues/t1_completion-higher-level-abstractions.md b/docs/dev/pages/issues/t1_completion-higher-level-abstractions.md new file mode 100644 index 0000000..69adba0 --- /dev/null +++ b/docs/dev/pages/issues/t1_completion-higher-level-abstractions.md @@ -0,0 +1,38 @@ +<h1 align="center">[T1] Higher-Level Abstractions for the Completion System</h1> +<p align="center"> + Feature: smarter state descriptions in <code>ShellContext</code> and a <code>picker_comp</code> module +</p> + +## Background + +Mingling's completion system filled a number of behavioral gaps in 0.4 and fixed many edge cases. It's now time to introduce more powerful higher-level abstractions. + +Currently, completion logic relies on manually identifying user behavior through fields like `previous_word`, which is fragile and requires every completion function to re-derive the user's intent. + +## Plan + +### 1. Utility functions on `ShellContext` + +Add a set of utility functions to `ShellContext`, enabling a smarter description of user state, rather than simply relying on manually identifying user behavior through fields like `previous_word`. + +### 2. `picker_comp` module + +Additionally, when the `picker` feature (introduced in 0.3.0) is enabled together with the `comp` feature, a module named `picker_comp` will be activated to enable more completion behaviors — e.g. completing picker-style flags (`--key=value`, multi-flag forms, etc.) using knowledge of the picker parsing model. + +## Tasks + +- [ ] Design the `ShellContext` utility API (state descriptions / high-level queries over the current input state) +- [ ] Implement the utility functions and add tests +- [ ] Implement the `picker_comp` module, gated on `picker` + `comp` +- [ ] Add completion behaviors specific to picker argument formats +- [ ] Update docs and examples +- [ ] Verify existing 0.4 completion edge-case fixes are preserved + +## 🕘 Progress + +- [ ] In Progress +- [ ] Complete + +<p align="center" style="font-size: 0.85em; color: gray;"> + Written by @Weicao-CatilGrass +</p> diff --git a/docs/dev/pages/issues/t1_modify-dispatcher-syntax.md b/docs/dev/pages/issues/t1_modify-dispatcher-syntax.md new file mode 100644 index 0000000..f1a9346 --- /dev/null +++ b/docs/dev/pages/issues/t1_modify-dispatcher-syntax.md @@ -0,0 +1,44 @@ +<h1 align="center">[T1] Modify the dispatcher! Syntax</h1> +<p align="center"> + Breaking: drop the <code>CMD*</code> struct from the explicit form of <code>dispatcher!</code> +</p> + +> [!NOTE] +> +> This is a **Breaking Change** planned for Mingling 0.5.0, and it depends on [Remove with_dispatcher and with_dispatchers](t0_remove-with-dispatcher). + +## Background + +Once `Dispatcher` registration is compile-time collected (see [Remove with_dispatcher and with_dispatchers](t0_remove-with-dispatcher)), the `CMD*` struct becomes unnecessary — it only existed to give `with_dispatcher` something to register. + +## Plan + +Simplify the `dispatcher!` syntax: the explicit form no longer creates a `CMD*` struct, so only the entry type needs to be given. + +```rust +// Before +dispatcher!("command", CMDCommand => EntryCommand); + +// After +dispatcher!("command", EntryCommand); + +// NOTE: The implicit mode is not affected +``` + +## Tasks + +- [ ] Update `dispatcher!` to accept the simplified explicit form (`"name", EntryType`) +- [ ] Decide whether the old `CMDType => EntryType` form should error with a helpful message or be removed outright +- [ ] Remove the generated `CMD*` struct machinery +- [ ] Update `#[command]` macro internals that depend on `CMD*` +- [ ] Migrate examples, tests, and docs +- [ ] Keep the implicit mode (`dispatcher!("name")`) working unchanged + +## 🕘 Progress + +- [ ] In Progress +- [ ] Complete + +<p align="center" style="font-size: 0.85em; color: gray;"> + Written by @Weicao-CatilGrass +</p> diff --git a/docs/dev/pages/issues/t1_move-structural-renderer.md b/docs/dev/pages/issues/t1_move-structural-renderer.md new file mode 100644 index 0000000..66da5c8 --- /dev/null +++ b/docs/dev/pages/issues/t1_move-structural-renderer.md @@ -0,0 +1,38 @@ +<h1 align="center">[T1] Move structural_renderer from mingling_core to mingling</h1> +<p align="center"> + Breaking: inject <code>StructuralRenderer</code> via Hook instead of hardcoding it into the core loop +</p> + +> [!NOTE] +> +> This is a **Breaking Change** planned for Mingling 0.5.0. + +## Background + +Mingling's Hook system is now complete, so there's no longer a need to hardcode `StructuralRenderer` into the core loop. + +The plan is to remove it from `exec.rs` and instead inject the Hook implementation via `StructuralRendererSetup`. + +## Plan + +- Remove the hardcoded `StructuralRenderer` from the core execution loop (`exec.rs`). +- Implement the renderer as a Hook and inject it via `StructuralRendererSetup`. +- `mingling_core` no longer depends on the structural renderer; the wiring moves up to the `mingling` crate level. + +## Tasks + +- [ ] Audit where `StructuralRenderer` is hardcoded in `mingling_core` (`exec.rs` and related) +- [ ] Design `StructuralRendererSetup` as a Hook implementation +- [ ] Move / re-implement the renderer wiring in `mingling` +- [ ] Clean up `mingling_core`'s `structural_renderer` feature and its serde deps (if no longer needed there) +- [ ] Migrate examples and tests +- [ ] Verify structural renderer output is unchanged + +## 🕘 Progress + +- [ ] In Progress +- [ ] Complete + +<p align="center" style="font-size: 0.85em; color: gray;"> + Written by @Weicao-CatilGrass +</p> diff --git a/docs/dev/pages/issues/t1_pathf-export-macro.md b/docs/dev/pages/issues/t1_pathf-export-macro.md new file mode 100644 index 0000000..0a98485 --- /dev/null +++ b/docs/dev/pages/issues/t1_pathf-export-macro.md @@ -0,0 +1,53 @@ +<h1 align="center">[T1] The pathf_export Attribute Macro</h1> +<p align="center"> + Feature: an escape hatch for <code>pathf</code>'s path inference +</p> + +## Background + +`pathf` has been around since 0.2.0 and has worked well for a long time, with many edge cases resolved. However, it still lacks an escape hatch — "when certain indirect expansions cannot be recognized by `pathf`, how can we assist its inference?" + +For example, when a Mingling type is created through a user-defined `macro_rules!` wrapper, `pathf` cannot see through the indirect expansion: + +```rust +#[macro_export] +macro_rules! repack { + ($name:ident) => { + // Ignored! This section cannot be parsed by pathf. + #[mingling::macros::pathf_ignore] + #[derive(mingling::Grouped)] + pub struct $name; + }; +} + +// The expansion contains macros that need to be parsed by pathf +#[pathf_export(MyType)] // Explicitly specified to assist pathf's inference +repack!(MyType); +``` + +> [!Note] +> Haha, hopefully we'll never have to use it. + +## Plan + +Introduce a new attribute macro, `#[pathf_export(type::TypePath)]`, to supplement `pathf`'s path inference. When applied to an item whose expansion contains Mingling types that `pathf` cannot recognize, it explicitly records the resulting type paths so the build-time analyzer can pick them up. + +The example above also shows `#[mingling::macros::pathf_ignore]`, which marks an item to be skipped by `pathf` (used inside macro bodies that `pathf` otherwise cannot parse). + +## Tasks + +- [ ] Design the `pathf_export` syntax and semantics (attribute position, multiple type paths, interplay with `pathf_ignore`) +- [ ] Implement `pathf_export` in `mingling_macros` +- [ ] Implement `pathf_ignore` support in `mingling_macros` +- [ ] Teach `mingling-pathf` to consume the exported mappings +- [ ] Add tests covering indirect macro expansions +- [ ] Update docs and examples + +## 🕘 Progress + +- [ ] In Progress +- [ ] Complete + +<p align="center" style="font-size: 0.85em; color: gray;"> + Written by @Weicao-CatilGrass +</p> diff --git a/docs/dev/pages/issues/t2_automated-dispatch-tree-optimization.md b/docs/dev/pages/issues/t2_automated-dispatch-tree-optimization.md new file mode 100644 index 0000000..691fd1a --- /dev/null +++ b/docs/dev/pages/issues/t2_automated-dispatch-tree-optimization.md @@ -0,0 +1,57 @@ +<h1 align="center">[T2] Automated dispatcher_tree Optimization Decisions</h1> +<p align="center"> + Feature: let Mingling decide when <code>dispatch_tree</code> pays off (under consideration) +</p> + +> [!NOTE] +> +> This item is **under consideration**. It depends on [Remove with_dispatcher and with_dispatchers](t0_remove-with-dispatcher). + +## Background + +`dispatch_tree` provides a faster dispatch path, but it is not always a win. Currently users must manually enable the `dispatch_tree` feature and make the trade-off themselves. + +After dispatcher registration becomes compile-time collected (see [Remove with_dispatcher and with_dispatchers](t0_remove-with-dispatcher)), Mingling can know the full set and depth of registered commands at compile time — making automated decisions implementable. + +## Plan + +Mingling can automatically decide whether to use `dispatcher_tree` to optimize dispatch efficiency based on the current number and depth of registered commands, so users no longer need to manually enable the `dispatch_tree` feature. + +### Conditions + +`dispatch_tree` has an advantage in cases where command depth is too high and the number of commands is too large. However, if the number of commands is too small, the increased CPU prediction failure rate will inevitably make it less efficient than linear lookup; specifics need to be tuned during implementation. + +### Resolve the `pathf` + `dispatch_tree` build-dependency issue + +Additionally, the issue where `pathf` + `dispatch_tree` must be explicitly specified in `[build-dependencies]` will be resolved: + +```toml +# Before +[build-dependencies.mingling] +version = "0.4.0" +features = [ "build", "pathf", "dispatch_tree" ] # `dispatch_tree` must be explicitly specified for `pathf` to recognize it + +# After +[build-dependencies.mingling] +version = "0.4.0" +features = [ "build", "pathf" ] # No `dispatch_tree` feature; `pathf` no longer needs to consider its branches +``` + +## Tasks + +- [ ] Collect statistics about registered commands (count, depth) at compile time +- [ ] Benchmark / tune the threshold between linear lookup and `dispatch_tree` +- [ ] Implement the automatic decision and wire it into dispatch code generation +- [ ] Remove the manual `dispatch_tree` feature toggle (or keep it as an override?) +- [ ] Refactor `pathf` so it no longer branches on `dispatch_tree` +- [ ] Update examples, tests, and docs + +## 🕘 Progress + +- [ ] Under Consideration +- [ ] In Progress +- [ ] Complete + +<p align="center" style="font-size: 0.85em; color: gray;"> + Written by @Weicao-CatilGrass +</p> diff --git a/docs/example-viewer.html b/docs/example-viewer.html index dd51e95..0417c30 100644 --- a/docs/example-viewer.html +++ b/docs/example-viewer.html @@ -31,6 +31,117 @@ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; overflow-x: hidden; + background-image: + linear-gradient( + to right, + rgba(212, 168, 75, 0.03) 1px, + transparent 1px + ), + linear-gradient( + to bottom, + rgba(212, 168, 75, 0.03) 1px, + transparent 1px + ), + linear-gradient( + to right, + rgba(212, 168, 75, 0.015) 1px, + transparent 1px + ), + linear-gradient( + to bottom, + rgba(212, 168, 75, 0.015) 1px, + transparent 1px + ); + background-size: + 40px 40px, + 40px 40px, + 20px 20px, + 20px 20px; + background-position: + calc(50% - 480px + 20px) 0, + calc(50% - 480px) 0, + calc(50% - 480px + 10px) 0, + calc(50% - 480px + 10px) 0; + } + + /* Dashed side lines along the viewer edges */ + html::before, + html::after { + content: ""; + position: fixed; + top: 0; + bottom: 0; + width: 1px; + background: repeating-linear-gradient( + to bottom, + rgba(212, 168, 75, 0.25) 0, + rgba(212, 168, 75, 0.25) 4px, + transparent 4px, + transparent 9px + ); + z-index: 49; + pointer-events: none; + } + + html::before { + left: max(0px, calc(50% - 480px)); + } + + html::after { + right: max(0px, calc(50% - 480px)); + } + + /* Cursor glow dot */ + .cursor-dot { + position: fixed; + pointer-events: none; + border-radius: 50%; + width: 400px; + height: 400px; + background: radial-gradient( + circle, + rgba(212, 168, 75, 0.06) 0%, + rgba(212, 168, 75, 0.02) 45%, + transparent 70% + ); + transform: translate(-50%, -50%); + z-index: 10000; + opacity: 0; + transition: + opacity 0.3s ease, + left 0.12s ease-out, + top 0.12s ease-out; + } + + /* Ink drop effect */ + .ink-drop { + position: fixed; + pointer-events: none; + border-radius: 50%; + background: radial-gradient( + circle, + rgba(212, 168, 75, 0.15) 0%, + rgba(196, 57, 49, 0.06) 50%, + transparent 100% + ); + width: 0; + height: 0; + transform: translate(-50%, -50%); + animation: inkSpread 0.9s ease-out forwards; + z-index: 9999; + } + + @keyframes inkSpread { + 0% { + width: 0; + height: 0; + opacity: 1; + } + 100% { + width: 500px; + height: 500px; + opacity: 0; + } } a { color: #d4a84b; @@ -83,6 +194,24 @@ color: #d4a84b; } + nav .nav-links .btn-nav { + display: inline-block; + padding: 0.35rem 1rem; + border: 1px solid #d4a84b; + border-radius: 20px; + color: #d4a84b; + font-weight: 600; + font-size: 0.85rem; + transition: + background 0.2s, + color 0.2s; + } + + nav .nav-links .btn-nav:hover { + background: #d4a84b; + color: #1a1410; + } + .viewer { margin-top: 4rem; padding: 1.5rem; @@ -184,6 +313,45 @@ font-variant-ligatures: inherit; } + /* Line numbers + hover highlight */ + .code-frame pre { + position: relative; + } + + .code-frame pre code { + counter-reset: line; + } + + .code-frame pre code .line { + display: block; + counter-increment: line; + } + + .code-frame pre code .line::before { + content: counter(line); + display: inline-block; + width: 2.5ch; + margin-right: 3ch; + text-align: right; + color: #6a5a4a; + user-select: none; + } + + .code-line-highlight { + position: absolute; + left: 0; + right: 0; + height: 28px; + background: rgba(255, 255, 255, 0.1); + border-left: 2px solid rgba(255, 255, 255, 0.45); + pointer-events: none; + opacity: 0; + transition: + opacity 0.15s ease, + top 0.08s ease; + z-index: 3; + } + .doc-box { background: #241c16; border: 1px solid #3a2e24; @@ -226,6 +394,13 @@ color: #e8ddd0; } + /* Markdown code blocks: drop the hljs theme's fg/bg, keep the pre styling */ + .doc-box pre code.hljs { + background: transparent !important; + color: #c0b0a0; + padding: 0 !important; + } + .loading { text-align: center; padding: 4rem 1rem; @@ -282,6 +457,12 @@ target="_blank" >GitHub</a > + <a + href="https://crates.io/crates/mingling" + target="_blank" + class="btn-nav" + >cargo add mingling</a + > </div> </nav> @@ -325,6 +506,33 @@ </p> </footer> + <script> + // Persistent glow dot following the mouse + var cursorDot = document.createElement("div"); + cursorDot.className = "cursor-dot"; + document.body.appendChild(cursorDot); + + document.addEventListener("mousemove", function (e) { + cursorDot.style.left = e.clientX + "px"; + cursorDot.style.top = e.clientY + "px"; + if (cursorDot.style.opacity !== "1") { + cursorDot.style.opacity = "1"; + } + }); + + // Ink drop ripple on click + document.addEventListener("click", function (e) { + var drop = document.createElement("div"); + drop.className = "ink-drop"; + drop.style.left = e.clientX + "px"; + drop.style.top = e.clientY + "px"; + document.body.appendChild(drop); + setTimeout(function () { + drop.remove(); + }, 1000); + }); + </script> + <script src="scripts/highlight/highlight.min.js"></script> <script src="scripts/highlight/rust.min.js"></script> <script src="scripts/highlight/bash.min.js"></script> @@ -390,10 +598,18 @@ i++; } i++; // skip closing ``` - var codeText = escapeHtml(codeLines.join("\n")); + var rawCode = codeLines.join("\n"); + var highlighted; + if (lang && hljs.getLanguage(lang)) { + highlighted = hljs.highlight(rawCode, { + language: lang, + }).value; + } else { + highlighted = escapeHtml(rawCode); + } html += - "<pre style=\"background:#1a1410;border:1px solid #3a2e24;border-radius:2px;padding:0.75rem;margin:0.5rem 0;overflow-x:auto;font-size:0.8rem;line-height:1.5;font-family:'JetBrains Mono','Noto Serif SC',monospace;font-variant-ligatures:contextual\"><code>" + - codeText + + "<pre style=\"background:#1a1410;border:1px solid #3a2e24;border-radius:2px;padding:0.75rem;margin:0.5rem 0;overflow-x:auto;font-size:0.8rem;line-height:1.5;font-family:'JetBrains Mono','Noto Serif SC',monospace;font-variant-ligatures:contextual\"><code class=\"hljs\">" + + highlighted + "</code></pre>"; continue; } @@ -594,6 +810,18 @@ delete codeContent.dataset.highlighted; hljs.highlightElement(codeContent); + // Wrap each line with a .line span for numbering + codeContent.innerHTML = codeContent.innerHTML + .split("\n") + .map(function (line) { + return ( + '<span class="line">' + + line + + "</span>" + ); + }) + .join(""); + tabsEl .querySelectorAll("button") .forEach(function (b) { @@ -627,6 +855,35 @@ tabsEl.appendChild(btn); }); + // Line highlight on hover + (function () { + var pre = codePre; + var code = codeContent; + var hl = document.createElement("div"); + hl.className = "code-line-highlight"; + pre.appendChild(hl); + + var lineHeight = parseFloat( + getComputedStyle(code).lineHeight, + ); + + pre.addEventListener("mousemove", function (e) { + var rect = pre.getBoundingClientRect(); + var y = e.clientY - rect.top; + var index = Math.floor(y / lineHeight); + var lineCount = code.innerText.split("\n").length; + if (index < 0) index = 0; + if (index >= lineCount) index = lineCount - 1; + hl.style.top = index * lineHeight + "px"; + hl.style.height = lineHeight + "px"; + hl.style.opacity = "1"; + }); + + pre.addEventListener("mouseleave", function () { + hl.style.opacity = "0"; + }); + })(); + // Load first file if (files.length > 0) loadFile(files[0]); } diff --git a/docs/examples.html b/docs/examples.html index a200000..9d2ae8c 100644 --- a/docs/examples.html +++ b/docs/examples.html @@ -33,6 +33,37 @@ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; overflow-x: hidden; + background-image: + linear-gradient( + to right, + rgba(212, 168, 75, 0.03) 1px, + transparent 1px + ), + linear-gradient( + to bottom, + rgba(212, 168, 75, 0.03) 1px, + transparent 1px + ), + linear-gradient( + to right, + rgba(212, 168, 75, 0.015) 1px, + transparent 1px + ), + linear-gradient( + to bottom, + rgba(212, 168, 75, 0.015) 1px, + transparent 1px + ); + background-size: + 40px 40px, + 40px 40px, + 20px 20px, + 20px 20px; + background-position: + calc(50% - 550px + 20px) 0, + calc(50% - 550px) 0, + calc(50% - 550px + 10px) 0, + calc(50% - 550px + 10px) 0; } a { @@ -55,6 +86,86 @@ padding: 0 1.5rem; } + /* Dashed side lines along the container edges */ + html::before, + html::after { + content: ""; + position: fixed; + top: 0; + bottom: 0; + width: 1px; + background: repeating-linear-gradient( + to bottom, + rgba(212, 168, 75, 0.25) 0, + rgba(212, 168, 75, 0.25) 4px, + transparent 4px, + transparent 9px + ); + z-index: 49; + pointer-events: none; + } + + html::before { + left: max(0px, calc(50% - 550px)); + } + + html::after { + right: max(0px, calc(50% - 550px)); + } + + /* Cursor glow dot */ + .cursor-dot { + position: fixed; + pointer-events: none; + border-radius: 50%; + width: 400px; + height: 400px; + background: radial-gradient( + circle, + rgba(212, 168, 75, 0.06) 0%, + rgba(212, 168, 75, 0.02) 45%, + transparent 70% + ); + transform: translate(-50%, -50%); + z-index: 10000; + opacity: 0; + transition: + opacity 0.3s ease, + left 0.12s ease-out, + top 0.12s ease-out; + } + + /* Ink drop effect */ + .ink-drop { + position: fixed; + pointer-events: none; + border-radius: 50%; + background: radial-gradient( + circle, + rgba(212, 168, 75, 0.15) 0%, + rgba(196, 57, 49, 0.06) 50%, + transparent 100% + ); + width: 0; + height: 0; + transform: translate(-50%, -50%); + animation: inkSpread 0.9s ease-out forwards; + z-index: 9999; + } + + @keyframes inkSpread { + 0% { + width: 0; + height: 0; + opacity: 1; + } + 100% { + width: 500px; + height: 500px; + opacity: 0; + } + } + /* ── Nav ── */ nav { position: fixed; @@ -107,7 +218,7 @@ display: inline-block; padding: 0.35rem 1rem; border: 1px solid #d4a84b; - border-radius: 2px; + border-radius: 20px; color: #d4a84b; font-weight: 600; font-size: 0.85rem; @@ -486,5 +597,32 @@ .replace(/"/g, """); } </script> + + <script> + // Persistent glow dot following the mouse + var cursorDot = document.createElement("div"); + cursorDot.className = "cursor-dot"; + document.body.appendChild(cursorDot); + + document.addEventListener("mousemove", function (e) { + cursorDot.style.left = e.clientX + "px"; + cursorDot.style.top = e.clientY + "px"; + if (cursorDot.style.opacity !== "1") { + cursorDot.style.opacity = "1"; + } + }); + + // Ink drop ripple on click + document.addEventListener("click", function (e) { + var drop = document.createElement("div"); + drop.className = "ink-drop"; + drop.style.left = e.clientX + "px"; + drop.style.top = e.clientY + "px"; + document.body.appendChild(drop); + setTimeout(function () { + drop.remove(); + }, 1000); + }); + </script> </body> </html> diff --git a/docs/pages/1-getting-started.md b/docs/pages/1-getting-started.md index 443ed74..0131ec1 100644 --- a/docs/pages/1-getting-started.md +++ b/docs/pages/1-getting-started.md @@ -13,7 +13,7 @@ Add the following to `Cargo.toml`: ```toml [dependencies.mingling] -version = "0.4.0" +version = "0.5.0" features = [] ``` @@ -25,7 +25,7 @@ Some features **directly affect the entire lifecycle behavior**, so you need to ```toml [dependencies.mingling] -version = "0.4.0" +version = "0.5.0" features = [ "parser", "comp", diff --git a/docs/res/guide.txt b/docs/res/guide.txt index 74f9c69..e84f893 100644 --- a/docs/res/guide.txt +++ b/docs/res/guide.txt @@ -5,5 +5,5 @@ │ > cargo add mingling │ │ │ │ Or add this to your Cargo.toml │ - │ > mingling = "0.4.0" │ + │ > mingling = "0.5.0" │ └────────────────────────────────────┘ diff --git a/docs/scripts/display-parse.js b/docs/scripts/display-parse.js new file mode 100644 index 0000000..ca7b391 --- /dev/null +++ b/docs/scripts/display-parse.js @@ -0,0 +1,269 @@ +/*! + * MlingDisplay — client-side port of mingling_cli/src/utils/display.rs + * + * Parses the same markdown-ish markup used by `display.rs` and returns HTML + * with the equivalent ANSI styling applied as CSS classes / inline colors. + * + * Supported syntax: + * - Bold: **text** + * - Italic: *text* + * - Underline: _text_ + * - Cyan: <text> + * - Inline code: `text` (green) + * - Colors: [[color_name]] ... [[/]] (nested) + * - Escapes: \* \< \> \` \_ + * - Headings: # Heading 1 ... ###### Heading 6 + * - Blockquote: > text + * + * Exposes: window.MlingDisplay.parse(text) -> HTML string + */ +(function () { + "use strict"; + + function findMatch(chars, start, delimiter) { + var len = delimiter.length; + for (var j = start; j < chars.length; j++) { + if (len === 1) { + if (chars[j] === delimiter) return j; + } else if ( + j + 1 < chars.length && + chars[j] === delimiter[0] && + chars[j + 1] === delimiter[1] + ) { + return j; + } + } + return -1; + } + + var COLOR_MAP = { + black: "#000", + red: "#ff7b72", + green: "#7ee787", + yellow: "#d4a84b", + blue: "#79c0ff", + magenta: "#d2a8ff", + cyan: "#79c0ff", + white: "#e8ddd0", + b_white: "#e8ddd0", + bright_gray: "#6a5a4a", + bright_grey: "#6a5a4a", + b_gray: "#6a5a4a", + b_grey: "#6a5a4a", + bright_black: "#6a5a4a", + b_black: "#6a5a4a", + gray: "#6a5a4a", + grey: "#6a5a4a", + bright_red: "#ff7b72", + b_red: "#ff7b72", + bright_green: "#7ee787", + b_green: "#7ee787", + bright_yellow: "#d4a84b", + b_yellow: "#d4a84b", + bright_blue: "#79c0ff", + b_blue: "#79c0ff", + bright_magenta: "#d2a8ff", + b_magenta: "#d2a8ff", + bright_cyan: "#79c0ff", + b_cyan: "#79c0ff", + bright_white: "#e8ddd0", + }; + + function applyColorStack(text, stack) { + var wrapped = text; + for (var k = stack.length - 1; k >= 0; k--) { + var css = COLOR_MAP[stack[k]]; + if (css) { + wrapped = + '<span style="color:' + css + '">' + wrapped + "</span>"; + } + } + return wrapped; + } + + function escapeHtml(s) { + return s + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">"); + } + + function processLine(line) { + var chars = Array.from(line); + var result = ""; + var colorStack = []; + var i = 0; + + while (i < chars.length) { + var c = chars[i]; + + // Escape sequences \\* \\< \\> \\` \\_ + if ( + c === "\\" && + i + 1 < chars.length && + ["*", "<", ">", "`", "_"].indexOf(chars[i + 1]) !== -1 + ) { + result += escapeHtml(chars[i + 1]); + i += 2; + continue; + } + + // Color tags [[color]] / [[/]] + if (c === "[" && chars[i + 1] === "[") { + var tagEnd = -1; + for (var t = i + 2; t + 1 < chars.length; t++) { + if (chars[t] === "]" && chars[t + 1] === "]") { + tagEnd = t; + break; + } + } + if (tagEnd !== -1) { + var tag = chars.slice(i + 2, tagEnd).join(""); + if (tag === "/") colorStack.pop(); + else colorStack.push(tag); + i = tagEnd + 2; + continue; + } + } + + // **bold** + if (c === "*" && chars[i + 1] === "*") { + var bEnd = findMatch(chars, i + 2, "**"); + if (bEnd !== -1) { + var inner = processLine(chars.slice(i + 2, bEnd).join("")); + result += applyColorStack( + "<b>" + inner + "</b>", + colorStack, + ); + i = bEnd + 2; + continue; + } + } + + // *italic* + if (c === "*") { + var iEnd = findMatch(chars, i + 1, "*"); + if (iEnd !== -1) { + var italic = processLine(chars.slice(i + 1, iEnd).join("")); + result += applyColorStack( + "<i>" + italic + "</i>", + colorStack, + ); + i = iEnd + 1; + continue; + } + } + + // _underline_ + if (c === "_") { + var uEnd = findMatch(chars, i + 1, "_"); + if (uEnd !== -1) { + var uText = processLine(chars.slice(i + 1, uEnd).join("")); + result += applyColorStack( + "<u>" + uText + "</u>", + colorStack, + ); + i = uEnd + 1; + continue; + } + } + + // <angle> cyan + if (c === "<") { + var cEnd = findMatch(chars, i + 1, ">"); + if (cEnd !== -1) { + var angle = chars.slice(i, cEnd + 1).join(""); + result += applyColorStack( + '<span class="t-cyan">' + escapeHtml(angle) + "</span>", + colorStack, + ); + i = cEnd + 1; + continue; + } + } + + // `code` green + if (c === "`") { + var gEnd = findMatch(chars, i + 1, "`"); + if (gEnd !== -1) { + var codeText = chars.slice(i, gEnd + 1).join(""); + result += applyColorStack( + '<span class="t-green">' + + escapeHtml(codeText) + + "</span>", + colorStack, + ); + i = gEnd + 1; + continue; + } + } + + // Regular character + result += applyColorStack(escapeHtml(c), colorStack); + i += 1; + } + return result; + } + + function processLineWithQuote(line) { + var chars = Array.from(line); + if (chars.length && chars[0] === ">") { + if (chars.length > 1 && chars[1] === "\\") { + return processLine(line); + } + var rest = chars.length > 1 ? chars.slice(1).join("") : ""; + return '<span class="t-quote"> </span>' + processLine(rest); + } + return processLine(line); + } + + /** + * Parse display.rs-flavored markup into HTML. + * @param {string} text - Raw markup text. + * @returns {string} HTML with t-cyan / t-green / t-heading / t-quote classes + * and inline colors for [[color]] tags. + */ + function parse(text) { + var lines = String(text).split("\n"); + var result = ""; + var contentIndent = 0; + for (var n = 0; n < lines.length; n++) { + var line = lines[n]; + var trimmed = line.trim(); + var ls = line.trimStart(); + var lineResult = ""; + + if (ls.startsWith("#")) { + var level = 0; + while (level < ls.length && level < 7 && ls[level] === "#") { + level++; + } + var effective = level > 6 ? 6 : level; + var start = level; + while ( + start < ls.length && + (ls[start] === " " || ls[start] === "\t") + ) { + start++; + } + var content = start < ls.length ? ls.slice(start) : ""; + var heading = + '<span class="t-heading"> ' + + processLine(content) + + " </span>"; + var indent = " ".repeat(effective > 0 ? effective - 1 : 0); + lineResult = indent + heading; + contentIndent = effective; + } else if (trimmed !== "") { + lineResult = + " ".repeat(contentIndent) + processLineWithQuote(trimmed); + } else { + lineResult = " "; + } + result += lineResult + "\n"; + } + return result; + } + + window.MlingDisplay = { parse: parse }; +})(); |
