From dcc68f4693658cd4b805d366408e9ce298910b93 Mon Sep 17 00:00:00 2001 From: Max Stoumen Date: Tue, 18 Jul 2023 14:16:31 -0700 Subject: [PATCH 1/4] make filtering aware of allowunnamed --- internal/cmd/common.go | 17 +++++-- internal/cmd/list.go | 11 ++++- internal/cmd/print.go | 9 +++- internal/cmd/run.go | 18 ++++++-- internal/cmd/tasks.go | 17 +++++-- internal/cmd/tui.go | 86 ++++++++++++++++++++++++++++++++---- internal/document/block.go | 7 ++- internal/project/document.go | 25 +++-------- internal/project/project.go | 39 ++++++++++++---- testdata/script/basic.txtar | 22 ++++----- 10 files changed, 188 insertions(+), 63 deletions(-) diff --git a/internal/cmd/common.go b/internal/cmd/common.go index 7dc6e585..2fda8979 100644 --- a/internal/cmd/common.go +++ b/internal/cmd/common.go @@ -132,8 +132,6 @@ func getProject() (proj project.Project, err error) { func getCodeBlocks() (document.CodeBlocks, error) { return project.GetCodeBlocks( filepath.Join(fChdir, fFileName), - fAllowUnknown, - fAllowUnnamed, nil, ) } @@ -434,13 +432,24 @@ func loadFiles(proj project.Project, w io.Writer, r io.Reader) ([]string, error) return m.files, nil } -func loadTasks(proj project.Project, w io.Writer, r io.Reader) (project.CodeBlocks, error) { +func loadTasks(proj project.Project, w io.Writer, r io.Reader, filter bool) (project.CodeBlocks, error) { m, err := runTasksModel(proj, false, w, r) if err != nil { return nil, err } - return m.tasks, nil + tasks := m.tasks + + if filter { + tasks = project.FilterCodeBlocks[project.CodeBlock](m.tasks, fAllowUnknown, fAllowUnnamed) + + if len(tasks) == 0 { + // try again without filtering unnamed + tasks = project.FilterCodeBlocks[project.CodeBlock](m.tasks, fAllowUnknown, true) + } + } + + return tasks, nil } func runTasksModel(proj project.Project, filesOnly bool, w io.Writer, r io.Reader) (*loadTasksModel, error) { diff --git a/internal/cmd/list.go b/internal/cmd/list.go index 578646d1..7aa63bbf 100644 --- a/internal/cmd/list.go +++ b/internal/cmd/list.go @@ -31,7 +31,7 @@ func listCmd() *cobra.Command { return err } - allBlocks, err := loadTasks(proj, cmd.OutOrStdout(), cmd.InOrStdin()) + allBlocks, err := loadTasks(proj, cmd.OutOrStdout(), cmd.InOrStdin(), true) if err != nil { return err } @@ -41,7 +41,7 @@ func listCmd() *cobra.Command { return err } - if len(blocks) < 1 && !fAllowUnnamed { + if len(blocks) <= 0 && !fAllowUnnamed { return errors.Errorf("no named code blocks, consider adding flag --allow-unnamed") } @@ -57,6 +57,7 @@ func listCmd() *cobra.Command { table.AddField(strings.ToUpper("File"), nil, nil) table.AddField(strings.ToUpper("First Command"), nil, nil) table.AddField(strings.ToUpper("Description"), nil, nil) + table.AddField(strings.ToUpper("Named"), nil, nil) table.EndRow() for _, fileBlock := range blocks { @@ -64,10 +65,16 @@ func listCmd() *cobra.Command { lines := block.Lines() + isNamedField := "True" + if block.IsUnnamed() { + isNamedField = "False" + } + table.AddField(block.Name(), nil, nil) table.AddField(fileBlock.File, nil, nil) table.AddField(shell.TryGetNonCommentLine(lines), nil, nil) table.AddField(block.Intro(), nil, nil) + table.AddField(isNamedField, nil, nil) table.EndRow() } diff --git a/internal/cmd/print.go b/internal/cmd/print.go index ccfd1e6f..2f4c1f28 100644 --- a/internal/cmd/print.go +++ b/internal/cmd/print.go @@ -5,6 +5,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/stateful/runme/internal/project" ) func printCmd() *cobra.Command { @@ -20,13 +21,19 @@ func printCmd() *cobra.Command { return err } - blocks, err := loadTasks(proj, cmd.OutOrStdout(), cmd.InOrStdin()) + generateBlocks: + blocks, err := loadTasks(proj, cmd.OutOrStdout(), cmd.InOrStdin(), true) if err != nil { return err } fileBlock, err := lookupCodeBlockWithPrompt(cmd, args[0], blocks) if err != nil { + if project.IsCodeBlockNotFoundError(err) && !fAllowUnnamed { + fAllowUnnamed = true + goto generateBlocks + } + return err } diff --git a/internal/cmd/run.go b/internal/cmd/run.go index c2ea9b1c..34d29346 100644 --- a/internal/cmd/run.go +++ b/internal/cmd/run.go @@ -71,7 +71,8 @@ func runCmd() *cobra.Command { runBlocks := make([]project.FileCodeBlock, 0) { - blocks, err := loadTasks(proj, cmd.OutOrStdout(), cmd.InOrStdin()) + searchBlocks: + blocks, err := loadTasks(proj, cmd.OutOrStdout(), cmd.InOrStdin(), true) if err != nil { return err } @@ -93,13 +94,20 @@ func runCmd() *cobra.Command { runBlocks = append(runBlocks, fileBlock) } } - if len(runBlocks) == 0 { - return errors.New("No tasks to execute with the category provided") + + if len(runBlocks) == 0 && !fAllowUnnamed { + fAllowUnnamed = true + goto searchBlocks } } else { for _, arg := range args { block, err := lookupCodeBlockWithPrompt(cmd, arg, blocks) if err != nil { + if project.IsCodeBlockNotFoundError(err) && !fAllowUnnamed { + fAllowUnnamed = true + goto searchBlocks + } + return err } @@ -112,6 +120,10 @@ func runCmd() *cobra.Command { } } + if len(runBlocks) == 0 { + return errors.New("No tasks to execute with the category provided") + } + ctx, cancel := ctxWithSigCancel(cmd.Context()) defer cancel() diff --git a/internal/cmd/tasks.go b/internal/cmd/tasks.go index 3530f266..45098d37 100644 --- a/internal/cmd/tasks.go +++ b/internal/cmd/tasks.go @@ -5,6 +5,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/stateful/runme/internal/project" "github.com/stateful/runme/internal/tasks" ) @@ -15,19 +16,27 @@ func tasksCmd() *cobra.Command { Hidden: true, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - blocks, err := getCodeBlocks() + proj, err := getProject() if err != nil { return err } - block, err := lookupCodeBlock(blocks, args[0]) + generateBlocks: + blocks, err := loadTasks(proj, cmd.OutOrStdout(), cmd.InOrStdin(), true) + + block, err := lookupCodeBlockWithPrompt(cmd, args[0], blocks) if err != nil { + if project.IsCodeBlockNotFoundError(err) && !fAllowUnnamed { + fAllowUnnamed = true + goto generateBlocks + } + return err } tasksDef, err := tasks.GenerateFromShellCommand( - block.Name(), - block.Lines()[0], + block.Block.Name(), + block.Block.Lines()[0], &tasks.ShellCommandOpts{ Cwd: fChdir, }, diff --git a/internal/cmd/tui.go b/internal/cmd/tui.go index 2dc1bbf0..aa06a8a3 100644 --- a/internal/cmd/tui.go +++ b/internal/cmd/tui.go @@ -37,11 +37,20 @@ func tuiCmd() *cobra.Command { return err } - blocks, err := loadTasks(proj, cmd.OutOrStdout(), cmd.InOrStdin()) + blocks, err := loadTasks(proj, cmd.OutOrStdout(), cmd.InOrStdin(), false) if err != nil { return err } + defaultAllowUnnamed := fAllowUnnamed + + if !defaultAllowUnnamed { + newBlocks := project.FilterCodeBlocks(blocks, fAllowUnknown, false) + if len(newBlocks) == 0 { + defaultAllowUnnamed = true + } + } + blocks = sortBlocks(blocks) if len(blocks) == 0 { @@ -102,7 +111,7 @@ func tuiCmd() *cobra.Command { } model := tuiModel{ - blocks: blocks, + unfilteredBlocks: blocks, header: fmt.Sprintf( "%s %s\n\n", ansi.Color("runme", "57+b"), @@ -110,8 +119,13 @@ func tuiCmd() *cobra.Command { ), visibleEntries: visibleEntries, expanded: make(map[int]struct{}), + + allowUnnamed: defaultAllowUnnamed, + allowUnknown: fAllowUnknown, } + model.filterCodeBlocks() + sessionEnvs, err := runnerClient.GetEnvs(context.Background()) if err != nil { return err @@ -188,13 +202,16 @@ func tuiCmd() *cobra.Command { } type tuiModel struct { - blocks project.CodeBlocks - header string - visibleEntries int - expanded map[int]struct{} - cursor int - scroll int - result tuiResult + unfilteredBlocks project.CodeBlocks + blocks project.CodeBlocks + header string + visibleEntries int + expanded map[int]struct{} + cursor int + scroll int + result tuiResult + allowUnnamed bool + allowUnknown bool } type tuiResult struct { @@ -217,6 +234,40 @@ func (m *tuiModel) scrollBy(delta int) { ) } +func (m *tuiModel) filterCodeBlocks() { + hasInitialized := m.blocks != nil + + var oldSelection project.CodeBlock + if hasInitialized { + oldSelection = m.blocks[m.cursor] + } + + m.blocks = project.FilterCodeBlocks(m.unfilteredBlocks, m.allowUnknown, m.allowUnnamed) + + if !hasInitialized { + return + } + + foundOldSelection := false + for i, block := range m.blocks { + if block == oldSelection { + m.moveCursorTo(i) + foundOldSelection = true + break + } + } + + if !foundOldSelection { + if m.cursor >= len(m.blocks) { + m.moveCursorTo(len(m.blocks) - 1) + } + } +} + +func (m *tuiModel) moveCursorTo(newPos int) { + m.moveCursor(newPos - m.cursor) +} + func (m *tuiModel) moveCursor(delta int) { m.cursor = rmath.Clamp( m.cursor+delta, @@ -258,6 +309,11 @@ func (m tuiModel) View() string { { name := block.Name() + + if block.IsUnnamed() { + name += " (unnamed)" + } + filename := ansi.Color(fileBlock.File, "white+d") if active { @@ -296,6 +352,13 @@ func (m tuiModel) View() string { _, _ = s.WriteRune('\n') + var unnamedVerb string + if m.allowUnnamed { + unnamedVerb = "Hide" + } else { + unnamedVerb = "Show" + } + { help := strings.Join( []string{ @@ -304,6 +367,7 @@ func (m tuiModel) View() string { "Run [Enter]", "Expand [Space]", "Quit [q]", + fmt.Sprintf("%s Unnamed [u]", unnamedVerb), }, tab, ) @@ -351,6 +415,10 @@ func (m tuiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } return m, tea.Quit + + case "u": + m.allowUnnamed = !m.allowUnnamed + m.filterCodeBlocks() } } diff --git a/internal/document/block.go b/internal/document/block.go index f22e9348..bb08c002 100644 --- a/internal/document/block.go +++ b/internal/document/block.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" + "github.com/stateful/runme/internal/executable" "github.com/stateful/runme/internal/shell" "github.com/yuin/goldmark/ast" @@ -153,10 +154,14 @@ func (b *CodeBlock) Name() string { return b.name } -func (b *CodeBlock) NameGenerated() bool { +func (b *CodeBlock) IsUnnamed() bool { return b.nameGenerated } +func (b *CodeBlock) IsUnknown() bool { + return b.Language() == "" || !executable.IsSupported(b.Language()) +} + func (b *CodeBlock) Unwrap() ast.Node { return b.inner } diff --git a/internal/project/document.go b/internal/project/document.go index 915b11c4..071bdbe7 100644 --- a/internal/project/document.go +++ b/internal/project/document.go @@ -9,7 +9,6 @@ import ( "github.com/go-git/go-billy/v5/util" "github.com/pkg/errors" "github.com/stateful/runme/internal/document" - "github.com/stateful/runme/internal/executable" "github.com/stateful/runme/internal/renderer/cmark" ) @@ -39,7 +38,7 @@ func WriteMarkdownFile(filename string, fs billy.Basic, data []byte) error { return util.WriteFile(fs, filename, data, 0o600) } -func parseDocumentForCodeBlocks(filepath string, allowUnknown bool, allowUnnamed bool, fs billy.Basic, doFrontmatter bool) (document.CodeBlocks, *document.Frontmatter, error) { +func parseDocumentForCodeBlocks(filepath string, fs billy.Basic, doFrontmatter bool) (document.CodeBlocks, *document.Frontmatter, error) { data, err := ReadMarkdownFile(filepath, fs) if err != nil { return nil, nil, err @@ -65,23 +64,11 @@ func parseDocumentForCodeBlocks(filepath string, allowUnknown bool, allowUnnamed blocks := document.CollectCodeBlocks(node) - filtered := make(document.CodeBlocks, 0, len(blocks)) - for _, b := range blocks { - if !(allowUnknown || (b.Language() != "" && executable.IsSupported(b.Language()))) { - continue - } - - if !allowUnnamed && b.NameGenerated() { - continue - } - - filtered = append(filtered, b) - } - return filtered, fmtr, nil + return blocks, fmtr, nil } -func GetCodeBlocksAndParseFrontmatter(filepath string, allowUnknown bool, allowUnnamed bool, fs billy.Basic) (document.CodeBlocks, document.Frontmatter, error) { - blocks, fmtr, err := parseDocumentForCodeBlocks(filepath, allowUnknown, allowUnnamed, fs, true) +func GetCodeBlocksAndParseFrontmatter(filepath string, fs billy.Basic) (document.CodeBlocks, document.Frontmatter, error) { + blocks, fmtr, err := parseDocumentForCodeBlocks(filepath, fs, true) var f document.Frontmatter if fmtr != nil { @@ -91,7 +78,7 @@ func GetCodeBlocksAndParseFrontmatter(filepath string, allowUnknown bool, allowU return blocks, f, err } -func GetCodeBlocks(filepath string, allowUnknown bool, allowUnnamed bool, fs billy.Basic) (document.CodeBlocks, error) { - blocks, _, err := parseDocumentForCodeBlocks(filepath, allowUnknown, allowUnnamed, fs, false) +func GetCodeBlocks(filepath string, fs billy.Basic) (document.CodeBlocks, error) { + blocks, _, err := parseDocumentForCodeBlocks(filepath, fs, false) return blocks, err } diff --git a/internal/project/project.go b/internal/project/project.go index e8231c7b..6a382466 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -32,7 +32,12 @@ func newCodeBlock( frontmatter document.Frontmatter, fs billy.Chroot, ) *CodeBlock { - return &CodeBlock{Block: block, File: file, Frontmatter: frontmatter, fs: fs} + return &CodeBlock{ + Block: block, + File: file, + Frontmatter: frontmatter, + fs: fs, + } } func (b CodeBlock) GetBlock() *document.CodeBlock { @@ -92,6 +97,10 @@ func (blocks CodeBlocks) Lookup(queryName string) []CodeBlock { return results } +func IsCodeBlockNotFoundError(err error) bool { + return errors.As(err, &ErrCodeBlockNameNotFound{}) || errors.As(err, &ErrCodeBlockFileNotFound{}) +} + type ErrCodeBlockFileNotFound struct { queryFile string } @@ -247,8 +256,6 @@ type DirectoryProject struct { repo *git.Repository fs billy.Filesystem - allowUnknown bool - allowUnnamed bool respectGitignore bool envLoadOrder []string @@ -278,8 +285,6 @@ func (p *DirectoryProject) SetRespectGitignore(respectGitignore bool) { func NewDirectoryProject(dir string, findNearestRepo bool, allowUnknown bool, allowUnnamed bool, ignorePatterns []string) (*DirectoryProject, error) { project := &DirectoryProject{ - allowUnknown: allowUnknown, - allowUnnamed: allowUnnamed, respectGitignore: true, ignorePatterns: ignorePatterns, } @@ -409,7 +414,7 @@ func (p *DirectoryProject) LoadTasks(filesOnly bool, channel chan<- interface{}) for _, mdFile := range markdownFiles { channel <- LoadTaskParsingFile{Filename: mdFile} - blocks, err := getFileCodeBlocks(mdFile, p.allowUnknown, p.allowUnnamed, p.fs) + blocks, err := getFileCodeBlocks(mdFile, p.fs) if err != nil { channel <- LoadTaskError{Err: err} return @@ -490,7 +495,7 @@ func (p *SingleFileProject) LoadTasks(filesOnly bool, channel chan<- interface{} channel <- LoadTaskStatusParsingFiles{} channel <- LoadTaskParsingFile{Filename: relFile} - blocks, err := getFileCodeBlocks(relFile, p.allowUnknown, p.allowUnnamed, fs) + blocks, err := getFileCodeBlocks(relFile, fs) if err != nil { channel <- LoadTaskError{Err: err} return @@ -518,8 +523,8 @@ type CodeBlockFS interface { billy.Chroot } -func getFileCodeBlocks(file string, allowUnknown bool, allowUnnamed bool, fs CodeBlockFS) ([]CodeBlock, error) { - blocks, fmtr, err := GetCodeBlocksAndParseFrontmatter(file, allowUnknown, allowUnnamed, fs) +func getFileCodeBlocks(file string, fs CodeBlockFS) ([]CodeBlock, error) { + blocks, fmtr, err := GetCodeBlocksAndParseFrontmatter(file, fs) if err != nil { return nil, err } @@ -574,3 +579,19 @@ func LoadProjectFiles(proj Project) ([]string, error) { return files, err } + +func FilterCodeBlocks[T FileCodeBlock](blocks []T, allowUnknown bool, allowUnnamed bool) (result []T) { + for _, b := range blocks { + if !allowUnknown && b.GetBlock().IsUnknown() { + continue + } + + if !allowUnnamed && b.GetBlock().IsUnnamed() { + continue + } + + result = append(result, b) + } + + return +} diff --git a/testdata/script/basic.txtar b/testdata/script/basic.txtar index 99ffcd70..e9882111 100644 --- a/testdata/script/basic.txtar +++ b/testdata/script/basic.txtar @@ -132,19 +132,19 @@ func main() { ``` -- golden-list.txt -- -NAME FILE FIRST COMMAND DESCRIPTION -echo README.md echo "Hello, runme!" With {name=hello} you can annotate it and give it a nice name. +NAME FILE FIRST COMMAND DESCRIPTION NAMED +echo README.md echo "Hello, runme!" With {name=hello} you can annotate it and give it a nice name. True hello-js README.md console.log("Hello, runme, from javascript!") It can even run scripting languages. hello-js-cat README.md console.log("Hello, runme, from javascript!") And it can even run a cell with a custom interpreter. -- golden-list-allow-unnamed.txt -- -NAME FILE FIRST COMMAND DESCRIPTION -echo-hello README.md echo "Hello, runme!" This is a basic snippet with shell command. -echo-hello-2 README.md echo "Hello, runme!" You can omit the language, and runme will assume you are in shell. -echo-inferred README.md echo Inferred Names will automatically be inferred from a script's contents. -echo README.md echo "Hello, runme!" With {name=hello} you can annotate it and give it a nice name. -echo-1 README.md echo "1" It can contain multiple lines too. -echo-hello-3 README.md echo "Hello, runme! Again!" Also, the dollar sign is not needed. +NAME FILE FIRST COMMAND DESCRIPTION NAMED +echo-hello README.md echo "Hello, runme!" This is a basic snippet with shell command. False +echo-hello-2 README.md echo "Hello, runme!" You can omit the language, and runme will assume you are in shell. False +echo-inferred README.md echo Inferred Names will automatically be inferred from a script's contents. False +echo README.md echo "Hello, runme!" With {name=hello} you can annotate it and give it a nice name. True +echo-1 README.md echo "1" It can contain multiple lines too. False +echo-hello-3 README.md echo "Hello, runme! Again!" Also, the dollar sign is not needed. False hello-js README.md console.log("Hello, runme, from javascript!") It can even run scripting languages. hello-js-cat README.md console.log("Hello, runme, from javascript!") And it can even run a cell with a custom interpreter. -tempdir README.md temp_dir=$(mktemp -d -t "runme-XXXXXXX") It works with cd, pushd, and similar because all lines are executed as a single script. -package-main README.md package main It can also execute a snippet of Go code. +tempdir README.md temp_dir=$(mktemp -d -t "runme-XXXXXXX") It works with cd, pushd, and similar because all lines are executed as a single script. False +package-main README.md package main It can also execute a snippet of Go code. False From 4f936457f02b177b8b1d36cb13dead913f77e36c Mon Sep 17 00:00:00 2001 From: Max Stoumen Date: Tue, 18 Jul 2023 14:23:34 -0700 Subject: [PATCH 2/4] fix lint --- internal/cmd/common.go | 8 -------- internal/cmd/tasks.go | 3 +++ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/internal/cmd/common.go b/internal/cmd/common.go index 2fda8979..25a84762 100644 --- a/internal/cmd/common.go +++ b/internal/cmd/common.go @@ -136,14 +136,6 @@ func getCodeBlocks() (document.CodeBlocks, error) { ) } -func lookupCodeBlock(blocks document.CodeBlocks, name string) (*document.CodeBlock, error) { - block := blocks.Lookup(name) - if block == nil { - return nil, errors.Errorf("command %q not found; known command names: %s", name, blocks.Names()) - } - return block, nil -} - func validCmdNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { blocks, err := getCodeBlocks() if err != nil { diff --git a/internal/cmd/tasks.go b/internal/cmd/tasks.go index 45098d37..9f96e009 100644 --- a/internal/cmd/tasks.go +++ b/internal/cmd/tasks.go @@ -23,6 +23,9 @@ func tasksCmd() *cobra.Command { generateBlocks: blocks, err := loadTasks(proj, cmd.OutOrStdout(), cmd.InOrStdin(), true) + if err != nil { + return err + } block, err := lookupCodeBlockWithPrompt(cmd, args[0], blocks) if err != nil { From 8dbfa983999551c2f8e28a437ebc475ca18cf1d6 Mon Sep 17 00:00:00 2001 From: Sebastian Tiedtke Date: Mon, 24 Jul 2023 17:29:29 -0400 Subject: [PATCH 3/4] Make quit be last --- internal/cmd/tui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/tui.go b/internal/cmd/tui.go index aa06a8a3..072e9d08 100644 --- a/internal/cmd/tui.go +++ b/internal/cmd/tui.go @@ -366,8 +366,8 @@ func (m tuiModel) View() string { "Choose ↑↓←→", "Run [Enter]", "Expand [Space]", - "Quit [q]", fmt.Sprintf("%s Unnamed [u]", unnamedVerb), + "Quit [q]", }, tab, ) From 115f7033ef2ef33c910c97410b0c837aeb1fe36e Mon Sep 17 00:00:00 2001 From: Sebastian Tiedtke Date: Mon, 24 Jul 2023 17:49:24 -0400 Subject: [PATCH 4/4] Reconcile tests --- testdata/script/basic.txtar | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testdata/script/basic.txtar b/testdata/script/basic.txtar index e9882111..ea53165d 100644 --- a/testdata/script/basic.txtar +++ b/testdata/script/basic.txtar @@ -134,8 +134,8 @@ func main() { -- golden-list.txt -- NAME FILE FIRST COMMAND DESCRIPTION NAMED echo README.md echo "Hello, runme!" With {name=hello} you can annotate it and give it a nice name. True -hello-js README.md console.log("Hello, runme, from javascript!") It can even run scripting languages. -hello-js-cat README.md console.log("Hello, runme, from javascript!") And it can even run a cell with a custom interpreter. +hello-js README.md console.log("Hello, runme, from javascript!") It can even run scripting languages. True +hello-js-cat README.md console.log("Hello, runme, from javascript!") And it can even run a cell with a custom interpreter. True -- golden-list-allow-unnamed.txt -- NAME FILE FIRST COMMAND DESCRIPTION NAMED echo-hello README.md echo "Hello, runme!" This is a basic snippet with shell command. False @@ -144,7 +144,7 @@ echo-inferred README.md echo Inferred Names will automatically be inferred from echo README.md echo "Hello, runme!" With {name=hello} you can annotate it and give it a nice name. True echo-1 README.md echo "1" It can contain multiple lines too. False echo-hello-3 README.md echo "Hello, runme! Again!" Also, the dollar sign is not needed. False -hello-js README.md console.log("Hello, runme, from javascript!") It can even run scripting languages. -hello-js-cat README.md console.log("Hello, runme, from javascript!") And it can even run a cell with a custom interpreter. +hello-js README.md console.log("Hello, runme, from javascript!") It can even run scripting languages. True +hello-js-cat README.md console.log("Hello, runme, from javascript!") And it can even run a cell with a custom interpreter. True tempdir README.md temp_dir=$(mktemp -d -t "runme-XXXXXXX") It works with cd, pushd, and similar because all lines are executed as a single script. False package-main README.md package main It can also execute a snippet of Go code. False