diff options
Diffstat (limited to 'docs/dev/pages/issues')
| -rw-r--r-- | docs/dev/pages/issues/.name | 1 | ||||
| -rw-r--r-- | docs/dev/pages/issues/remove-r-print-macro.md | 97 | ||||
| -rw-r--r-- | docs/dev/pages/issues/the-mod-pathfinder.md | 64 | ||||
| -rw-r--r-- | docs/dev/pages/issues/the-shit-time.md | 100 |
4 files changed, 262 insertions, 0 deletions
diff --git a/docs/dev/pages/issues/.name b/docs/dev/pages/issues/.name new file mode 100644 index 0000000..b207fb4 --- /dev/null +++ b/docs/dev/pages/issues/.name @@ -0,0 +1 @@ +❓ Issues diff --git a/docs/dev/pages/issues/remove-r-print-macro.md b/docs/dev/pages/issues/remove-r-print-macro.md new file mode 100644 index 0000000..96f99ef --- /dev/null +++ b/docs/dev/pages/issues/remove-r-print-macro.md @@ -0,0 +1,97 @@ +<h1 align="center">Remove r_print! and r_println! Macros</h1> + +`r_print!` and `r_println!` are important macros in Mingling for use inside `#[help]` and `#[renderer]` functions, but their implementation is not clean: they implicitly introduce a `__renderer_inner_result` field. While this might look elegant at the API level, it is **incorrect** and even **objectionable**. + +## Why **Objectionable**? + +Because you can't define declarative macros with `macro_rules` that wrap them. + +This is because `r_println!` depends on the implicit variable `__renderer_inner_result` injected by the `#[renderer]` proc macro into the function body. However, when a `macro_rules` declarative macro expands, **its internal code is placed in the caller's context**, which does not contain `__renderer_inner_result` — that variable only exists within the direct scope of the function body processed by `#[renderer]`. + +Let's look at some code to see why: + +```rust +// Suppose you want to write a wrapper macro: +macro_rules! my_println { + ($($arg:tt)*) => { + // When expanded here, the context is the call site of my_println!, + // not the location where the renderer function's injected variables live. + // So __renderer_inner_result is NOT visible here! + r_println!("Custom: {}", format!($($arg)*)); + }; +} + +#[renderer] +fn render_something(_p: ResultSomething) { + // Although this function body has __renderer_inner_result injected, + // the code from my_println! does NOT expand "inside this function body" — + // macro_rules expansion is essentially text replacement. The replaced code + // lives at the line where my_println! is called, and any variables referenced + // inside that macro must resolve to identifiers accessible at the call site. + // __renderer_inner_result is not a public, path-accessible variable; + // it's a hygienic local variable generated by the `#[renderer]` macro, + // and external macros cannot directly access it by name. + my_println!("{}", box_val); // Compile error: cannot find __renderer_inner_result +} +``` + +## Deeper Issues + +I have to admit, this is an early design flaw. After re-examining the code, I found the problem goes beyond "can't be wrapped". + +This isn't just a "can't wrap" issue — it reflects that `r_println!`'s design fundamentally violates Rust's macro hygiene principles: + +- **Implicit dependency**: Users of the macro must know that a variable named `__renderer_inner_result` exists — but this variable is neither part of the public API nor explicitly documented anywhere. +- **Scope leakage**: Variables injected by a proc macro should be confined to the scope processed by that macro. But `r_println!` attempts to make that variable accessible across macro calls, which effectively breaks Rust's identifier hygiene. +- **Non-composable**: Any attempt to wrap `r_println!` will fail, because declarative macros cannot "pass through" access to implicit variables. Even using a proc macro to wrap it would encounter similar hygiene issues. + +## Desired New Syntax + +I've designed two alternative approaches and will choose based on actual needs. + +### Option 1: Explicit Return + +```rust +#[renderer] +fn render_something(prev: ResultSomething) -> RenderResult { + let mut result = RenderResult::new(); + result.println(prev.to_string()); + // or + write!(result, "{}", prev.to_string()); + + result // return here +} +``` + +Clear boundaries — the entire rendering process is confined within the function body decorated by `#[help]` or `#[renderer]`, without introducing extra out-of-scope dependencies. The trade-off is slightly more boilerplate compared to the original approach. + +### Option 2: Resource Injection + +```rust +#[renderer] +fn render_something(prev: ResultSomething, result: &mut ResRenderResult) { + result.println(prev.to_string()); + // or + write!(result, "{}", prev.to_string()); + + result // return here +} +``` + +More flexible, but blurs the boundary between logic functions like `#[chain]` and rendering functions like `#[help]`. + +### Preferred Direction + +I lean toward **Option 1 (Explicit Return)**. There's no need to turn `RenderResult` into `ResRenderResult` as a global resource. + +As for rendering in logic functions like `#[chain]`, that should be handled by a separate system — not discussed here. + +## 🕘 Progress + +- [ ] In Progress + - [ ] Remove `r_println!` and `r_print!` macros + - [ ] Modify `#[renderer]` and `#[help]` macros, remove implicit injection + - [ ] Provide **no-return-value mode** and **RenderResult return value mode** for `#[renderer]` and `#[help]` macros + - [ ] Add new simplified syntax + - [ ] Update documentation and test cases, ensure **all pass** +- [ ] Complete diff --git a/docs/dev/pages/issues/the-mod-pathfinder.md b/docs/dev/pages/issues/the-mod-pathfinder.md new file mode 100644 index 0000000..676251d --- /dev/null +++ b/docs/dev/pages/issues/the-mod-pathfinder.md @@ -0,0 +1,64 @@ +<h1 align="center">The Mod Pathfinder</h1> +<p align="center"> + A build-time analyzer that computes full module paths for Mingling types, resolving path ambiguity in macros. +</p> + +## Background + +Currently, `gen_program!` requires all involved types to be `use`d within their module. Mingling lacks a complete module path analyzer — waiting for `proc-macro-span` to stabilize is clearly not practical, so a solution for obtaining module paths is needed. + +## Solution + +We plan to create an analyzer called `mingling-mod-pathf`, enabled via Mingling's `"pathf"` feature, to compute the full paths of all defined Mingling types. + +### Behavior When Enabled + +**`mingling_core`**: If the `builds` feature is enabled, introduces the `mingling::build::analyze_and_build_type_mapping()` method (analysis completed at Build-Time) + +**`mingling_macros`**: Modifies the behavior of the `gen_program!()` macro — automatically loads the mapping table from the analysis file generated by `mingling::build::analyze_and_build_type_mapping()`, and directly uses the full `mod::path` instead of `TypeName` (injected at Compile-Time) + +## Challenges + +`mingling-mod-pathf` needs to understand **all** Mingling syntax features. +Fortunately, Mingling's type creation is almost always explicit: + +```rust +mod sub { + mingling::macros::pack!(ResultMyName = String); // directly creates ..::sub::ResultMyName +} +``` + +There are a few exceptions, such as the implicit Dispatcher provided by `extra_macros`, but these can be inferred from the node name: + +```rust +dispatcher!("remote.add"); // although the type is unknown, we can infer CMDRemoteAdd and EntryRemoteAdd +``` + +And also `#[program_setup]`: + +```rust +#[program_setup] // can infer CustomSetup from the function name `custom_setup` +fn custom_setup(program: &mut Program<ThisProgram>) { + program.with_dispatchers((CMD1, CMD2, CMD3, CMD4, CMD5)); +} +``` + +## Pathf Output Format + +Uses TOML key-value pairs, formatted as follows: + +```toml +ResultRemoteAdd = "crate::mymod::ResultRemoteAdd" +``` + +Recommended storage location is under the target directory: + +``` +/target/{target}/{crate-name}/type-mapping.toml +``` + +## Other Issues + +This solution is limited to Mingling's own syntax system. If types like `dispatcher!`, `pack!` are indirectly expanded through macros, the analyzer will not be able to discover them. + +However, this approach solves the current main pain points, so this issue can be set aside for now and addressed later. diff --git a/docs/dev/pages/issues/the-shit-time.md b/docs/dev/pages/issues/the-shit-time.md new file mode 100644 index 0000000..9d6c429 --- /dev/null +++ b/docs/dev/pages/issues/the-shit-time.md @@ -0,0 +1,100 @@ +<h1 align="center">Some Situations Where You'd Be Like "Shit!"</h1> +<p align="center"> + This document collects the discomforts currently experienced while using Mingling. +</p> + +This document collects the discomforts currently experienced while using Mingling. + +Of course, you can also contribute to this document. + +--- + +## Why is there no fallback completion logic? + +(completion) (fallback) + +Currently, Mingling's Completion only supports providing completion logic for specific subcommands, with no way to provide global completion. + +For example: + +``` +mycmd <tab> +completion: +--help -h --- Display helps +--version -V --- Display versions +``` + +Currently, there is no workaround. + +Ideal solution: + +```rust +#[completion(EntryGlobal)] +fn complete(ctx: &ShellContext) -> Suggest { + // ... +} +``` + +--- + +## Why can't I register descriptions for commands? + +(completion) (dispatcher) + +Currently, Mingling's Completion cannot register a description for each subcommand. + +For example: + +``` +mycmd <tab> +completion: +add rm list <--- You cannot register descriptions for commands +``` + +Expected behavior: + +``` +mycmd <tab> +completion: +add --- Add something +rm --- Remove something +list --- List something +``` + +Ideal solution: + +```rust +// It should be able to freely integrate with crates that provide i18n functionality, +// so the following approach cannot be used as a data source for descriptions. +dispatcher! { + /// Add Something <--- How to i18n? + "add", CMDAdd => EntryAdd +} + +// Ideally, it should satisfy the following two conditions: +// 1. No need to use `with_dispatcher`, because `with_dispatcher` is disabled in `dispatch_tree` mode +// 2. Must be able to accept String or &str at runtime + +// Current idea +#[inline(always)] +#[dispatcher_desc(EntryAdd)] +fn desc_add() -> String { + // If using rust_i18n + t!("cmd.add.desc") +} + +// Or + +#[completion(CMDAdd)] +fn desc_add(_ctx: &ShellContext) -> Suggest { + // If using rust_i18n + suggest{ + t!("cmd.add.desc") + } +} + +// Collected and generated by `gen_program!()` +// Generate something like get_dispatcher_desc(id: &ThisProgram) -> String +// Match the corresponding function using enum values inside ThisProgram +gen_program!() +``` |
