From caaa368533f6cda17bac9c84255d5d979439c513 Mon Sep 17 00:00:00 2001
From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com>
Date: Sat, 26 Aug 2023 13:42:18 +0400
Subject: [PATCH] feat(documentator): `IncludeExample` can include raw markdown
output via `raw` tag.
---
.../documentator/docs/commands/preprocess.md | 4 ++
.../Instructions/IncludeExample.php | 65 +++++++++++++++----
.../Instructions/IncludeExampleTest.php | 64 +++++++++++++++++-
3 files changed, 118 insertions(+), 15 deletions(-)
diff --git a/packages/documentator/docs/commands/preprocess.md b/packages/documentator/docs/commands/preprocess.md
index 064cdabeb..9cea34285 100644
--- a/packages/documentator/docs/commands/preprocess.md
+++ b/packages/documentator/docs/commands/preprocess.md
@@ -35,6 +35,10 @@ Includes contents of the `` file as an example wrapped into
` ```code block``` `. It also searches for `.run` file, execute
it if found, and include its result right after the code block.
+By default, output of `.run` will be included as ` ```plain text``` `
+block. You can wrap the output into `text` tags to
+insert it as is.
+
#### `[include:exec]: `
* `` - Path to the executable.
diff --git a/packages/documentator/src/Preprocessor/Instructions/IncludeExample.php b/packages/documentator/src/Preprocessor/Instructions/IncludeExample.php
index 216ae3293..ad98bac3b 100644
--- a/packages/documentator/src/Preprocessor/Instructions/IncludeExample.php
+++ b/packages/documentator/src/Preprocessor/Instructions/IncludeExample.php
@@ -10,13 +10,17 @@
use function dirname;
use function is_file;
use function pathinfo;
+use function preg_match;
use function preg_match_all;
+use function preg_replace_callback;
use function trim;
use const PATHINFO_EXTENSION;
+use const PREG_UNMATCHED_AS_NULL;
class IncludeExample extends IncludeFile {
- public const Limit = 20;
+ public const Limit = 20;
+ protected const MarkdownRegexp = '/^\<(?Pmarkdown)\>(?P.*?)\<\/(?P=tag)\>$/msu';
public function __construct(
protected readonly Process $process,
@@ -33,6 +37,10 @@ public static function getDescription(): string {
Includes contents of the `` file as an example wrapped into
` ```code block``` `. It also searches for `.run` file, execute
it if found, and include its result right after the code block.
+
+ By default, output of `.run` will be included as ` ```plain text``` `
+ block. You can wrap the output into `text` tags to
+ insert it as is.
DESC;
}
@@ -51,30 +59,61 @@ public function process(string $path, string $target): string {
$command = $this->getCommand($path, $target);
if ($command) {
+ // Call
try {
$output = $this->process->run($command, dirname($path));
+ $output = trim($output);
} catch (Exception $exception) {
throw new TargetExecFailed($path, $target, $exception);
}
- if (preg_match_all('/\R/u', $output) > static::Limit) {
+ // Markdown?
+ $isMarkdown = (bool) preg_match(static::MarkdownRegexp, $output);
+
+ if ($isMarkdown) {
+ $output = trim(
+ (string) preg_replace_callback(
+ pattern : static::MarkdownRegexp,
+ callback: static function (array $matches): string {
+ return $matches['markdown'];
+ },
+ subject : $output,
+ flags : PREG_UNMATCHED_AS_NULL,
+ ),
+ );
+ }
+
+ // Format
+ $isTooLong = preg_match_all('/\R/u', $output) > static::Limit;
+
+ if ($isMarkdown && $isTooLong) {
+ $output = <<Example output
+
+ {$output}
+
+
+ CODE;
+ } elseif ($isMarkdown) {
+ // as is
+ } elseif ($isTooLong) {
$output = <<Output
+ Example output
- ```plain
- $output
- ```
+ ```plain
+ {$output}
+ ```
-
- CODE;
+
+ CODE;
} else {
$output = <<Output
+ Example output
```plain
{$output}
@@ -106,4 +106,64 @@ public function testProcessLongOutput(): void {
$instance->process($path, $file),
);
}
+
+ public function testProcessMarkdown(): void {
+ $path = self::getTestData()->path('~runnable.md');
+ $file = basename(self::getTestData()->path('~runnable.md'));
+ $expected = trim(self::getTestData()->content('~runnable.md'));
+ $output = 'command output';
+ $process = Mockery::mock(Process::class);
+ $process
+ ->shouldReceive('run')
+ ->with([self::getTestData()->path('~runnable.run')], dirname($path))
+ ->once()
+ ->andReturn("{$output}");
+
+ $instance = $this->app->make(IncludeExample::class, [
+ 'process' => $process,
+ ]);
+
+ self::assertEquals(
+ <<process($path, $file),
+ );
+ }
+
+ public function testProcessMarkdownLongOutput(): void {
+ $path = self::getTestData()->path('~runnable.md');
+ $file = self::getTestData()->path('~runnable.md');
+ $expected = trim(self::getTestData()->content('~runnable.md'));
+ $output = implode("\n", range(0, IncludeExample::Limit + 1));
+ $process = Mockery::mock(Process::class);
+ $process
+ ->shouldReceive('run')
+ ->with([self::getTestData()->path('~runnable.run')], dirname($path))
+ ->once()
+ ->andReturn("{$output}");
+
+ $instance = $this->app->make(IncludeExample::class, [
+ 'process' => $process,
+ ]);
+
+ self::assertEquals(
+ <<Example output
+
+ {$output}
+
+
+ EXPECTED,
+ $instance->process($path, $file),
+ );
+ }
}