Commits: 6

Use more block assertions in self-check

index c4f2996..f52866b 100644
--- a/spec/basic-usage.md
+++ b/spec/basic-usage.md
@@ -34,20 +34,35 @@ TODO: Mention `run` and `list` commands.
=
=  * Run the program with `list samples/basic.md` command line arguments
=  * The exit code should be `0`
-  * The output will contain `Basic BDD suite`
-  * The output will contain `\(python -m samples.basic\)`
-  * The output will contain `\* Arithmetic`
-  * The output will contain `\d{2}. Add 7 and 5 to get 12 \["7", "5", "12"\]`
-  * The output will contain `\d+. Divide 10 by 4 to get 2.5 \["10", "4", "2.5"\]`
-  * The output will contain `\d+. Subtract 7 from 5 to get -2 \["7", "5", "-2"\]`
-  * The output will contain `\* Text`
-  * The output will contain `\d{2}. The word blocks has 6 characters \["blocks", "6"\]`
-  * The output will contain `\d{2}. There are 3 properties in the following JSON \["3"\]`
-  * The output will contain `\d{2}. There are 3 rs in the word strawberry \["3", "r", "strawberry"\]`
-  * The output will contain `\d{2}. The following table maps words to their lengths \[\]`
-  * The output will contain `\d{2}. The reverse of abc is cba \["abc", "cba"\]`
-  * The output will contain `\d{2}. The reverse of CIA is KGB \["CIA", "KGB"\]`
-  * The output will contain `\d{2}. There are 2 os in the word boost \["2", "o", "boost"\]`
+  * The output will contain `the suite` block
+  
+    ``` text 
+    Basic BDD suite (python -m samples.basic)
+    ```
+
+  * The output will contain `the Aritmetic scenario` block
+
+    ``` text
+      * Arithmetic
+
+        00. Add 7 and 5 to get 12 ["7", "5", "12"]
+        01. Divide 10 by 4 to get 2.5 ["10", "4", "2.5"]
+        02. Subtract 7 from 5 to get -2 ["7", "5", "-2"]
+    ```
+
+  * The output will contain `the Text scenario` block
+
+    ``` text
+      * Text
+
+        00. The word blocks has 6 characters ["blocks", "6"]
+        01. There are 3 properties in the following JSON ["3"]
+        02. There are 3 rs in the word strawberry ["3", "r", "strawberry"]
+        03. The following table maps words to their lengths []
+        04. The reverse of abc is cba ["abc", "cba"]
+        05. The reverse of CIA is KGB ["CIA", "KGB"]
+        06. There are 2 os in the word boost ["2", "o", "boost"]
+    ```
=
=
=## Running a spec from a single document

Let tbb.py utilize the hint field on failure

If the exception message is longer than one line, treat the rest as hint. This produces more readable output in reports.

index 9d4ce31..b4824af 100644
--- a/spec/self-check.py
+++ b/spec/self-check.py
@@ -11,6 +11,8 @@ import spec.tbb as tbb
=from spec.tbb import step, log, indent_tail
=
=
+log.name = "self-check"
+
=base_command = "tbb"
=if os.environ.get ("CARGO"):
=    cargo_command = "cargo run --"
@@ -49,6 +51,8 @@ def step_implementation_02(pattern: str, **kwargs):
=    # tester.assertRegex (output, expected_text)
=
=    assert re.search(pattern, output), dedent(f"""
+    pattern not found
+    
=    ``` regular-expression
=    {pattern} 
=    ```
@@ -70,6 +74,8 @@ def step_implementation_03(pattern: str, **kwargs):
=    # tester.assertRegex (output, expected_text)
=
=    assert re.search(pattern, output), dedent(f"""
+    pattern not found
+
=    ``` regular-expression
=    {pattern} 
=    ```
@@ -90,6 +96,8 @@ def step_implementation_04(label: str, **kwargs):
=    # tester.assertIn gives unreadable output
=
=    assert block in output, dedent(f"""
+    block not found
+
=    ``` text
=    {tbb.indent_tail(block, "    ")}
=    ```
@@ -100,5 +108,5 @@ def step_implementation_04(label: str, **kwargs):
=    {tbb.indent_tail(output, "    ")}
=    ```
=    """)
- 
+
=tbb.ready()
index 1b96e52..1b193be 100644
--- a/spec/tbb.py
+++ b/spec/tbb.py
@@ -16,7 +16,7 @@ from typing import Any, Callable
=# Setup logging
=log_level = os.environ.get("tbb_interpreter_log", "warn").upper()
=logging.basicConfig(level=getattr(logging, log_level))
-log = logging.getLogger("basic_interpreter")
+log = logging.getLogger("tbb.py")
=
=
=def send(message):
@@ -98,6 +98,7 @@ def ready():
=    # Loop over input
=    while True:
=        log.debug("Awaiting message...")
+        # TODO: The main loop is very hairy. Try to extract some testable functions.
=
=        try:
=            received = input()
@@ -131,7 +132,21 @@ def ready():
=                    implementation(*arguments, **step)
=                    send({ "type": 'Success' })
=                except Exception as error:
-                    send({ "type": "Failure", "reason": str(error) })
+                    import itertools
+                    
+                    reason, *hintlines = str(error).strip().splitlines()
+                    response = {
+                        "type": "Failure",
+                        "reason": reason,
+                    }
+
+                    match itertools.dropwhile(lambda line: "" == line.strip(), hintlines):
+                        case []: None
+                        case lines:
+                            hint = "\n".join(lines)
+                            response["hint"] = hint
+                            
+                    send(response)
=
=            else:
=                # Generate helpful hint
@@ -151,7 +166,7 @@ def ready():
=                def {name}({str.join(", ", params + [ "**kwargs" ])}):
=                    assert "cat" == "dog", "Obviously you need to replace this!"
=                ```
-                """)
+                """).strip()
=
=                log.warning(f"Not implemented: {variant}")
=                send({

Specify displaying tags and sub-commands comments

index f61f2ac..60889a3 100644
--- a/samples/basic.md
+++ b/samples/basic.md
@@ -1,9 +1,16 @@
=---
=interpreter: "python -m samples.basic"
+tags: 
+  - basic
+  - sample
=---
=
=# Basic BDD suite
=
+``` yaml tbb
+tags: [tutorial]
+```
+
=This suite contains several simple **scenarios**. Use it as a reference to get started with Tad Better Behavior.
=
=Scenarios are delimited by 2nd level heading (`## Heading` in markdown). Each bullet point inside a scenario defines a **step**. 
@@ -13,6 +20,10 @@ For each scenario the **interpreter** program (see the front-matter above) will
=
=## Arithmetic
=
+``` yaml tbb
+tags: [math]
+```
+
=Content outside of bullet points (like this) won't have any effect on the program. You can user it as a humane description of the scenario, or for any other purpose.
=
=  * Add `7` and `5` to get `12`
@@ -22,6 +33,14 @@ Content outside of bullet points (like this) won't have any effect on the progra
=
=## Text
=
+``` yaml tbb
+tags: 
+  - strings
+  - work-in-progress
+```
+
+This 
+
=  * The word `blocks` has `6` characters
=  * There are `3` properties in the following JSON
=
index f52866b..2b05695 100644
--- a/spec/basic-usage.md
+++ b/spec/basic-usage.md
@@ -12,7 +12,7 @@ interpreter: "python -m spec.self-check"
=  * The output will contain `-h, +--help +Print help`
=  * The output will contain `-V, +--version +Print version`
=  * The output will contain `-v, +--verbose +Enable verbose logging`
-  * The output will contain `a sub-commands` block
+  * The output will contain `the sub-commands` block
=  
=    ``` text
=    Commands:
@@ -38,12 +38,14 @@ TODO: Mention `run` and `list` commands.
=  
=    ``` text 
=    Basic BDD suite (python -m samples.basic)
+    tagged: basic sample tutorial
=    ```
=
=  * The output will contain `the Aritmetic scenario` block
=
=    ``` text
=      * Arithmetic
+        tagged: math
=
=        00. Add 7 and 5 to get 12 ["7", "5", "12"]
=        01. Divide 10 by 4 to get 2.5 ["10", "4", "2.5"]
@@ -54,6 +56,7 @@ TODO: Mention `run` and `list` commands.
=
=    ``` text
=      * Text
+        tagged: strings work-in-progress
=
=        00. The word blocks has 6 characters ["blocks", "6"]
=        01. There are 3 properties in the following JSON ["3"]
@@ -70,15 +73,16 @@ TODO: Mention `run` and `list` commands.
=A complete sample output is like this:
=
=``` text
-Basic BDD suite (python -m samples.basic)
=
=✓ Arithmetic
-
+  tagged: math
+  
=  ⊞ Add 7 and 5 to get 12 ["7", "5", "12"]
=  ⊞ Divide 10 by 4 to get 2.5 ["10", "4", "2.5"]
=  ⊞ Subtract 7 from 5 to get -2 ["7", "5", "-2"]
=
=✓ Text
+  tagged: strings work-in-progress
=
=  ⊞ The word blocks has 6 characters ["blocks", "6"]
=  ⊞ There are 3 properties in the following JSON ["3"]
@@ -103,19 +107,42 @@ Notice it's similar to the output of `tbb list`, but now contains unicode symbol
=
=     The `basic.md` suit is intentionally wrong. It should be reflected in the status code.
=
-  * The output will contain `Basic BDD suite`
-  * The output will contain `\(python -m samples.basic\)`
-  * The output will contain `✓ Arithmetic`
-  * The output will contain `  ⊞ Add 7 and 5 to get 12 \["7", "5", "12"\]`
-  * The output will contain `  ⊞ Divide 10 by 4 to get 2.5 \["10", "4", "2.5"\]`
-  * The output will contain `  ⊞ Subtract 7 from 5 to get -2 \["7", "5", "-2"\]`
-  * The output will contain `✓ Text`
-  * The output will contain `  ⊞ The word blocks has 6 characters \["blocks", "6"\]`
-  * The output will contain `  ⊞ There are 3 properties in the following JSON \["3"\]`
-  * The output will contain `  ⊞ There are 3 rs in the word strawberry \["3", "r", "strawberry"\]`
-  * The output will contain `  ⊞ The following table maps words to their lengths \[\]`
-  * The output will contain `  ⊞ The reverse of abc is cba \["abc", "cba"\]`
-  * The output will contain `  □ There are 2 os in the word boost \["2", "o", "boost"\]`
+  * The output will contain `the suite header` block
+  
+  ```text
+  Basic BDD suite (python -m samples.basic)
+  tagged: basic sample tutorial
+  ```
+  
+  * The output will contain `arithmetic scenario` block
+  
+  ```text
+  ✓ Arithmetic
+    tagged: math
+    
+    ⊞ Add 7 and 5 to get 12 ["7", "5", "12"]
+    ⊞ Divide 10 by 4 to get 2.5 ["10", "4", "2.5"]
+    ⊞ Subtract 7 from 5 to get -2 ["7", "5", "-2"]
+  ```
+
+  * The output will contain `text scenario` block
+  
+  ```text
+  ✓ Text
+    tagged: strings work-in-progress
+  
+    ⊞ The word blocks has 6 characters ["blocks", "6"]
+    ⊞ There are 3 properties in the following JSON ["3"]
+    ⊞ There are 3 rs in the word strawberry ["3", "r", "strawberry"]
+    ⊞ The following table maps words to their lengths []
+    ⊞ The reverse of abc is cba ["abc", "cba"]
+    ⊠ The reverse of CIA is KGB ["CIA", "KGB"]
+  
+      'KGB' != 'AIC'
+      - KGB
+      + AIC
+  ```
+
=  * The standard error will contain `\[.+ ERROR +tbb\] Step failed: Basic BDD suite ❯ Text ❯ The reverse of CIA is KGB`
=
=## Running without a subcommand

Add descriptions to sub-commands

index 3ac8b83..67f9fb3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -24,12 +24,14 @@ struct Cli {
=
=#[derive(Subcommand)]
=enum Command {
+    /// Print the suites, scenarios and steps of the specification
=    List {
=        /// A directory or a markdown file with the spec to list
=        #[arg(value_name = "SPEC PATH", default_value = "./spec/")]
=        input: PathBuf,
=    },
=
+    /// Evaluate the specification
=    Run {
=        /// A directory or a markdown file with the spec to evaluate
=        #[arg(value_name = "SPEC PATH", default_value = "./spec/")]

Use block assertions to specify help output

index 2b05695..5fb5ce4 100644
--- a/spec/basic-usage.md
+++ b/spec/basic-usage.md
@@ -9,9 +9,18 @@ interpreter: "python -m spec.self-check"
=  * Run the program with `--help` command line arguments
=  * The exit code should be `0`
=  * The output will contain `Usage: tbb`
-  * The output will contain `-h, +--help +Print help`
-  * The output will contain `-V, +--version +Print version`
-  * The output will contain `-v, +--verbose +Enable verbose logging`
+  
+    The name of the program should be included in the output.
+    
+  * The output will contain `the options` block
+  
+    ``` text
+    Options:
+      -v, --verbose  Enable verbose logging
+      -h, --help     Print help
+      -V, --version  Print version
+    ```
+
=  * The output will contain `the sub-commands` block
=  
=    ``` text
@@ -21,8 +30,6 @@ interpreter: "python -m spec.self-check"
=      help  Print this message or the help of the given subcommand(s)
=    ```
=
-TODO: Mention `run` and `list` commands.
-
=## Getting a version
=
=  * Run the program with `--version` command line arguments

Fix the indentation of code blocks in spec

They were outside of the step elements.

index 5fb5ce4..8a2160d 100644
--- a/spec/basic-usage.md
+++ b/spec/basic-usage.md
@@ -116,40 +116,40 @@ Notice it's similar to the output of `tbb list`, but now contains unicode symbol
=
=  * The output will contain `the suite header` block
=  
-  ```text
-  Basic BDD suite (python -m samples.basic)
-  tagged: basic sample tutorial
-  ```
+    ```text
+    Basic BDD suite (python -m samples.basic)
+    tagged: basic sample tutorial
+    ```
=  
=  * The output will contain `arithmetic scenario` block
=  
-  ```text
-  ✓ Arithmetic
-    tagged: math
-    
-    ⊞ Add 7 and 5 to get 12 ["7", "5", "12"]
-    ⊞ Divide 10 by 4 to get 2.5 ["10", "4", "2.5"]
-    ⊞ Subtract 7 from 5 to get -2 ["7", "5", "-2"]
-  ```
+    ```text
+    ✓ Arithmetic
+      tagged: math
+      
+      ⊞ Add 7 and 5 to get 12 ["7", "5", "12"]
+      ⊞ Divide 10 by 4 to get 2.5 ["10", "4", "2.5"]
+      ⊞ Subtract 7 from 5 to get -2 ["7", "5", "-2"]
+    ```
=
=  * The output will contain `text scenario` block
=  
-  ```text
-  ✓ Text
-    tagged: strings work-in-progress
-  
-    ⊞ The word blocks has 6 characters ["blocks", "6"]
-    ⊞ There are 3 properties in the following JSON ["3"]
-    ⊞ There are 3 rs in the word strawberry ["3", "r", "strawberry"]
-    ⊞ The following table maps words to their lengths []
-    ⊞ The reverse of abc is cba ["abc", "cba"]
-    ⊠ The reverse of CIA is KGB ["CIA", "KGB"]
+    ```text
+    ✓ Text
+      tagged: strings work-in-progress
+
+      ⊞ The word blocks has 6 characters ["blocks", "6"]
+      ⊞ There are 3 properties in the following JSON ["3"]
+      ⊞ There are 3 rs in the word strawberry ["3", "r", "strawberry"]
+      ⊞ The following table maps words to their lengths []
+      ⊞ The reverse of abc is cba ["abc", "cba"]
+      ⊠ The reverse of CIA is KGB ["CIA", "KGB"]
+    
+        'KGB' != 'AIC'
+        - KGB
+        + AIC
+    ```
=  
-      'KGB' != 'AIC'
-      - KGB
-      + AIC
-  ```
-
=  * The standard error will contain `\[.+ ERROR +tbb\] Step failed: Basic BDD suite ❯ Text ❯ The reverse of CIA is KGB`
=
=## Running without a subcommand