Add pre-processing of slides

This commit is contained in:
Maas Lalani
2021-06-25 22:15:26 -04:00
parent 09f46bae4b
commit 7b1b2cd894
4 changed files with 202 additions and 0 deletions

31
examples/preprocess.md Normal file
View File

@@ -0,0 +1,31 @@
# Slides
~~~sd replaced processed
This content will be replaced and passed into stdin
of the command above
~~~
---
Any command will work
~~~echo "You can do whatever, really"
This doesn't matter, since it will be replaced by the stdout
of the command above
~~~
---
Pre-process Graphs
~~~graph-easy --as=boxart
[ A ] - to -> [ B ]
~~~
The above will be pre-processed to look like:
```
┌───┐ to ┌───┐
│ A │ ────> │ B │
└───┘ └───┘
```

View File

@@ -0,0 +1,38 @@
package process
import "testing"
func TestExecute(t *testing.T) {
tt := []struct {
block Block
want string
}{
{
block: Block{
Command: "cat",
Input: "Hello, world!",
},
want: "Hello, world!",
},
{
block: Block{
Command: "sd Find Replace",
Input: "Find",
},
want: "Replace",
},
}
for _, tc := range tt {
t.Run(tc.want, func(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
tc.block.Execute()
got := tc.block.Output
if tc.want != got {
t.Fatalf("Invalid execution, want %s, got %s", tc.want, got)
}
})
}
}

View File

@@ -0,0 +1,69 @@
package process
import (
"fmt"
"io"
"os/exec"
"regexp"
"strings"
)
// Block represents a pre-processable block which looks like the following: It
// is delimited by ~~~ and contains a command to be run along with the input to
// be passed, the entire block should be replaced with its command output
//
// ~~~sd block process
// block
// ~~~
type Block struct {
Command string
Input string
Output string
Raw string
}
func (b Block) String() string {
return fmt.Sprintf("===\n%s\n%s\n%s\n===", b.Raw, b.Command, b.Input)
}
// ?: means non-capture group
var reng = regexp.MustCompile("~~~(.+)\n(?:.|\n)*?\n~~~\\s?")
var reg = regexp.MustCompile("(?s)~~~(.+?)\n(.*?)\n~~~\\s?")
// Parse takes some markdown and returns blocks to be pre-processed
func Parse(markdown string) []Block {
var blocks []Block
matches := reng.FindAllString(markdown, -1)
for _, match := range matches {
m := reg.FindStringSubmatch(match)
blocks = append(blocks, Block{
Command: m[1],
Input: m[2],
Raw: strings.TrimSuffix(m[0], "\n"),
})
}
return blocks
}
// Execute takes performs the execution of the block's command
// by passing in the block's input as stdin and sets the block output
func (b *Block) Execute() {
c := strings.Split(b.Command, " ")
cmd := exec.Command(c[0], c[1:]...)
stdin, err := cmd.StdinPipe()
if err != nil {
return
}
go func() {
defer stdin.Close()
_, _ = io.WriteString(stdin, b.Input)
}()
out, err := cmd.CombinedOutput()
if err != nil {
return
}
b.Output = string(out)
}

View File

@@ -0,0 +1,64 @@
package process
import (
"reflect"
"testing"
)
func TestParse(t *testing.T) {
md := `
# Slide
~~~sd Replace Process
Replace
~~~
Hello
~~~sd Replace Process
Replace
Multi-line input
~~~
~~~echo -n World
Hello
~~~
---
# Next Slide
GraphViz Test
~~~graph-easy --as=boxart
digraph {
A -> B
}
~~~
`
got := Parse(md)
want := []Block{{
Command: "sd Replace Process",
Input: "Replace",
Raw: "~~~sd Replace Process\nReplace\n~~~",
}, {
Command: "sd Replace Process",
Input: "Replace\nMulti-line input",
Raw: "~~~sd Replace Process\nReplace\nMulti-line input\n~~~",
}, {
Command: "echo -n World",
Input: "Hello",
Raw: "~~~echo -n World\nHello\n~~~",
}, {
Command: "graph-easy --as=boxart",
Input: "digraph {\n A -> B\n}",
Raw: "~~~graph-easy --as=boxart\ndigraph {\n A -> B\n}\n~~~",
}}
if !reflect.DeepEqual(got, want) {
t.Log(want)
t.Log(got)
t.Fatal("Did not parse blocks correctly")
}
}