diff --git a/examples/code_blocks.md b/examples/code_blocks.md new file mode 100644 index 0000000..a970419 --- /dev/null +++ b/examples/code_blocks.md @@ -0,0 +1,50 @@ +# Code blocks + +Slides allows you to execute code blocks directly inside your slides! + +Just press `e` and the result of the code block will be displayed as virtual +text in your slides. + +Currently supported languages: +* `bash` +* `go` +* `ruby` +* `python` + +--- + +### Bash + +```bash +ls +``` + +--- + +### Go + +```go +package main + +import "fmt" + +func main() { + fmt.Println("Hello, world!") +} +``` + +--- + +### Ruby + +```ruby +puts "Hello, world!" +``` + +--- + +### Python + +```python +print("Hello, world!") +``` diff --git a/internal/code/code.go b/internal/code/code.go index eefd893..8939b69 100644 --- a/internal/code/code.go +++ b/internal/code/code.go @@ -21,7 +21,7 @@ type Result struct { } // ?: means non-capture group -var re = regexp.MustCompile("(?:```|~~~)(.*)\n(.*)\n(?:```|~~~)") +var re = regexp.MustCompile("(?s)(?:```|~~~)(\\w+)\n(.*)\n(?:```|~~~)\n") var ( ErrParse = errors.New("Error: could not parse code block") diff --git a/internal/code/code_test.go b/internal/code/code_test.go index fb651d4..1e7651d 100644 --- a/internal/code/code_test.go +++ b/internal/code/code_test.go @@ -37,28 +37,38 @@ fmt.Println("Hello, world!") }, { markdown: ` +# Welcome to Slides + +A terminal based presentation tool + +~~~go +package main + +import "fmt" + +func main() { + fmt.Println("Written in Go!") +} +~~~ +`, + expected: code.Block{ + Code: `package main + +import "fmt" + +func main() { + fmt.Println("Written in Go!") +}`, + Language: "go", + }, + }, + { + markdown: ` # Slide 1 Just a regular slide, no code block `, expected: code.Block{}, }, - { - markdown: ` -# Multiple Code Blocks -~~~go -fmt.Println("Oh no!") -~~~ - -# Secondary Code Block -~~~ruby -puts "We will only parse the first code block" -~~~ -`, - expected: code.Block{ - Code: `fmt.Println("Oh no!")`, - Language: "go", - }, - }, { markdown: ``, expected: code.Block{}, diff --git a/internal/model/model.go b/internal/model/model.go index 4feb764..b1ada5f 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -7,6 +7,7 @@ import ( "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/glamour" + "github.com/maaslalani/slides/internal/code" "github.com/maaslalani/slides/internal/meta" "github.com/maaslalani/slides/styles" "io" @@ -29,6 +30,9 @@ type Model struct { Theme glamour.TermRendererOption FileName string viewport viewport.Model + // VirtualText is used for additional information that is not part of the + // original slides, it will be displayed on a slide and reset on page change + VirtualText string } type fileWatchMsg struct{} @@ -96,10 +100,22 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.Page < len(m.Slides)-1 { m.Page++ } + m.VirtualText = "" case "up", "j", "left", "h", "p": if m.Page > 0 { m.Page-- } + m.VirtualText = "" + case "e": + // Run code block + block, err := code.Parse(m.Slides[m.Page]) + if err != nil { + // We couldn't parse the code block on the screen + m.VirtualText = "\n" + err.Error() + return m, nil + } + res := code.Execute(block) + m.VirtualText = res.Out } case fileWatchMsg: @@ -118,7 +134,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m Model) View() string { r, _ := glamour.NewTermRenderer(m.Theme, glamour.WithWordWrap(0)) - slide, err := r.Render(m.Slides[m.Page]) + slide := m.Slides[m.Page] + slide += m.VirtualText + slide, err := r.Render(slide) if err != nil { slide = fmt.Sprintf("Error: Could not render markdown! (%v)", err) }