Skip to content

Commit 1426bec

Browse files
authored
Merge pull request #2 from szkiba/feature/run-command
feat: run subcommand
2 parents e3b8c8a + a54ab0a commit 1426bec

File tree

14 files changed

+308
-22
lines changed

14 files changed

+308
-22
lines changed

.golangci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ linters-settings:
3434
- github.com/spf13/cobra
3535
- github.com/gobwas/glob
3636
- github.com/liamg/memoryfs
37+
- mvdan.cc/sh/v3/interp
38+
- mvdan.cc/sh/v3/syntax
3739
- github.com/szkiba/mdcode/internal
3840
deny:
3941
- pkg: io/ioutil

README.md

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,19 @@ This document includes the necessary code for testing within invisible code bloc
7878

7979
Code blocks embedded in this document can be saved to files using the [`mdcode extract`](#mdcode-extract) command. A `README_test.go` and a `README.test.js` file will be created in the current directory. After modification, the code blocks can be updated from these files to the document using the [`mdcode update`](#mdcode-update) command.
8080

81+
After the modification, it is advisable to test the above examples using the following commands:
82+
83+
```sh name=test
84+
go test ./...
85+
node --test
86+
```
87+
88+
Since the above code block has a name (`test`), it can also be run with the [`mdcode run`](#mdcode-run) command:
89+
90+
```
91+
mdcode run -n test
92+
```
93+
8194
More examples can be found in the [examples](examples/) directory and in the [tutorial](docs/testable-markdown-code-blocks.md).
8295

8396
### Features
@@ -239,11 +252,11 @@ Or if only block comments can be used (CSS):
239252

240253
/* #endregion */
241254

242-
Regions marked this way are used by IDEs to collapse parts of the source code.
255+
Regions marked in this way are used by IDEs to collapse parts of the source code.
243256

244257
In the case of `mdcode`, regions can be referenced with the `region` metadata. If a region is specified for a code block, the subcommand (update or extract) applies only to the specified region of the file. That is, the update command only embeds the specified region from the file to the markdown document, and the extract command overwrites only the specified region in the file.
245258

246-
`mdcode` can handle regions in any programming language, the only requirement is that the comments indicating the beginning and end of the region are placed in separate lines containing only the given comment.
259+
`mdcode` can handle regions in any programming language, the only requirement is that the comment indicating the beginning and end of the region is placed in a separate line containing only the given comment.
247260
<!-- #endregion regions -->
248261

249262
### Invisible
@@ -435,7 +448,7 @@ The optional argument of the `mdcode` command is the name of the markdown file.
435448

436449

437450
```
438-
mdcode [filename] [flags]
451+
mdcode [flags] [filename]
439452
```
440453

441454
### Flags
@@ -453,6 +466,7 @@ mdcode [filename] [flags]
453466

454467
* [mdcode dump](#mdcode-dump) - Dump markdown code blocks
455468
* [mdcode extract](#mdcode-extract) - Extract markdown code blocks to the file system
469+
* [mdcode run](#mdcode-run) - Run shell commands on markdown code blocks
456470
* [mdcode update](#mdcode-update) - Update markdown code blocks from the file system
457471

458472
---
@@ -472,7 +486,7 @@ The optional argument of the `mdcode dump` command is the name of the markdown f
472486

473487

474488
```
475-
mdcode dump [filename] [flags]
489+
mdcode dump [flags] [filename]
476490
```
477491

478492
### Flags
@@ -513,7 +527,7 @@ The optional argument of the `mdcode extract` command is the name of the markdow
513527

514528

515529
```
516-
mdcode extract [filename] [flags]
530+
mdcode extract [flags] [filename]
517531
```
518532

519533
### Flags
@@ -536,6 +550,54 @@ mdcode extract [filename] [flags]
536550

537551
* [mdcode](#mdcode) - Markdown code block authoring tool
538552

553+
---
554+
## mdcode run
555+
556+
Run shell commands on markdown code blocks
557+
558+
### Synopsis
559+
560+
Extract code blocks to the file system and run shell commands on them
561+
562+
The code blocks are written to the file named in the `file` metadata.
563+
564+
The code block may include `region` metadata, which contains the name of the region. In this case, the code block is written to the appropriate part of the file marked with the `#region` comment.
565+
566+
The optional argument of the `mdcode run` command is the name of the markdown file. If it is missing, the `README.md` file in the current directory (if it exists) is processed.
567+
568+
This can be followed by a double dash (`--`) and then the shell command line to be executed (even a complex command, such as `for`).
569+
570+
Alternatively, the commands to be executed can be embedded in a code block in the document. In this case, the language must be `sh` and it is necessary to name the code block with the metadata `name`. The name of the code block containing the commands can be specified with the `--name` flag (if not, the first code block containing the `sh` language and `name` metadata will be executed).
571+
572+
Code blocks are extracted to a temporary directory. This directory will be the current directory when running the commands. The temporary directory is deleted after executing the commands (deletion can be prevented by using the `--keep` flag). Instead of a temporary directory, the name of the directory to be used can be specified with the `--dir` flag. In this case, of course, the directory is not deleted after executing the commands.
573+
574+
575+
```
576+
mdcode run [flags] [filename] [-- commands]
577+
```
578+
579+
### Flags
580+
581+
```
582+
-d, --dir string base directory name (default ".")
583+
-h, --help help for run
584+
-k, --keep don't remove temporary directory
585+
-n, --name string code block name contains commands
586+
-q, --quiet suppress the status output
587+
```
588+
589+
### Global Flags
590+
591+
```
592+
-f, --file strings file filter (default [?*])
593+
-l, --lang strings language filter (default [?*])
594+
-m, --meta stringToString metadata filter (default [])
595+
```
596+
597+
### SEE ALSO
598+
599+
* [mdcode](#mdcode) - Markdown code block authoring tool
600+
539601
---
540602
## mdcode update
541603

@@ -553,7 +615,7 @@ The optional argument of the `mdcode update` command is the name of the markdown
553615

554616

555617
```
556-
mdcode update [filename] [flags]
618+
mdcode update [flags] [filename]
557619
```
558620

559621
### Flags
@@ -576,4 +638,4 @@ mdcode update [filename] [flags]
576638

577639
* [mdcode](#mdcode) - Markdown code block authoring tool
578640

579-
<!-- #endregion cli -->
641+
<!-- #endregion cli -->

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/spf13/cobra v1.8.0
1111
github.com/stretchr/testify v1.7.1
1212
github.com/yuin/goldmark v1.6.0
13+
mvdan.cc/sh/v3 v3.7.0
1314
)
1415

1516
require (
@@ -20,5 +21,8 @@ require (
2021
github.com/pmezard/go-difflib v1.0.0 // indirect
2122
github.com/russross/blackfriday/v2 v2.1.0 // indirect
2223
github.com/spf13/pflag v1.0.5 // indirect
24+
golang.org/x/sync v0.2.0 // indirect
25+
golang.org/x/sys v0.8.0 // indirect
26+
golang.org/x/term v0.8.0 // indirect
2327
gopkg.in/yaml.v3 v3.0.1 // indirect
2428
)

go.sum

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
22
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
3+
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
4+
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
35
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
46
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
57
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8+
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
9+
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
610
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
711
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
812
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
@@ -11,6 +15,10 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU
1115
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
1216
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
1317
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
18+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
19+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
20+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
21+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
1422
github.com/liamg/memoryfs v1.6.0 h1:jAFec2HI1PgMTem5gR7UT8zi9u4BfG5jorCRlLH06W8=
1523
github.com/liamg/memoryfs v1.6.0/go.mod h1:z7mfqXFQS8eSeBBsFjYLlxYRMRyiPktytvYCYTb3BSk=
1624
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@@ -22,6 +30,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
2230
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
2331
github.com/rodaine/table v1.1.0 h1:/fUlCSdjamMY8VifdQRIu3VWZXYLY7QHFkVorS8NTr4=
2432
github.com/rodaine/table v1.1.0/go.mod h1:Qu3q5wi1jTQD6B6HsP6szie/S4w1QUQ8pq22pz9iL8g=
33+
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY=
34+
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
2535
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
2636
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
2737
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
@@ -34,8 +44,16 @@ github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMT
3444
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
3545
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
3646
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
47+
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
48+
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
49+
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
50+
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
51+
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
52+
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
3753
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
3854
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
3955
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
4056
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
4157
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
58+
mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg=
59+
mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8=

internal/cmd/dump.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var dumpHelp string
1818

1919
func dumpCmd(opts *options) *cobra.Command {
2020
cmd := &cobra.Command{ //nolint:exhaustruct
21-
Use: "dump [filename]",
21+
Use: "dump [flags] [filename]",
2222
Aliases: []string{"d"},
2323
Short: "Dump markdown code blocks",
2424
Long: dumpHelp,

internal/cmd/extract.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var extractHelp string
1818

1919
func extractCmd(opts *options) *cobra.Command {
2020
cmd := &cobra.Command{ //nolint:exhaustruct
21-
Use: "extract [filename]",
21+
Use: "extract [flags] [filename]",
2222
Aliases: []string{"x"},
2323
Short: "Extract markdown code blocks to the file system",
2424
Long: extractHelp,

internal/cmd/help/filtering.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
By default, `mdcode` work with all code blocks in a markdown document. It is possible to filter code blocks based on programming language or metadata. In this case, `mdcode` ignore code blocks that do not meet the filter criteria.
1+
By default, `mdcode` work with all code blocks in a markdown document. It is possible to filter code blocks based on programming language or metadata. In this case, `mdcode` ignores code blocks that do not meet the filter criteria.
22

33
A language filter pattern can be specified using the `--lang` flag. Then only code blocks with a language matching the pattern will be processed. For example, filtering for code blocks containing JavaScript code:
44

internal/cmd/help/run.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Extract code blocks to the file system and run shell commands on them
2+
3+
The code blocks are written to the file named in the `file` metadata.
4+
5+
The code block may include `region` metadata, which contains the name of the region. In this case, the code block is written to the appropriate part of the file marked with the `#region` comment.
6+
7+
The optional argument of the `mdcode run` command is the name of the markdown file. If it is missing, the `README.md` file in the current directory (if it exists) is processed.
8+
9+
This can be followed by a double dash (`--`) and then the shell command line to be executed (even a complex command, such as `for`).
10+
11+
Alternatively, the commands to be executed can be embedded in a code block in the document. In this case, the language must be `sh` and it is necessary to name the code block with the metadata `name`. The name of the code block containing the commands can be specified with the `--name` flag (if not, the first code block containing the `sh` language and `name` metadata will be executed).
12+
13+
Code blocks are extracted to a temporary directory. This directory will be the current directory when running the commands. The temporary directory is deleted after executing the commands (deletion can be prevented by using the `--keep` flag). Instead of a temporary directory, the name of the directory to be used can be specified with the `--dir` flag. In this case, of course, the directory is not deleted after executing the commands.

internal/cmd/list.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ func listRun(filename string, out io.Writer, opts *options) error {
1818
return err
1919
}
2020

21-
blocks, err := unfence(src, opts.filter)
21+
blocks, err := unfence(src, func(lang string, meta mdcode.Meta) bool {
22+
if isScript(lang, meta) {
23+
return true
24+
}
25+
26+
return opts.filter(lang, meta)
27+
})
2228
if err != nil {
2329
return err
2430
}
@@ -99,7 +105,7 @@ func metaKeys(blocks mdcode.Blocks) []string {
99105

100106
special := make(map[string]struct{})
101107

102-
for _, s := range []string{metaFile, metaOutline, metaRegion} {
108+
for _, s := range []string{metaName, metaFile, metaOutline, metaRegion} {
103109
special[s] = struct{}{}
104110

105111
if _, has := keyset[s]; has {

internal/cmd/options.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ const (
1010
metaFile = "file"
1111
metaRegion = "region"
1212
metaOutline = "outline"
13+
metaName = "name"
1314
)
1415

1516
type statusFunc func(format string, args ...any)
1617

1718
type options struct {
1819
lang []string
1920
file []string
21+
name string
2022
meta map[string]string
2123

2224
dir string
@@ -25,6 +27,7 @@ type options struct {
2527
json bool
2628

2729
quiet bool
30+
keep bool
2831

2932
filter filterFunc
3033
status statusFunc

0 commit comments

Comments
 (0)