From 8fdae6c1d7fd066fa00f79f4bbcbc967a71c12df Mon Sep 17 00:00:00 2001 From: sammyette Date: Mon, 25 Dec 2023 23:08:29 -0400 Subject: [PATCH] refactor: doc improvements (again) (#260) --- CHANGELOG.md | 10 + aliases.go | 24 +- api.go | 213 +++++-- cmd/docgen/docgen.go | 190 +++++-- cmd/docgen/docgen.lua | 146 +++++ complete.go | 122 ++-- {website/content/docs => docs}/_index.md | 0 docs/api/_index.md | 2 +- docs/api/bait.md | 167 +++++- docs/api/commander.md | 79 ++- docs/api/fs.md | 249 +++++++- docs/api/hilbish/_index.md | 536 +++++++++++++++--- docs/api/hilbish/hilbish.aliases.md | 84 ++- docs/api/hilbish/hilbish.completion.md | 146 ++++- docs/api/hilbish/hilbish.completions.md | 29 - docs/api/hilbish/hilbish.editor.md | 97 +++- docs/api/hilbish/hilbish.history.md | 94 ++- docs/api/hilbish/hilbish.jobs.md | 126 +++- docs/api/hilbish/hilbish.module.md | 34 +- docs/api/hilbish/hilbish.os.md | 19 +- docs/api/hilbish/hilbish.runner.md | 109 +++- docs/api/hilbish/hilbish.timers.md | 81 ++- docs/api/hilbish/hilbish.userDir.md | 10 +- docs/api/terminal.md | 73 ++- docs/completions.md | 114 ++-- {website/content/docs => docs}/faq.md | 9 +- .../content/docs => docs}/features/_index.md | 0 .../docs => docs}/features/notifications.md | 0 docs/features/opts.md | 78 +++ docs/{ => features}/runner-mode.md | 75 ++- .../content/docs => docs}/getting-started.md | 0 docs/hooks/_index.md | 22 +- docs/hooks/command.md | 73 ++- docs/hooks/hilbish.md | 51 +- docs/hooks/signal.md | 41 +- docs/jobs.md | 2 + docs/lunacolors.md | 7 + docs/nature/_index.md | 11 +- docs/nature/dirs.md | 79 +++ docs/vim-mode/_index.md | 7 + docs/vim-mode/actions.md | 9 + editor.go | 13 +- emmyLuaDocs/bait.lua | 24 +- emmyLuaDocs/commander.lua | 10 +- emmyLuaDocs/fs.lua | 61 +- emmyLuaDocs/hilbish.lua | 218 +++---- emmyLuaDocs/terminal.lua | 6 +- go.mod | 1 + go.sum | 2 + golibs/bait/bait.go | 151 +++-- golibs/commander/commander.go | 46 +- golibs/fs/fs.go | 329 ++++++----- golibs/terminal/terminal.go | 6 +- job.go | 26 +- module.go | 1 + nature/commands/doc.lua | 68 ++- nature/dirs.lua | 21 +- nature/doc.lua | 47 ++ nature/greenhouse/init.lua | 34 +- os.go | 7 +- rl.go | 14 +- runnermode.go | 56 +- timerhandler.go | 22 +- website/config.toml | 10 +- website/content/docs | 1 + website/content/docs/api | 1 - website/content/docs/features/runner-mode.md | 21 - website/static/completion.mp4 | Bin 0 -> 34742 bytes website/themes/hsh/assets/css/syntax.css | 89 +++ website/themes/hsh/layouts/partials/head.html | 40 ++ .../themes/hsh/layouts/shortcodes/video.html | 5 + 71 files changed, 3475 insertions(+), 1073 deletions(-) create mode 100644 cmd/docgen/docgen.lua rename {website/content/docs => docs}/_index.md (100%) delete mode 100644 docs/api/hilbish/hilbish.completions.md rename {website/content/docs => docs}/faq.md (74%) rename {website/content/docs => docs}/features/_index.md (100%) rename {website/content/docs => docs}/features/notifications.md (100%) create mode 100644 docs/features/opts.md rename docs/{ => features}/runner-mode.md (50%) rename {website/content/docs => docs}/getting-started.md (100%) create mode 100644 docs/nature/dirs.md create mode 100644 nature/doc.lua create mode 120000 website/content/docs delete mode 120000 website/content/docs/api delete mode 100644 website/content/docs/features/runner-mode.md create mode 100644 website/static/completion.mp4 create mode 100644 website/themes/hsh/assets/css/syntax.css create mode 100644 website/themes/hsh/layouts/shortcodes/video.html diff --git a/CHANGELOG.md b/CHANGELOG.md index d8f5cf9..23de65b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,16 @@ completed. - Using this also brings enhancements to the `doc` command like easy navigation of neighboring doc files. +### Changed +- Documentation for EVERYTHING has been improved, with more +information added, code example, parameter details, etc. +You can see the improvements! +- Documentation has gotten an uplift in the `doc` command. +This includes: + - Proper highlighting of code + - Paging (via Greenhouse) + - Highlighting more markdown things + ### Fixed - Fix infinite loop when navigating history without any history. [#252](https://github.com/Rosettea/Hilbish/issues/252) - Return the prefix when calling `hilbish.completions.call`. [#219](https://github.com/Rosettea/Hilbish/issues/219) diff --git a/aliases.go b/aliases.go index 8b815b3..8c90fe5 100644 --- a/aliases.go +++ b/aliases.go @@ -111,15 +111,23 @@ func (a *aliasModule) Loader(rtm *rt.Runtime) *rt.Table { // #interface aliases // add(alias, cmd) -// This is an alias (ha) for the `hilbish.alias` function. +// This is an alias (ha) for the [hilbish.alias](../#alias) function. // --- @param alias string // --- @param cmd string func _hlalias() {} // #interface aliases -// list() -> table +// list() -> table[string, string] // Get a table of all aliases, with string keys as the alias and the value as the command. -// --- @returns table +// #returns table[string, string] +/* +#example +hilbish.aliases.add('hi', 'echo hi') + +local aliases = hilbish.aliases.list() +-- -> {hi = 'echo hi'} +#example +*/ func (a *aliasModule) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { aliasesList := rt.NewTable() for k, v := range a.All() { @@ -132,7 +140,7 @@ func (a *aliasModule) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #interface aliases // delete(name) // Removes an alias. -// --- @param name string +// #param name string func (a *aliasModule) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -147,10 +155,10 @@ func (a *aliasModule) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // #interface aliases -// resolve(alias) -> command (string) -// Tries to resolve an alias to its command. -// --- @param alias string -// --- @returns string +// resolve(alias) -> string? +// Resolves an alias to its original command. Will thrown an error if the alias doesn't exist. +// #param alias string +// #returns string func (a *aliasModule) luaResolve(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err diff --git a/api.go b/api.go index 5fde6e5..b5a55b1 100644 --- a/api.go +++ b/api.go @@ -9,7 +9,7 @@ // #field interactive Is Hilbish in an interactive shell? // #field login Is Hilbish the login shell? // #field vimMode Current Vim input mode of Hilbish (will be nil if not in Vim input mode) -// #field exitCode xit code of the last executed command +// #field exitCode Exit code of the last executed command package main import ( @@ -192,12 +192,10 @@ func unsetVimMode() { } // run(cmd, returnOut) -> exitCode (number), stdout (string), stderr (string) -// Runs `cmd` in Hilbish's sh interpreter. -// If returnOut is true, the outputs of `cmd` will be returned as the 2nd and -// 3rd values instead of being outputted to the terminal. -// --- @param cmd string -// --- @param returnOut boolean -// --- @returns number, string, string +// Runs `cmd` in Hilbish's shell script interpreter. +// #param cmd string +// #param returnOut boolean If this is true, the function will return the standard output and error of the command instead of printing it. +// #returns number, string, string func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -239,8 +237,8 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // cwd() -> string -// Returns the current directory of the shell -// --- @returns string +// Returns the current directory of the shell. +// #returns string func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { cwd, _ := os.Getwd() @@ -251,9 +249,9 @@ func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // read(prompt) -> input (string) // Read input from the user, using Hilbish's line editor/input reader. // This is a separate instance from the one Hilbish actually uses. -// Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen) -// --- @param prompt? string -// --- @returns string|nil +// Returns `input`, will be nil if Ctrl-D is pressed, or an error occurs. +// #param prompt? string Text to print before input, can be empty. +// #returns string|nil func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { luaprompt := c.Arg(0) if typ := luaprompt.Type(); typ != rt.StringType && typ != rt.NilType { @@ -281,14 +279,21 @@ func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { /* prompt(str, typ) -Changes the shell prompt to `str` +Changes the shell prompt to the provided string. There are a few verbs that can be used in the prompt text. These will be formatted and replaced with the appropriate values. `%d` - Current working directory `%u` - Name of current user `%h` - Hostname of device ---- @param str string ---- @param typ? string Type of prompt, being left or right. Left by default. +#param str string +#param typ? string Type of prompt, being left or right. Left by default. +#example +-- the default hilbish prompt without color +hilbish.prompt '%u %d ∆' +-- or something of old: +hilbish.prompt '%u@%h :%d $' +-- prompt: user@hostname: ~/directory $ +#example */ func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { err := c.Check1Arg() @@ -322,8 +327,28 @@ func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // multiprompt(str) -// Changes the continued line prompt to `str` -// --- @param str string +// Changes the text prompt when Hilbish asks for more input. +// This will show up when text is incomplete, like a missing quote +// #param str string +/* +#example +--[[ +imagine this is your text input: +user ~ ∆ echo "hey + +but there's a missing quote! hilbish will now prompt you so the terminal +will look like: +user ~ ∆ echo "hey +--> ...!" + +so then you get +user ~ ∆ echo "hey +--> ...!" +hey ...! +]]-- +hilbish.multiprompt '-->' +#example +*/ func hlmultiprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -338,9 +363,19 @@ func hlmultiprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // alias(cmd, orig) -// Sets an alias of `cmd` to `orig` -// --- @param cmd string -// --- @param orig string +// Sets an alias, with a name of `cmd` to another command. +// #param cmd string Name of the alias +// #param orig string Command that will be aliased +/* +#example +-- With this, "ga file" will turn into "git add file" +hilbish.alias('ga', 'git add') + +-- Numbered substitutions are supported here! +hilbish.alias('dircount', 'ls %1 | wc -l') +-- "dircount ~" would count how many files are in ~ (home directory). +#example +*/ func hlalias(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.CheckNArgs(2); err != nil { return nil, err @@ -360,8 +395,20 @@ func hlalias(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // appendPath(dir) -// Appends `dir` to $PATH -// --- @param dir string|table +// Appends the provided dir to the command path (`$PATH`) +// #param dir string|table Directory (or directories) to append to path +/* +#example +hilbish.appendPath '~/go/bin' +-- Will add ~/go/bin to the command path. + +-- Or do multiple: +hilbish.appendPath { + '~/go/bin', + '~/.local/bin' +} +#example +*/ func hlappendPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -395,8 +442,9 @@ func appendPath(dir string) { } // exec(cmd) -// Replaces running hilbish with `cmd` -// --- @param cmd string +// Replaces the currently running Hilbish instance with the supplied command. +// This can be used to do an in-place restart. +// #param cmd string func hlexec(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -430,8 +478,11 @@ func hlexec(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // goro(fn) -// Puts `fn` in a goroutine -// --- @param fn function +// Puts `fn` in a Goroutine. +// This can be used to run any function in another thread at the same time as other Lua code. +// **NOTE: THIS FUNCTION MAY CRASH HILBISH IF OUTSIDE VARIABLES ARE ACCESSED.** +// **This is a limitation of the Lua runtime.** +// #param fn function func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -453,11 +504,11 @@ func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // timeout(cb, time) -> @Timer -// Runs the `cb` function after `time` in milliseconds. -// This creates a timer that starts immediately. -// --- @param cb function -// --- @param time number -// --- @returns Timer +// Executed the `cb` function after a period of `time`. +// This creates a Timer that starts ticking immediately. +// #param cb function +// #param time number Time to run in milliseconds. +// #returns Timer func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.CheckNArgs(2); err != nil { return nil, err @@ -479,11 +530,11 @@ func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // interval(cb, time) -> @Timer -// Runs the `cb` function every `time` milliseconds. -// This creates a timer that starts immediately. -// --- @param cb function -// --- @param time number -// --- @return Timer +// Runs the `cb` function every specified amount of `time`. +// This creates a timer that ticking immediately. +// #param cb function +// #param time number Time in milliseconds. +// #return Timer func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.CheckNArgs(2); err != nil { return nil, err @@ -505,13 +556,40 @@ func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // complete(scope, cb) -// Registers a completion handler for `scope`. -// A `scope` is currently only expected to be `command.`, +// Registers a completion handler for the specified scope. +// A `scope` is expected to be `command.`, // replacing with the name of the command (for example `command.git`). -// `cb` must be a function that returns a table of "completion groups." -// Check `doc completions` for more information. -// --- @param scope string -// --- @param cb function +// The documentation for completions, under Features/Completions or `doc completions` +// provides more details. +// #param scope string +// #param cb function +/* +#example +-- This is a very simple example. Read the full doc for completions for details. +hilbish.complete('command.sudo', function(query, ctx, fields) + if #fields == 0 then + -- complete for commands + local comps, pfx = hilbish.completion.bins(query, ctx, fields) + local compGroup = { + items = comps, -- our list of items to complete + type = 'grid' -- what our completions will look like. + } + + return {compGroup}, pfx + end + + -- otherwise just be boring and return files + + local comps, pfx = hilbish.completion.files(query, ctx, fields) + local compGroup = { + items = comps, + type = 'grid' + } + + return {compGroup}, pfx +end) +#example +*/ func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { scope, cb, err := util.HandleStrCallback(t, c) if err != nil { @@ -523,8 +601,8 @@ func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // prependPath(dir) -// Prepends `dir` to $PATH -// --- @param dir string +// Prepends `dir` to $PATH. +// #param dir string func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -547,8 +625,8 @@ func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // which(name) -> string // Checks if `name` is a valid command. // Will return the path of the binary, or a basename if it's a commander. -// --- @param name string -// --- @returns string +// #param name string +// #returns string func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -578,8 +656,10 @@ func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // inputMode(mode) -// Sets the input mode for Hilbish's line reader. Accepts either emacs or vim -// --- @param mode string +// Sets the input mode for Hilbish's line reader. +// `emacs` is the default. Setting it to `vim` changes behavior of input to be +// Vim-like with modes and Vim keybinds. +// #param mode string Can be set to either `emacs` or `vim` func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -604,12 +684,14 @@ func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // runnerMode(mode) -// Sets the execution/runner mode for interactive Hilbish. This determines whether -// Hilbish wll try to run input as Lua and/or sh or only do one of either. +// Sets the execution/runner mode for interactive Hilbish. +// This determines whether Hilbish wll try to run input as Lua +// and/or sh or only do one of either. // Accepted values for mode are hybrid (the default), hybridRev (sh first then Lua), // sh, and lua. It also accepts a function, to which if it is passed one // will call it to execute user input instead. -// --- @param mode string|function +// Read [about runner mode](../features/runner-mode) for more information. +// #param mode string|function func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -635,26 +717,33 @@ func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // line and cursor position. It is expected to return a string which is used // as the text for the hint. This is by default a shim. To set hints, // override this function with your custom handler. -// --- @param line string -// --- @param pos number +// #param line string +// #param pos number Position of cursor in line. Usually equals string.len(line) +/* +#example +-- this will display "hi" after the cursor in a dimmed color. +function hilbish.hinter(line, pos) + return 'hi' +end +#example +*/ func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), nil } // highlighter(line) -// Line highlighter handler. This is mainly for syntax highlighting, but in -// reality could set the input of the prompt to *display* anything. The -// callback is passed the current line and is expected to return a line that -// will be used as the input display. +// Line highlighter handler. +// This is mainly for syntax highlighting, but in reality could set the input +// of the prompt to *display* anything. The callback is passed the current line +// and is expected to return a line that will be used as the input display. // Note that to set a highlighter, one has to override this function. -// Example: -// ``` +// #example +// --This code will highlight all double quoted strings in green. // function hilbish.highlighter(line) // return line:gsub('"%w+"', function(c) return lunacolors.green(c) end) // end -// ``` -// This code will highlight all double quoted strings in green. -// --- @param line string +// #example +// #param line string func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), nil } diff --git a/cmd/docgen/docgen.go b/cmd/docgen/docgen.go index cbe6baa..86a622a 100644 --- a/cmd/docgen/docgen.go +++ b/cmd/docgen/docgen.go @@ -11,6 +11,8 @@ import ( "strings" "os" "sync" + + md "github.com/atsushinee/go-markdown-generator/doc" ) var header = `--- @@ -43,6 +45,12 @@ type module struct { HasTypes bool } +type param struct{ + Name string + Type string + Doc []string +} + type docPiece struct { Doc []string FuncSig string @@ -55,11 +63,14 @@ type docPiece struct { IsType bool Fields []docPiece Properties []docPiece + Params []param + Tags map[string][]tag } type tag struct { id string fields []string + startIdx int } var docs = make(map[string]module) @@ -80,7 +91,7 @@ func getTagsAndDocs(docs string) (map[string][]tag, []string) { parts := []string{} tags := make(map[string][]tag) - for _, part := range pts { + for idx, part := range pts { if strings.HasPrefix(part, "#") { tagParts := strings.Split(strings.TrimPrefix(part, "#"), " ") if tags[tagParts[0]] == nil { @@ -89,12 +100,21 @@ func getTagsAndDocs(docs string) (map[string][]tag, []string) { id = tagParts[1] } tags[tagParts[0]] = []tag{ - {id: id}, + {id: id, startIdx: idx}, } if len(tagParts) >= 2 { tags[tagParts[0]][0].fields = tagParts[2:] } } else { + if tagParts[0] == "example" { + exampleIdx := tags["example"][0].startIdx + exampleCode := pts[exampleIdx+1:idx] + + tags["example"][0].fields = exampleCode + parts = strings.Split(strings.Replace(strings.Join(parts, "\n"), strings.TrimPrefix(strings.Join(exampleCode, "\n"), "#example\n"), "", -1), "\n") + continue + } + fleds := []string{} if len(tagParts) >= 2 { fleds = tagParts[2:] @@ -179,6 +199,7 @@ func setupDocType(mod string, typ *doc.Type) *docPiece { ParentModule: parentMod, Fields: fields, Properties: properties, + Tags: tags, } typeTable[strings.ToLower(typeName)] = []string{parentMod, interfaces} @@ -215,6 +236,17 @@ start: fields := docPieceTag("field", tags) properties := docPieceTag("property", tags) + var params []param + if paramsRaw := tags["param"]; paramsRaw != nil { + params = make([]param, len(paramsRaw)) + for i, p := range paramsRaw { + params[i] = param{ + Name: p.id, + Type: p.fields[0], + Doc: p.fields[1:], + } + } + } for _, d := range doc { if strings.HasPrefix(d, "---") { @@ -252,6 +284,8 @@ start: ParentModule: parentMod, Fields: fields, Properties: properties, + Params: params, + Tags: tags, } if strings.HasSuffix(dps.GoFuncName, strings.ToLower("loader")) { dps.Doc = parts @@ -412,13 +446,14 @@ func main() { defer wg.Done() modOrIface := "Module" if modu.ParentModule != "" { - modOrIface = "Interface" + modOrIface = "Module" } + lastHeader := "" f, _ := os.Create(docPath) f.WriteString(fmt.Sprintf(header, modOrIface, modname, modu.ShortDescription)) typeTag, _ := regexp.Compile(`\B@\w+`) - modDescription := typeTag.ReplaceAllStringFunc(strings.Replace(modu.Description, "<", `\<`, -1), func(typ string) string { + modDescription := typeTag.ReplaceAllStringFunc(strings.Replace(strings.Replace(modu.Description, "<", `\<`, -1), "{{\\<", "{{<", -1), func(typ string) string { typName := typ[1:] typLookup := typeTable[strings.ToLower(typName)] ifaces := typLookup[0] + "." + typLookup[1] + "/" @@ -429,32 +464,77 @@ func main() { return fmt.Sprintf(`%s`, linkedTyp, typName) }) f.WriteString(fmt.Sprintf("## Introduction\n%s\n\n", modDescription)) - if len(modu.Fields) != 0 { - f.WriteString("## Interface fields\n") - for _, dps := range modu.Fields { - f.WriteString(fmt.Sprintf("- `%s`: ", dps.FuncName)) - f.WriteString(strings.Join(dps.Doc, " ")) - f.WriteString("\n") - } - f.WriteString("\n") - } - if len(modu.Properties) != 0 { - f.WriteString("## Object properties\n") - for _, dps := range modu.Properties { - f.WriteString(fmt.Sprintf("- `%s`: ", dps.FuncName)) - f.WriteString(strings.Join(dps.Doc, " ")) - f.WriteString("\n") - } - f.WriteString("\n") - } - if len(modu.Docs) != 0 { - f.WriteString("## Functions\n") + funcCount := 0 for _, dps := range modu.Docs { if dps.IsMember { continue } - htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(dps.FuncSig, "<", `\<`, -1), func(typ string) string { + funcCount++ + } + + f.WriteString("## Functions\n") + lastHeader = "functions" + + mdTable := md.NewTable(funcCount, 2) + mdTable.SetTitle(0, "") + mdTable.SetTitle(1, "") + + diff := 0 + for i, dps := range modu.Docs { + if dps.IsMember { + diff++ + continue + } + + mdTable.SetContent(i - diff, 0, fmt.Sprintf(`%s`, dps.FuncName, dps.FuncSig)) + mdTable.SetContent(i - diff, 1, dps.Doc[0]) + } + f.WriteString(mdTable.String()) + f.WriteString("\n") + } + + if len(modu.Fields) != 0 { + f.WriteString("## Static module fields\n") + + mdTable := md.NewTable(len(modu.Fields), 2) + mdTable.SetTitle(0, "") + mdTable.SetTitle(1, "") + + + for i, dps := range modu.Fields { + mdTable.SetContent(i, 0, dps.FuncName) + mdTable.SetContent(i, 1, strings.Join(dps.Doc, " ")) + } + f.WriteString(mdTable.String()) + f.WriteString("\n") + } + if len(modu.Properties) != 0 { + f.WriteString("## Object properties\n") + + mdTable := md.NewTable(len(modu.Fields), 2) + mdTable.SetTitle(0, "") + mdTable.SetTitle(1, "") + + + for i, dps := range modu.Properties { + mdTable.SetContent(i, 0, dps.FuncName) + mdTable.SetContent(i, 1, strings.Join(dps.Doc, " ")) + } + f.WriteString(mdTable.String()) + f.WriteString("\n") + } + + if len(modu.Docs) != 0 { + if lastHeader != "functions" { + f.WriteString("## Functions\n") + } + for _, dps := range modu.Docs { + if dps.IsMember { + continue + } + f.WriteString(fmt.Sprintf("
\n
", dps.FuncName)) + htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(modname + "." + dps.FuncSig, "<", `\<`, -1), func(typ string) string { typName := typ[1:] typLookup := typeTable[strings.ToLower(typName)] ifaces := typLookup[0] + "." + typLookup[1] + "/" @@ -462,21 +542,55 @@ func main() { ifaces = "" } linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s#%s", typLookup[0], ifaces, strings.ToLower(typName)) - return fmt.Sprintf(`%s`, linkedTyp, typName) + return fmt.Sprintf(`%s`, linkedTyp, typName) }) - f.WriteString(fmt.Sprintf("### %s\n", htmlSig)) + f.WriteString(fmt.Sprintf(` +

+%s + + + +

+ +`, htmlSig, dps.FuncName)) for _, doc := range dps.Doc { - if !strings.HasPrefix(doc, "---") { - f.WriteString(doc + "\n") + if !strings.HasPrefix(doc, "---") && doc != "" { + f.WriteString(doc + " \n") } } - f.WriteString("\n") + f.WriteString("\n#### Parameters\n") + if len(dps.Params) == 0 { + f.WriteString("This function has no parameters. \n") + } + for _, p := range dps.Params { + isVariadic := false + typ := p.Type + if strings.HasPrefix(p.Type, "...") { + isVariadic = true + typ = p.Type[3:] + } + + f.WriteString(fmt.Sprintf("`%s` **`%s`**", typ, p.Name)) + if isVariadic { + f.WriteString(" (This type is variadic. You can pass an infinite amount of parameters with this type.)") + } + f.WriteString(" \n") + f.WriteString(strings.Join(p.Doc, " ")) + f.WriteString("\n\n") + } + if codeExample := dps.Tags["example"]; codeExample != nil { + f.WriteString("#### Example\n") + f.WriteString(fmt.Sprintf("```lua\n%s\n```\n", strings.Join(codeExample[0].fields, "\n"))) + } + f.WriteString("
") + f.WriteString("\n\n") } } if len(modu.Types) != 0 { f.WriteString("## Types\n") for _, dps := range modu.Types { + f.WriteString("
\n\n") f.WriteString(fmt.Sprintf("## %s\n", dps.FuncName)) for _, doc := range dps.Doc { if !strings.HasPrefix(doc, "---") { @@ -484,12 +598,18 @@ func main() { } } if len(dps.Properties) != 0 { - f.WriteString("### Properties\n") - for _, dps := range dps.Properties { - f.WriteString(fmt.Sprintf("- `%s`: ", dps.FuncName)) - f.WriteString(strings.Join(dps.Doc, " ")) - f.WriteString("\n") + f.WriteString("## Object properties\n") + + mdTable := md.NewTable(len(dps.Properties), 2) + mdTable.SetTitle(0, "") + mdTable.SetTitle(1, "") + + for i, d := range dps.Properties { + mdTable.SetContent(i, 0, d.FuncName) + mdTable.SetContent(i, 1, strings.Join(d.Doc, " ")) } + f.WriteString(mdTable.String()) + f.WriteString("\n") } f.WriteString("\n") f.WriteString("### Methods\n") diff --git a/cmd/docgen/docgen.lua b/cmd/docgen/docgen.lua new file mode 100644 index 0000000..207357a --- /dev/null +++ b/cmd/docgen/docgen.lua @@ -0,0 +1,146 @@ +local fs = require 'fs' +local emmyPattern = '^%-%-%- (.+)' +local modpattern = '^%-+ @module (%w+)' +local pieces = {} + +local files = fs.readdir 'nature' +for _, fname in ipairs(files) do + local isScript = fname:match'%.lua$' + if not isScript then goto continue end + + local f = io.open(string.format('nature/%s', fname)) + local header = f:read '*l' + local mod = header:match(modpattern) + if not mod then goto continue end + + print(fname, mod) + pieces[mod] = {} + + local docPiece = {} + local lines = {} + local lineno = 0 + for line in f:lines() do + lineno = lineno + 1 + lines[lineno] = line + + if line == header then goto continue2 end + if not line:match(emmyPattern) then + if line:match '^function' then + local pattern = (string.format('^function %s%%.', mod) .. '(%w+)') + local funcName = line:match(pattern) + if not funcName then goto continue2 end + + local dps = { + description = {}, + params = {} + } + + local offset = 1 + while true do + local prev = lines[lineno - offset] + + local docline = prev:match '^%-+ (.+)' + if docline then + local emmy = docline:match '@(%w+)' + local cut = 0 + + if emmy then cut = emmy:len() + 3 end + local emmythings = string.split(docline:sub(cut), ' ') + + if emmy then + if emmy == 'param' then + table.insert(dps.params, 1, { + name = emmythings[1], + type = emmythings[2] + }) + end + else + table.insert(dps.description, 1, docline) + end + offset = offset + 1 + else + break + end + end + + pieces[mod][funcName] = dps + end + docPiece = {} + goto continue2 + end + + table.insert(docPiece, line) + ::continue2:: + end + ::continue:: +end + +local header = [[--- +title: %s %s +description: %s +layout: doc +menu: + docs: + parent: "Nature" +--- + +]] + +for iface, dps in pairs(pieces) do + local mod = iface:match '(%w+)%.' or 'nature' + local path = string.format('docs/%s/%s.md', mod, iface) + fs.mkdir(fs.dir(path), true) + local f = io.open(path, 'w') + f:write(string.format(header, 'Module', iface, 'No description.')) + print(f) + + print(mod, path) + + for func, docs in pairs(dps) do + f:write(string.format('
\n
', func)) + local sig = string.format('%s.%s(', iface, func) + for idx, param in ipairs(docs.params) do + sig = sig .. ((param.name:gsub('%?$', ''))) + if idx ~= #docs.params then sig = sig .. ', ' end + end + sig = sig .. ')' + f:write(string.format([[ +

+%s + + + +

+ +]], sig, func)) + + f:write(table.concat(docs.description, '\n') .. '\n') + f:write '#### Parameters\n' + if #docs.params == 0 then + f:write 'This function has no parameters. \n' + end + for _, param in ipairs(docs.params) do + f:write(string.format('`%s` **`%s`**\n', param.name:gsub('%?$', ''), param.type)) + end + --[[ + local params = table.filter(docs, function(t) + return t:match '^%-%-%- @param' + end) + for i, str in ipairs(params) do + if i ~= 1 then + f:write ', ' + end + f:write(str:match '^%-%-%- @param ([%w]+) ') + end + f:write(')\n') + + for _, str in ipairs(docs) do + if not str:match '^%-%-%- @' then + f:write(str:match '^%-%-%- (.+)' .. '\n') + end + end + ]]-- + f:write('
') + f:write('\n\n') + end +end diff --git a/complete.go b/complete.go index 4975ebd..71d92fb 100644 --- a/complete.go +++ b/complete.go @@ -193,10 +193,10 @@ func escapeFilename(fname string) string { // The completions interface deals with tab completions. func completionLoader(rtm *rt.Runtime) *rt.Table { exports := map[string]util.LuaExport{ - "files": {luaFileComplete, 3, false}, - "bins": {luaBinaryComplete, 3, false}, - "call": {callLuaCompleter, 4, false}, - "handler": {completionHandler, 2, false}, + "bins": {hcmpBins, 3, false}, + "call": {hcmpCall, 4, false}, + "files": {hcmpFiles, 3, false}, + "handler": {hcmpHandler, 2, false}, } mod := rt.NewTable() @@ -206,26 +206,57 @@ func completionLoader(rtm *rt.Runtime) *rt.Table { } // #interface completion -// handler(line, pos) -// The handler function is the callback for tab completion in Hilbish. -// You can check the completions doc for more info. -// --- @param line string -// --- @param pos string -func completionHandler(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - return c.Next(), nil +// bins(query, ctx, fields) -> entries (table), prefix (string) +// Return binaries/executables based on the provided parameters. +// This function is meant to be used as a helper in a command completion handler. +// #param query string +// #param ctx string +// #param fields table +/* +#example +-- an extremely simple completer for sudo. +hilbish.complete('command.sudo', function(query, ctx, fields) + table.remove(fields, 1) + if #fields[1] then + -- return commands because sudo runs a command as root..! + + local entries, pfx = hilbish.completion.bins(query, ctx, fields) + return { + type = 'grid', + items = entries + }, pfx + end + + -- ... else suggest files or anything else .. +end) +#example +*/ +func hcmpBins(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + query, ctx, fds, err := getCompleteParams(t, c) + if err != nil { + return nil, err + } + + completions, pfx := binaryComplete(query, ctx, fds) + luaComps := rt.NewTable() + + for i, comp := range completions { + luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp)) + } + + return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil } // #interface completion // call(name, query, ctx, fields) -> completionGroups (table), prefix (string) -// Calls a completer function. This is mainly used to call -// a command completer, which will have a `name` in the form -// of `command.name`, example: `command.git`. -// You can check `doc completions` for info on the `completionGroups` return value. -// --- @param name string -// --- @param query string -// --- @param ctx string -// --- @param fields table -func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { +// Calls a completer function. This is mainly used to call a command completer, which will have a `name` +// in the form of `command.name`, example: `command.git`. +// You can check the Completions doc or `doc completions` for info on the `completionGroups` return value. +// #param name string +// #param query string +// #param ctx string +// #param fields table +func hcmpCall(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.CheckNArgs(4); err != nil { return nil, err } @@ -267,11 +298,12 @@ func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #interface completion // files(query, ctx, fields) -> entries (table), prefix (string) -// Returns file completion candidates based on the provided query. -// --- @param query string -// --- @param ctx string -// --- @param fields table -func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { +// Returns file matches based on the provided parameters. +// This function is meant to be used as a helper in a command completion handler. +// #param query string +// #param ctx string +// #param fields table +func hcmpFiles(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { query, ctx, fds, err := getCompleteParams(t, c) if err != nil { return nil, err @@ -288,27 +320,31 @@ func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // #interface completion -// bins(query, ctx, fields) -> entries (table), prefix (string) -// Returns binary/executale completion candidates based on the provided query. -// --- @param query string -// --- @param ctx string -// --- @param fields table -func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - query, ctx, fds, err := getCompleteParams(t, c) - if err != nil { - return nil, err - } +// handler(line, pos) +// This function contains the general completion handler for Hilbish. This function handles +// completion of everything, which includes calling other command handlers, binaries, and files. +// This function can be overriden to supply a custom handler. Note that alias resolution is required to be done in this function. +// #param line string The current Hilbish command line +// #param pos number Numerical position of the cursor +/* +#example +-- stripped down version of the default implementation +function hilbish.completion.handler(line, pos) + local query = fields[#fields] - completions, pfx := binaryComplete(query, ctx, fds) - luaComps := rt.NewTable() - - for i, comp := range completions { - luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp)) - } - - return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil + if #fields == 1 then + -- call bins handler here + else + -- call command completer or files completer here + end +end +#example +*/ +func hcmpHandler(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + return c.Next(), nil } + func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, error) { if err := c.CheckNArgs(3); err != nil { return "", "", []string{}, err diff --git a/website/content/docs/_index.md b/docs/_index.md similarity index 100% rename from website/content/docs/_index.md rename to docs/_index.md diff --git a/docs/api/_index.md b/docs/api/_index.md index 8c9f722..f34539e 100644 --- a/docs/api/_index.md +++ b/docs/api/_index.md @@ -1,7 +1,7 @@ --- title: API layout: doc -weight: -50 +weight: -100 menu: docs --- diff --git a/docs/api/bait.md b/docs/api/bait.md index a70eb17..60b1056 100644 --- a/docs/api/bait.md +++ b/docs/api/bait.md @@ -8,27 +8,160 @@ menu: --- ## Introduction -Bait is the event emitter for Hilbish. Why name it bait? Why not. -It throws hooks that you can catch. This is what you will use if -you want to listen in on hooks to know when certain things have -happened, like when you've changed directory, a command has failed, -etc. To find all available hooks thrown by Hilbish, see doc hooks. + +Bait is the event emitter for Hilbish. Much like Node.js and +its `events` system, many actions in Hilbish emit events. +Unlike Node.js, Hilbish events are global. So make sure to +pick a unique name! + +Usage of the Bait module consists of userstanding +event-driven architecture, but it's pretty simple: +If you want to act on a certain event, you can `catch` it. +You can act on events via callback functions. + +Examples of this are in the Hilbish default config! +Consider this part of it: +```lua +bait.catch('command.exit', function(code) + running = false + doPrompt(code ~= 0) + doNotifyPrompt() +end) +``` + +What this does is, whenever the `command.exit` event is thrown, +this function will set the user prompt. ## Functions -### catch(name, cb) -Catches a hook with `name`. Runs the `cb` when it is thrown +||| +|----|----| +|catch(name, cb)|Catches an event. This function can be used to act on events.| +|catchOnce(name, cb)|Catches an event, but only once. This will remove the hook immediately after it runs for the first time.| +|hooks(name) -> table|Returns a table of functions that are hooked on an event with the corresponding `name`.| +|release(name, catcher)|Removes the `catcher` for the event with `name`.| +|throw(name, ...args)|Throws a hook with `name` with the provided `args`.| -### catchOnce(name, cb) -Same as catch, but only runs the `cb` once and then removes the hook +
+
+

+bait.catch(name, cb) + + + +

-### hooks(name) -> table -Returns a table with hooks (callback functions) on the event with `name`. +Catches an event. This function can be used to act on events. -### release(name, catcher) -Removes the `catcher` for the event with `name`. -For this to work, `catcher` has to be the same function used to catch -an event, like one saved to a variable. +#### Parameters +`string` **`name`** +The name of the hook. -### throw(name, ...args) -Throws a hook with `name` with the provided `args` +`function` **`cb`** +The function that will be called when the hook is thrown. + +#### Example +```lua +bait.catch('hilbish.exit', function() + print 'Goodbye Hilbish!' +end) +``` +
+ +
+
+

+bait.catchOnce(name, cb) + + + +

+ +Catches an event, but only once. This will remove the hook immediately after it runs for the first time. + +#### Parameters +`string` **`name`** +The name of the event + +`function` **`cb`** +The function that will be called when the event is thrown. + +
+ +
+
+

+bait.hooks(name) -> table + + + +

+ +Returns a table of functions that are hooked on an event with the corresponding `name`. + +#### Parameters +`string` **`name`** +The name of the hook + +
+ +
+
+

+bait.release(name, catcher) + + + +

+ +Removes the `catcher` for the event with `name`. +For this to work, `catcher` has to be the same function used to catch +an event, like one saved to a variable. + +#### Parameters +`string` **`name`** +Name of the event the hook is on + +`function` **`catcher`** +Hook function to remove + +#### Example +```lua +local hookCallback = function() print 'hi' end + +bait.catch('event', hookCallback) + +-- a little while later.... +bait.release('event', hookCallback) +-- and now hookCallback will no longer be ran for the event. +``` +
+ +
+
+

+bait.throw(name, ...args) + + + +

+ +Throws a hook with `name` with the provided `args`. + +#### Parameters +`string` **`name`** +The name of the hook. + +`any` **`args`** (This type is variadic. You can pass an infinite amount of parameters with this type.) +The arguments to pass to the hook. + +#### Example +```lua +bait.throw('greeting', 'world') + +-- This can then be listened to via +bait.catch('gretting', function(greetTo) + print('Hello ' .. greetTo) +end) +``` +
diff --git a/docs/api/commander.md b/docs/api/commander.md index 341eeda..03ece54 100644 --- a/docs/api/commander.md +++ b/docs/api/commander.md @@ -9,11 +9,10 @@ menu: ## Introduction -Commander is a library for writing custom commands in Lua. -In order to make it easier to write commands for Hilbish, -not require separate scripts and to be able to use in a config, -the Commander library exists. This is like a very simple wrapper -that works with Hilbish for writing commands. Example: +Commander is the library which handles Hilbish commands. This makes +the user able to add Lua-written commands to their shell without making +a separate script in a bin folder. Instead, you may simply use the Commander +library in your Hilbish config. ```lua local commander = require 'commander' @@ -28,19 +27,67 @@ that will print `Hello world!` to output. One question you may have is: What is the `sinks` parameter? The `sinks` parameter is a table with 3 keys: `in`, `out`, -and `err`. The values of these is a Sink. +and `err`. All of them are a Sink. -- `in` is the standard input. You can read from this sink -to get user input. (**This is currently unimplemented.**) -- `out` is standard output. This is usually where text meant for -output should go. -- `err` is standard error. This sink is for writing errors, as the -name would suggest. +- `in` is the standard input. +You may use the read functions on this sink to get input from the user. +- `out` is standard output. +This is usually where command output should go. +- `err` is standard error. +This sink is for writing errors, as the name would suggest. ## Functions -### deregister(name) -Deregisters any command registered with `name` +||| +|----|----| +|deregister(name)|Removes the named command. Note that this will only remove Commander-registered commands.| +|register(name, cb)|Adds a new command with the given `name`. When Hilbish has to run a command with a name,| -### register(name, cb) -Register a command with `name` that runs `cb` when ran +
+
+

+commander.deregister(name) + + + +

+ +Removes the named command. Note that this will only remove Commander-registered commands. + +#### Parameters +`string` **`name`** +Name of the command to remove. + +
+ +
+
+

+commander.register(name, cb) + + + +

+ +Adds a new command with the given `name`. When Hilbish has to run a command with a name, +it will run the function providing the arguments and sinks. + +#### Parameters +`string` **`name`** +Name of the command + +`function` **`cb`** +Callback to handle command invocation + +#### Example +```lua +-- When you run the command `hello` in the shell, it will print `Hello world`. +-- If you run it with, for example, `hello Hilbish`, it will print 'Hello Hilbish' +commander.register('hello', function(args, sinks) + local name = 'world' + if #args > 0 then name = args[1] end + + sinks.out:writeln('Hello ' .. name) +end) +``` +
diff --git a/docs/api/fs.md b/docs/api/fs.md index ee6949f..bc14055 100644 --- a/docs/api/fs.md +++ b/docs/api/fs.md @@ -8,44 +8,233 @@ menu: --- ## Introduction -The fs module provides easy and simple access to filesystem functions -and other things, and acts an addition to the Lua standard library's -I/O and filesystem functions. + +The fs module provides filesystem functions to Hilbish. While Lua's standard +library has some I/O functions, they're missing a lot of the basics. The `fs` +library offers more functions and will work on any operating system Hilbish does. ## Functions -### abs(path) -> string -Gives an absolute version of `path`. +||| +|----|----| +|abs(path) -> string|Returns an absolute version of the `path`.| +|basename(path) -> string|Returns the "basename," or the last part of the provided `path`. If path is empty,| +|cd(dir)|Changes Hilbish's directory to `dir`.| +|dir(path) -> string|Returns the directory part of `path`. If a file path like| +|glob(pattern) -> matches (table)|Match all files based on the provided `pattern`.| +|join(...path) -> string|Takes any list of paths and joins them based on the operating system's path separator.| +|mkdir(name, recursive)|Creates a new directory with the provided `name`.| +|readdir(path) -> table[string]|Returns a list of all files and directories in the provided path.| +|stat(path) -> {}|Returns the information about a given `path`.| -### basename(path) -> string -Gives the basename of `path`. For the rules, -see Go's filepath.Base +## Static module fields +||| +|----|----| +|pathSep|The operating system's path separator.| -### cd(dir) -Changes directory to `dir` +
+
+

+fs.abs(path) -> string + + + +

-### dir(path) -> string -Returns the directory part of `path`. For the rules, see Go's -filepath.Dir +Returns an absolute version of the `path`. +This can be used to resolve short paths like `..` to `/home/user`. -### glob(pattern) -> matches (table) -Glob all files and directories that match the pattern. -For the rules, see Go's filepath.Glob +#### Parameters +`string` **`path`** -### join(...) -> string -Takes paths and joins them together with the OS's -directory separator (forward or backward slash). -### mkdir(name, recursive) -Makes a directory called `name`. If `recursive` is true, it will create its parent directories. +
-### readdir(dir) -> {} -Returns a table of files in `dir`. +
+
+

+fs.basename(path) -> string + + + +

-### stat(path) -> {} -Returns a table of info about the `path`. -It contains the following keys: -name (string) - Name of the path -size (number) - Size of the path -mode (string) - Permission mode in an octal format string (with leading 0) -isDir (boolean) - If the path is a directory +Returns the "basename," or the last part of the provided `path`. If path is empty, +`.` will be returned. + +#### Parameters +`string` **`path`** +Path to get the base name of. + +
+ +
+
+

+fs.cd(dir) + + + +

+ +Changes Hilbish's directory to `dir`. + +#### Parameters +`string` **`dir`** +Path to change directory to. + +
+ +
+
+

+fs.dir(path) -> string + + + +

+ +Returns the directory part of `path`. If a file path like +`~/Documents/doc.txt` then this function will return `~/Documents`. + +#### Parameters +`string` **`path`** +Path to get the directory for. + +
+ +
+
+

+fs.glob(pattern) -> matches (table) + + + +

+ +Match all files based on the provided `pattern`. +For the syntax' refer to Go's filepath.Match function: https://pkg.go.dev/path/filepath#Match + +#### Parameters +`string` **`pattern`** +Pattern to compare files with. + +#### Example +```lua +--[[ + Within a folder that contains the following files: + a.txt + init.lua + code.lua + doc.pdf +]]-- +local matches = fs.glob './*.lua' +print(matches) +-- -> {'init.lua', 'code.lua'} +``` +
+ +
+
+

+fs.join(...path) -> string + + + +

+ +Takes any list of paths and joins them based on the operating system's path separator. + +#### Parameters +`string` **`path`** (This type is variadic. You can pass an infinite amount of parameters with this type.) +Paths to join together + +#### Example +```lua +-- This prints the directory for Hilbish's config! +print(fs.join(hilbish.userDir.config, 'hilbish')) +-- -> '/home/user/.config/hilbish' on Linux +``` +
+ +
+
+

+fs.mkdir(name, recursive) + + + +

+ +Creates a new directory with the provided `name`. +With `recursive`, mkdir will create parent directories. + +#### Parameters +`string` **`name`** +Name of the directory + +`boolean` **`recursive`** +Whether to create parent directories for the provided name + +#### Example +```lua +-- This will create the directory foo, then create the directory bar in the +-- foo directory. If recursive is false in this case, it will fail. +fs.mkdir('./foo/bar', true) +``` +
+ +
+
+

+fs.readdir(path) -> table[string] + + + +

+ +Returns a list of all files and directories in the provided path. + +#### Parameters +`string` **`dir`** + + +
+ +
+
+

+fs.stat(path) -> {} + + + +

+ +Returns the information about a given `path`. +The returned table contains the following values: +name (string) - Name of the path +size (number) - Size of the path in bytes +mode (string) - Unix permission mode in an octal format string (with leading 0) +isDir (boolean) - If the path is a directory + +#### Parameters +`string` **`path`** + + +#### Example +```lua +local inspect = require 'inspect' + +local stat = fs.stat '~' +print(inspect(stat)) +--[[ +Would print the following: +{ + isDir = true, + mode = "0755", + name = "username", + size = 12288 +} +]]-- +``` +
diff --git a/docs/api/hilbish/_index.md b/docs/api/hilbish/_index.md index a683172..b79dcde 100644 --- a/docs/api/hilbish/_index.md +++ b/docs/api/hilbish/_index.md @@ -11,108 +11,490 @@ menu: The Hilbish module includes the core API, containing interfaces and functions which directly relate to shell functionality. -## Interface fields -- `ver`: The version of Hilbish -- `goVersion`: The version of Go that Hilbish was compiled with -- `user`: Username of the user -- `host`: Hostname of the machine -- `dataDir`: Directory for Hilbish data files, including the docs and default modules -- `interactive`: Is Hilbish in an interactive shell? -- `login`: Is Hilbish the login shell? -- `vimMode`: Current Vim input mode of Hilbish (will be nil if not in Vim input mode) -- `exitCode`: xit code of the last executed command - ## Functions -### alias(cmd, orig) -Sets an alias of `cmd` to `orig` +||| +|----|----| +|alias(cmd, orig)|Sets an alias, with a name of `cmd` to another command.| +|appendPath(dir)|Appends the provided dir to the command path (`$PATH`)| +|complete(scope, cb)|Registers a completion handler for the specified scope.| +|cwd() -> string|Returns the current directory of the shell.| +|exec(cmd)|Replaces the currently running Hilbish instance with the supplied command.| +|goro(fn)|Puts `fn` in a Goroutine.| +|highlighter(line)|Line highlighter handler.| +|hinter(line, pos)|The command line hint handler. It gets called on every key insert to| +|inputMode(mode)|Sets the input mode for Hilbish's line reader.| +|interval(cb, time) -> @Timer|Runs the `cb` function every specified amount of `time`.| +|multiprompt(str)|Changes the text prompt when Hilbish asks for more input.| +|prependPath(dir)|Prepends `dir` to $PATH.| +|prompt(str, typ)|Changes the shell prompt to the provided string.| +|read(prompt) -> input (string)|Read input from the user, using Hilbish's line editor/input reader.| +|run(cmd, returnOut) -> exitCode (number), stdout (string), stderr (string)|Runs `cmd` in Hilbish's shell script interpreter.| +|runnerMode(mode)|Sets the execution/runner mode for interactive Hilbish.| +|timeout(cb, time) -> @Timer|Executed the `cb` function after a period of `time`.| +|which(name) -> string|Checks if `name` is a valid command.| -### appendPath(dir) -Appends `dir` to $PATH +## Static module fields +||| +|----|----| +|ver|The version of Hilbish| +|goVersion|The version of Go that Hilbish was compiled with| +|user|Username of the user| +|host|Hostname of the machine| +|dataDir|Directory for Hilbish data files, including the docs and default modules| +|interactive|Is Hilbish in an interactive shell?| +|login|Is Hilbish the login shell?| +|vimMode|Current Vim input mode of Hilbish (will be nil if not in Vim input mode)| +|exitCode|Exit code of the last executed command| -### complete(scope, cb) -Registers a completion handler for `scope`. -A `scope` is currently only expected to be `command.`, -replacing with the name of the command (for example `command.git`). -`cb` must be a function that returns a table of "completion groups." -Check `doc completions` for more information. +
+
+

+hilbish.alias(cmd, orig) + + + +

-### cwd() -> string -Returns the current directory of the shell +Sets an alias, with a name of `cmd` to another command. -### exec(cmd) -Replaces running hilbish with `cmd` +#### Parameters +`string` **`cmd`** +Name of the alias -### goro(fn) -Puts `fn` in a goroutine +`string` **`orig`** +Command that will be aliased -### highlighter(line) -Line highlighter handler. This is mainly for syntax highlighting, but in -reality could set the input of the prompt to *display* anything. The -callback is passed the current line and is expected to return a line that -will be used as the input display. -Note that to set a highlighter, one has to override this function. -Example: +#### Example +```lua +-- With this, "ga file" will turn into "git add file" +hilbish.alias('ga', 'git add') + +-- Numbered substitutions are supported here! +hilbish.alias('dircount', 'ls %1 | wc -l') +-- "dircount ~" would count how many files are in ~ (home directory). ``` +
+ +
+
+

+hilbish.appendPath(dir) + + + +

+ +Appends the provided dir to the command path (`$PATH`) + +#### Parameters +`string|table` **`dir`** +Directory (or directories) to append to path + +#### Example +```lua +hilbish.appendPath '~/go/bin' +-- Will add ~/go/bin to the command path. + +-- Or do multiple: +hilbish.appendPath { + '~/go/bin', + '~/.local/bin' +} +``` +
+ +
+
+

+hilbish.complete(scope, cb) + + + +

+ +Registers a completion handler for the specified scope. +A `scope` is expected to be `command.`, +replacing with the name of the command (for example `command.git`). +The documentation for completions, under Features/Completions or `doc completions` +provides more details. + +#### Parameters +`string` **`scope`** + + +`function` **`cb`** + + +#### Example +```lua +-- This is a very simple example. Read the full doc for completions for details. +hilbish.complete('command.sudo', function(query, ctx, fields) + if #fields == 0 then + -- complete for commands + local comps, pfx = hilbish.completion.bins(query, ctx, fields) + local compGroup = { + items = comps, -- our list of items to complete + type = 'grid' -- what our completions will look like. + } + + return {compGroup}, pfx + end + + -- otherwise just be boring and return files + + local comps, pfx = hilbish.completion.files(query, ctx, fields) + local compGroup = { + items = comps, + type = 'grid' + } + + return {compGroup}, pfx +end) +``` +
+ +
+
+

+hilbish.cwd() -> string + + + +

+ +Returns the current directory of the shell. + +#### Parameters +This function has no parameters. +
+ +
+
+

+hilbish.exec(cmd) + + + +

+ +Replaces the currently running Hilbish instance with the supplied command. +This can be used to do an in-place restart. + +#### Parameters +`string` **`cmd`** + + +
+ +
+
+

+hilbish.goro(fn) + + + +

+ +Puts `fn` in a Goroutine. +This can be used to run any function in another thread at the same time as other Lua code. +**NOTE: THIS FUNCTION MAY CRASH HILBISH IF OUTSIDE VARIABLES ARE ACCESSED.** +**This is a limitation of the Lua runtime.** + +#### Parameters +`function` **`fn`** + + +
+ +
+
+

+hilbish.highlighter(line) + + + +

+ +Line highlighter handler. +This is mainly for syntax highlighting, but in reality could set the input +of the prompt to *display* anything. The callback is passed the current line +and is expected to return a line that will be used as the input display. +Note that to set a highlighter, one has to override this function. + +#### Parameters +`string` **`line`** + + +#### Example +```lua +--This code will highlight all double quoted strings in green. function hilbish.highlighter(line) return line:gsub('"%w+"', function(c) return lunacolors.green(c) end) end ``` -This code will highlight all double quoted strings in green. +
-### hinter(line, pos) -The command line hint handler. It gets called on every key insert to -determine what text to use as an inline hint. It is passed the current -line and cursor position. It is expected to return a string which is used -as the text for the hint. This is by default a shim. To set hints, -override this function with your custom handler. +
+
+

+hilbish.hinter(line, pos) + + + +

-### inputMode(mode) -Sets the input mode for Hilbish's line reader. Accepts either emacs or vim +The command line hint handler. It gets called on every key insert to +determine what text to use as an inline hint. It is passed the current +line and cursor position. It is expected to return a string which is used +as the text for the hint. This is by default a shim. To set hints, +override this function with your custom handler. -### interval(cb, time) -> Timer -Runs the `cb` function every `time` milliseconds. -This creates a timer that starts immediately. +#### Parameters +`string` **`line`** -### multiprompt(str) -Changes the continued line prompt to `str` -### prependPath(dir) -Prepends `dir` to $PATH +`number` **`pos`** +Position of cursor in line. Usually equals string.len(line) -### prompt(str, typ) -Changes the shell prompt to `str` -There are a few verbs that can be used in the prompt text. -These will be formatted and replaced with the appropriate values. -`%d` - Current working directory -`%u` - Name of current user -`%h` - Hostname of device +#### Example +```lua +-- this will display "hi" after the cursor in a dimmed color. +function hilbish.hinter(line, pos) + return 'hi' +end +``` +
-### read(prompt) -> input (string) -Read input from the user, using Hilbish's line editor/input reader. -This is a separate instance from the one Hilbish actually uses. -Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen) +
+
+

+hilbish.inputMode(mode) + + + +

-### run(cmd, returnOut) -> exitCode (number), stdout (string), stderr (string) -Runs `cmd` in Hilbish's sh interpreter. -If returnOut is true, the outputs of `cmd` will be returned as the 2nd and -3rd values instead of being outputted to the terminal. +Sets the input mode for Hilbish's line reader. +`emacs` is the default. Setting it to `vim` changes behavior of input to be +Vim-like with modes and Vim keybinds. -### runnerMode(mode) -Sets the execution/runner mode for interactive Hilbish. This determines whether -Hilbish wll try to run input as Lua and/or sh or only do one of either. -Accepted values for mode are hybrid (the default), hybridRev (sh first then Lua), -sh, and lua. It also accepts a function, to which if it is passed one -will call it to execute user input instead. +#### Parameters +`string` **`mode`** +Can be set to either `emacs` or `vim` -### timeout(cb, time) -> Timer -Runs the `cb` function after `time` in milliseconds. -This creates a timer that starts immediately. +
-### which(name) -> string -Checks if `name` is a valid command. -Will return the path of the binary, or a basename if it's a commander. +
+
+

+hilbish.interval(cb, time) -> Timer + + + +

+ +Runs the `cb` function every specified amount of `time`. +This creates a timer that ticking immediately. + +#### Parameters +`function` **`cb`** + + +`number` **`time`** +Time in milliseconds. + +
+ +
+
+

+hilbish.multiprompt(str) + + + +

+ +Changes the text prompt when Hilbish asks for more input. +This will show up when text is incomplete, like a missing quote + +#### Parameters +`string` **`str`** + + +#### Example +```lua +--[[ +imagine this is your text input: +user ~ ∆ echo "hey + +but there's a missing quote! hilbish will now prompt you so the terminal +will look like: +user ~ ∆ echo "hey +--> ...!" + +so then you get +user ~ ∆ echo "hey +--> ...!" +hey ...! +]]-- +hilbish.multiprompt '-->' +``` +
+ +
+
+

+hilbish.prependPath(dir) + + + +

+ +Prepends `dir` to $PATH. + +#### Parameters +`string` **`dir`** + + +
+ +
+
+

+hilbish.prompt(str, typ) + + + +

+ +Changes the shell prompt to the provided string. +There are a few verbs that can be used in the prompt text. +These will be formatted and replaced with the appropriate values. +`%d` - Current working directory +`%u` - Name of current user +`%h` - Hostname of device + +#### Parameters +`string` **`str`** + + +`string` **`typ?`** +Type of prompt, being left or right. Left by default. + +#### Example +```lua +-- the default hilbish prompt without color +hilbish.prompt '%u %d ∆' +-- or something of old: +hilbish.prompt '%u@%h :%d $' +-- prompt: user@hostname: ~/directory $ +``` +
+ +
+
+

+hilbish.read(prompt) -> input (string) + + + +

+ +Read input from the user, using Hilbish's line editor/input reader. +This is a separate instance from the one Hilbish actually uses. +Returns `input`, will be nil if Ctrl-D is pressed, or an error occurs. + +#### Parameters +`string` **`prompt?`** +Text to print before input, can be empty. + +
+ +
+
+

+hilbish.run(cmd, returnOut) -> exitCode (number), stdout (string), stderr (string) + + + +

+ +Runs `cmd` in Hilbish's shell script interpreter. + +#### Parameters +`string` **`cmd`** + + +`boolean` **`returnOut`** +If this is true, the function will return the standard output and error of the command instead of printing it. + +
+ +
+
+

+hilbish.runnerMode(mode) + + + +

+ +Sets the execution/runner mode for interactive Hilbish. +This determines whether Hilbish wll try to run input as Lua +and/or sh or only do one of either. +Accepted values for mode are hybrid (the default), hybridRev (sh first then Lua), +sh, and lua. It also accepts a function, to which if it is passed one +will call it to execute user input instead. +Read [about runner mode](../features/runner-mode) for more information. + +#### Parameters +`string|function` **`mode`** + + +
+ +
+
+

+hilbish.timeout(cb, time) -> Timer + + + +

+ +Executed the `cb` function after a period of `time`. +This creates a Timer that starts ticking immediately. + +#### Parameters +`function` **`cb`** + + +`number` **`time`** +Time to run in milliseconds. + +
+ +
+
+

+hilbish.which(name) -> string + + + +

+ +Checks if `name` is a valid command. +Will return the path of the binary, or a basename if it's a commander. + +#### Parameters +`string` **`name`** + + +
## Types +
+ ## Sink A sink is a structure that has input and/or output to/from a desination. diff --git a/docs/api/hilbish/hilbish.aliases.md b/docs/api/hilbish/hilbish.aliases.md index bae5bfc..e0a6f48 100644 --- a/docs/api/hilbish/hilbish.aliases.md +++ b/docs/api/hilbish/hilbish.aliases.md @@ -1,5 +1,5 @@ --- -title: Interface hilbish.aliases +title: Module hilbish.aliases description: command aliasing layout: doc menu: @@ -11,15 +11,81 @@ menu: The alias interface deals with all command aliases in Hilbish. ## Functions -### add(alias, cmd) -This is an alias (ha) for the `hilbish.alias` function. +||| +|----|----| +|add(alias, cmd)|This is an alias (ha) for the [hilbish.alias](../#alias) function.| +|delete(name)|Removes an alias.| +|list() -> table[string, string]|Get a table of all aliases, with string keys as the alias and the value as the command.| +|resolve(alias) -> string?|Resolves an alias to its original command. Will thrown an error if the alias doesn't exist.| -### delete(name) -Removes an alias. +
+
+

+hilbish.aliases.add(alias, cmd) + + + +

-### list() -> table\ -Get a table of all aliases, with string keys as the alias and the value as the command. +This is an alias (ha) for the [hilbish.alias](../#alias) function. -### resolve(alias) -> command (string) -Tries to resolve an alias to its command. +#### Parameters +This function has no parameters. +
+ +
+
+

+hilbish.aliases.delete(name) + + + +

+ +Removes an alias. + +#### Parameters +`string` **`name`** + + +
+ +
+
+

+hilbish.aliases.list() -> table[string, string] + + + +

+ +Get a table of all aliases, with string keys as the alias and the value as the command. + +#### Parameters +This function has no parameters. +#### Example +```lua +hilbish.aliases.add('hi', 'echo hi') + +local aliases = hilbish.aliases.list() +-- -> {hi = 'echo hi'} +``` +
+ +
+
+

+hilbish.aliases.resolve(alias) -> string? + + + +

+ +Resolves an alias to its original command. Will thrown an error if the alias doesn't exist. + +#### Parameters +`string` **`alias`** + + +
diff --git a/docs/api/hilbish/hilbish.completion.md b/docs/api/hilbish/hilbish.completion.md index 8bee704..be6c094 100644 --- a/docs/api/hilbish/hilbish.completion.md +++ b/docs/api/hilbish/hilbish.completion.md @@ -1,5 +1,5 @@ --- -title: Interface hilbish.completion +title: Module hilbish.completion description: tab completions layout: doc menu: @@ -11,19 +11,139 @@ menu: The completions interface deals with tab completions. ## Functions -### call(name, query, ctx, fields) -> completionGroups (table), prefix (string) -Calls a completer function. This is mainly used to call -a command completer, which will have a `name` in the form -of `command.name`, example: `command.git`. -You can check `doc completions` for info on the `completionGroups` return value. +||| +|----|----| +|bins(query, ctx, fields) -> entries (table), prefix (string)|Return binaries/executables based on the provided parameters.| +|call(name, query, ctx, fields) -> completionGroups (table), prefix (string)|Calls a completer function. This is mainly used to call a command completer, which will have a `name`| +|files(query, ctx, fields) -> entries (table), prefix (string)|Returns file matches based on the provided parameters.| +|handler(line, pos)|This function contains the general completion handler for Hilbish. This function handles| -### handler(line, pos) -The handler function is the callback for tab completion in Hilbish. -You can check the completions doc for more info. +
+
+

+hilbish.completion.bins(query, ctx, fields) -> entries (table), prefix (string) + + + +

-### bins(query, ctx, fields) -> entries (table), prefix (string) -Returns binary/executale completion candidates based on the provided query. +Return binaries/executables based on the provided parameters. +This function is meant to be used as a helper in a command completion handler. -### files(query, ctx, fields) -> entries (table), prefix (string) -Returns file completion candidates based on the provided query. +#### Parameters +`string` **`query`** + + +`string` **`ctx`** + + +`table` **`fields`** + + +#### Example +```lua +-- an extremely simple completer for sudo. +hilbish.complete('command.sudo', function(query, ctx, fields) + table.remove(fields, 1) + if #fields[1] then + -- return commands because sudo runs a command as root..! + + local entries, pfx = hilbish.completion.bins(query, ctx, fields) + return { + type = 'grid', + items = entries + }, pfx + end + + -- ... else suggest files or anything else .. +end) +``` +
+ +
+
+

+hilbish.completion.call(name, query, ctx, fields) -> completionGroups (table), prefix (string) + + + +

+ +Calls a completer function. This is mainly used to call a command completer, which will have a `name` +in the form of `command.name`, example: `command.git`. +You can check the Completions doc or `doc completions` for info on the `completionGroups` return value. + +#### Parameters +`string` **`name`** + + +`string` **`query`** + + +`string` **`ctx`** + + +`table` **`fields`** + + +
+ +
+
+

+hilbish.completion.files(query, ctx, fields) -> entries (table), prefix (string) + + + +

+ +Returns file matches based on the provided parameters. +This function is meant to be used as a helper in a command completion handler. + +#### Parameters +`string` **`query`** + + +`string` **`ctx`** + + +`table` **`fields`** + + +
+ +
+
+

+hilbish.completion.handler(line, pos) + + + +

+ +This function contains the general completion handler for Hilbish. This function handles +completion of everything, which includes calling other command handlers, binaries, and files. +This function can be overriden to supply a custom handler. Note that alias resolution is required to be done in this function. + +#### Parameters +`string` **`line`** +The current Hilbish command line + +`number` **`pos`** +Numerical position of the cursor + +#### Example +```lua +-- stripped down version of the default implementation +function hilbish.completion.handler(line, pos) + local query = fields[#fields] + + if #fields == 1 then + -- call bins handler here + else + -- call command completer or files completer here + end +end +``` +
diff --git a/docs/api/hilbish/hilbish.completions.md b/docs/api/hilbish/hilbish.completions.md deleted file mode 100644 index 6f8740f..0000000 --- a/docs/api/hilbish/hilbish.completions.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Interface hilbish.completions -description: tab completions -layout: doc -menu: - docs: - parent: "API" ---- - -## Introduction -The completions interface deals with tab completions. - -## Functions -### call(name, query, ctx, fields) -> completionGroups (table), prefix (string) -Calls a completer function. This is mainly used to call -a command completer, which will have a `name` in the form -of `command.name`, example: `command.git`. -You can check `doc completions` for info on the `completionGroups` return value. - -### handler(line, pos) -The handler function is the callback for tab completion in Hilbish. -You can check the completions doc for more info. - -### bins(query, ctx, fields) -> entries (table), prefix (string) -Returns binary/executale completion candidates based on the provided query. - -### files(query, ctx, fields) -> entries (table), prefix (string) -Returns file completion candidates based on the provided query. - diff --git a/docs/api/hilbish/hilbish.editor.md b/docs/api/hilbish/hilbish.editor.md index d75d4c2..c70b605 100644 --- a/docs/api/hilbish/hilbish.editor.md +++ b/docs/api/hilbish/hilbish.editor.md @@ -1,5 +1,5 @@ --- -title: Interface hilbish.editor +title: Module hilbish.editor description: interactions for Hilbish's line reader layout: doc menu: @@ -12,19 +12,92 @@ The hilbish.editor interface provides functions to directly interact with the line editor in use. ## Functions -### getLine() -> string -Returns the current input line. +||| +|----|----| +|getLine() -> string|Returns the current input line.| +|getVimRegister(register) -> string|Returns the text that is at the register.| +|insert(text)|Inserts text into the Hilbish command line.| +|getChar() -> string|Reads a keystroke from the user. This is in a format of something like Ctrl-L.| +|setVimRegister(register, text)|Sets the vim register at `register` to hold the passed text.| -### getVimRegister(register) -> string -Returns the text that is at the register. +
+
+

+hilbish.editor.getLine() -> string + + + +

-### insert(text) -Inserts text into the line. +Returns the current input line. -### getChar() -> string -Reads a keystroke from the user. This is in a format -of something like Ctrl-L.. +#### Parameters +This function has no parameters. +
-### setVimRegister(register, text) -Sets the vim register at `register` to hold the passed text. +
+
+

+hilbish.editor.getVimRegister(register) -> string + + + +

+ +Returns the text that is at the register. + +#### Parameters +`string` **`register`** + + +
+ +
+
+

+hilbish.editor.insert(text) + + + +

+ +Inserts text into the Hilbish command line. + +#### Parameters +`string` **`text`** + + +
+ +
+
+

+hilbish.editor.getChar() -> string + + + +

+ +Reads a keystroke from the user. This is in a format of something like Ctrl-L. + +#### Parameters +This function has no parameters. +
+ +
+
+

+hilbish.editor.setVimRegister(register, text) + + + +

+ +Sets the vim register at `register` to hold the passed text. + +#### Parameters +`string` **`text`** + + +
diff --git a/docs/api/hilbish/hilbish.history.md b/docs/api/hilbish/hilbish.history.md index 9fa9b01..6de9bdf 100644 --- a/docs/api/hilbish/hilbish.history.md +++ b/docs/api/hilbish/hilbish.history.md @@ -1,5 +1,5 @@ --- -title: Interface hilbish.history +title: Module hilbish.history description: command history layout: doc menu: @@ -13,18 +13,90 @@ This includes the ability to override functions to change the main method of saving history. ## Functions -### add(cmd) -Adds a command to the history. +||| +|----|----| +|add(cmd)|Adds a command to the history.| +|all() -> table|Retrieves all history as a table.| +|clear()|Deletes all commands from the history.| +|get(index)|Retrieves a command from the history based on the `index`.| +|size() -> number|Returns the amount of commands in the history.| -### all() -> table -Retrieves all history. +
+
+

+hilbish.history.add(cmd) + + + +

-### clear() -Deletes all commands from the history. +Adds a command to the history. -### get(idx) -Retrieves a command from the history based on the `idx`. +#### Parameters +`string` **`cmd`** -### size() -> number -Returns the amount of commands in the history. + +
+ +
+
+

+hilbish.history.all() -> table + + + +

+ +Retrieves all history as a table. + +#### Parameters +This function has no parameters. +
+ +
+
+

+hilbish.history.clear() + + + +

+ +Deletes all commands from the history. + +#### Parameters +This function has no parameters. +
+ +
+
+

+hilbish.history.get(index) + + + +

+ +Retrieves a command from the history based on the `index`. + +#### Parameters +`number` **`index`** + + +
+ +
+
+

+hilbish.history.size() -> number + + + +

+ +Returns the amount of commands in the history. + +#### Parameters +This function has no parameters. +
diff --git a/docs/api/hilbish/hilbish.jobs.md b/docs/api/hilbish/hilbish.jobs.md index e41be2c..fe3978f 100644 --- a/docs/api/hilbish/hilbish.jobs.md +++ b/docs/api/hilbish/hilbish.jobs.md @@ -1,5 +1,5 @@ --- -title: Interface hilbish.jobs +title: Module hilbish.jobs description: background job management layout: doc menu: @@ -15,32 +15,120 @@ Jobs are the name of background tasks/commands. A job can be started via interactive usage or with the functions defined below for use in external runners. ## Functions -### add(cmdstr, args, execPath) -Adds a new job to the job table. Note that this does not immediately run it. +||| +|----|----| +|add(cmdstr, args, execPath)|Creates a new job. This function does not run the job. This function is intended to be| +|all() -> table[@Job]|Returns a table of all job objects.| +|disown(id)|Disowns a job. This simply deletes it from the list of jobs without stopping it.| +|get(id) -> @Job|Get a job object via its ID.| +|last() -> @Job|Returns the last added job to the table.| -### all() -> table\<Job> -Returns a table of all job objects. +
+
+

+hilbish.jobs.add(cmdstr, args, execPath) + + + +

-### disown(id) -Disowns a job. This deletes it from the job table. +Creates a new job. This function does not run the job. This function is intended to be +used by runners, but can also be used to create jobs via Lua. Commanders cannot be ran as jobs. -### get(id) -> Job -Get a job object via its ID. +#### Parameters +`string` **`cmdstr`** +String that a user would write for the job -### last() -> Job -Returns the last added job from the table. +`table` **`args`** +Arguments for the commands. Has to include the name of the command. + +`string` **`execPath`** +Binary to use to run the command. Needs to be an absolute path. + +#### Example +```lua +hilbish.jobs.add('go build', {'go', 'build'}, '/usr/bin/go') +``` +
+ +
+
+

+hilbish.jobs.all() -> table[Job] + + + +

+ +Returns a table of all job objects. + +#### Parameters +This function has no parameters. +
+ +
+
+

+hilbish.jobs.disown(id) + + + +

+ +Disowns a job. This simply deletes it from the list of jobs without stopping it. + +#### Parameters +`number` **`id`** + + +
+ +
+
+

+hilbish.jobs.get(id) -> Job + + + +

+ +Get a job object via its ID. + +#### Parameters +This function has no parameters. +
+ +
+
+

+hilbish.jobs.last() -> Job + + + +

+ +Returns the last added job to the table. + +#### Parameters +This function has no parameters. +
## Types +
+ ## Job The Job type describes a Hilbish job. -### Properties -- `cmd`: The user entered command string for the job. -- `running`: Whether the job is running or not. -- `id`: The ID of the job in the job table -- `pid`: The Process ID -- `exitCode`: The last exit code of the job. -- `stdout`: The standard output of the job. This just means the normal logs of the process. -- `stderr`: The standard error stream of the process. This (usually) includes error messages of the job. +## Object properties +||| +|----|----| +|cmd|The user entered command string for the job.| +|running|Whether the job is running or not.| +|id|The ID of the job in the job table| +|pid|The Process ID| +|exitCode|The last exit code of the job.| +|stdout|The standard output of the job. This just means the normal logs of the process.| +|stderr|The standard error stream of the process. This (usually) includes error messages of the job.| + ### Methods #### background() diff --git a/docs/api/hilbish/hilbish.module.md b/docs/api/hilbish/hilbish.module.md index e88ac2c..4029d7a 100644 --- a/docs/api/hilbish/hilbish.module.md +++ b/docs/api/hilbish/hilbish.module.md @@ -1,5 +1,5 @@ --- -title: Interface hilbish.module +title: Module hilbish.module description: native module loading layout: doc menu: @@ -43,11 +43,31 @@ func Loader(rtm *rt.Runtime) rt.Value { This can be compiled with `go build -buildmode=plugin plugin.go`. If you attempt to require and print the result (`print(require 'plugin')`), it will show "hello world!" -## Interface fields -- `paths`: A list of paths to search when loading native modules. This is in the style of Lua search paths and will be used when requiring native modules. Example: `?.so;?/?.so` - ## Functions -### load(path) -Loads a module at the designated `path`. -It will throw if any error occurs. +||| +|----|----| +|load(path)|Loads a module at the designated `path`.| + +## Static module fields +||| +|----|----| +|paths|A list of paths to search when loading native modules. This is in the style of Lua search paths and will be used when requiring native modules. Example: `?.so;?/?.so`| + +
+
+

+hilbish.module.load(path) + + + +

+ +Loads a module at the designated `path`. +It will throw if any error occurs. + +#### Parameters +`string` **`path`** + + +
diff --git a/docs/api/hilbish/hilbish.os.md b/docs/api/hilbish/hilbish.os.md index aa2198e..13b56b0 100644 --- a/docs/api/hilbish/hilbish.os.md +++ b/docs/api/hilbish/hilbish.os.md @@ -1,6 +1,6 @@ --- -title: Interface hilbish.os -description: OS Info +title: Module hilbish.os +description: operating system info layout: doc menu: docs: @@ -8,12 +8,13 @@ menu: --- ## Introduction -The `os` interface provides simple text information properties about -the current OS on the systen. This mainly includes the name and -version. +Provides simple text information properties about the current operating system. +This mainly includes the name and version. -## Interface fields -- `family`: Family name of the current OS -- `name`: Pretty name of the current OS -- `version`: Version of the current OS +## Static module fields +||| +|----|----| +|family|Family name of the current OS| +|name|Pretty name of the current OS| +|version|Version of the current OS| diff --git a/docs/api/hilbish/hilbish.runner.md b/docs/api/hilbish/hilbish.runner.md index 68ffdc6..b5cfde4 100644 --- a/docs/api/hilbish/hilbish.runner.md +++ b/docs/api/hilbish/hilbish.runner.md @@ -1,5 +1,5 @@ --- -title: Interface hilbish.runner +title: Module hilbish.runner description: interactive command runner customization layout: doc menu: @@ -8,24 +8,107 @@ menu: --- ## Introduction -The runner interface contains functions that allow the user to change + The runner interface contains functions that allow the user to change how Hilbish interprets interactive input. Users can add and change the default runner for interactive input to any language or script of their choosing. A good example is using it to write command in Fennel. +Runners are functions that evaluate user input. The default runners in +Hilbish can run shell script and Lua code. + +A runner is passed the input and has to return a table with these values. +All are not required, only the useful ones the runner needs to return. +(So if there isn't an error, just omit `err`.) + +- `exitCode` (number): A numerical code to indicate the exit result. +- `input` (string): The user input. This will be used to add +to the history. +- `err` (string): A string to indicate an interal error for the runner. +It can be set to a few special values for Hilbish to throw the right hooks and have a better looking message: + +`[command]: not-found` will throw a command.not-found hook based on what `[command]` is. + +`[command]: not-executable` will throw a command.not-executable hook. +- `continue` (boolean): Whether to prompt the user for more input. + +Here is a simple example of a fennel runner. It falls back to +shell script if fennel eval has an error. +```lua +local fennel = require 'fennel' + +hilbish.runnerMode(function(input) + local ok = pcall(fennel.eval, input) + if ok then + return { + input = input + } + end + + return hilbish.runner.sh(input) +end) +``` + ## Functions -### setMode(cb) -This is the same as the `hilbish.runnerMode` function. It takes a callback, -which will be used to execute all interactive input. -In normal cases, neither callbacks should be overrided by the user, -as the higher level functions listed below this will handle it. +||| +|----|----| +|setMode(cb)|This is the same as the `hilbish.runnerMode` function.| +|lua(cmd)|Evaluates `cmd` as Lua input. This is the same as using `dofile`| +|sh(cmd)|Runs a command in Hilbish's shell script interpreter.| -### lua(cmd) -Evaluates `cmd` as Lua input. This is the same as using `dofile` -or `load`, but is appropriated for the runner interface. +
+
+

+hilbish.runner.setMode(cb) + + + +

-### sh(cmd) -Runs a command in Hilbish's shell script interpreter. -This is the equivalent of using `source`. +This is the same as the `hilbish.runnerMode` function. +It takes a callback, which will be used to execute all interactive input. +In normal cases, neither callbacks should be overrided by the user, +as the higher level functions listed below this will handle it. + +#### Parameters +`function` **`cb`** + + +
+ +
+
+

+hilbish.runner.lua(cmd) + + + +

+ +Evaluates `cmd` as Lua input. This is the same as using `dofile` +or `load`, but is appropriated for the runner interface. + +#### Parameters +`string` **`cmd`** + + +
+ +
+
+

+hilbish.runner.sh(cmd) + + + +

+ +Runs a command in Hilbish's shell script interpreter. +This is the equivalent of using `source`. + +#### Parameters +`string` **`cmd`** + + +
diff --git a/docs/api/hilbish/hilbish.timers.md b/docs/api/hilbish/hilbish.timers.md index e899d1d..f218d2b 100644 --- a/docs/api/hilbish/hilbish.timers.md +++ b/docs/api/hilbish/hilbish.timers.md @@ -1,5 +1,5 @@ --- -title: Interface hilbish.timers +title: Module hilbish.timers description: timeout and interval API layout: doc menu: @@ -14,14 +14,10 @@ a few seconds, you don't have to rely on timing tricks, as Hilbish has a timer API to set intervals and timeouts. These are the simple functions `hilbish.interval` and `hilbish.timeout` (doc -accessible with `doc hilbish`). But if you want slightly more control over -them, there is the `hilbish.timers` interface. It allows you to get -a timer via ID and control them. - -All functions documented with the `Timer` type refer to a Timer object. +accessible with `doc hilbish`, or `Module hilbish` on the Website). An example of usage: -``` +```lua local t = hilbish.timers.create(hilbish.timers.TIMEOUT, 5000, function() print 'hello!' end) @@ -30,25 +26,70 @@ t:start() print(t.running) // true ``` -## Interface fields -- `INTERVAL`: Constant for an interval timer type -- `TIMEOUT`: Constant for a timeout timer type - ## Functions -### create(type, time, callback) -> Timer -Creates a timer that runs based on the specified `time` in milliseconds. -The `type` can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT` +||| +|----|----| +|create(type, time, callback) -> @Timer|Creates a timer that runs based on the specified `time`.| +|get(id) -> @Timer|Retrieves a timer via its ID.| -### get(id) -> Timer -Retrieves a timer via its ID. +## Static module fields +||| +|----|----| +|INTERVAL|Constant for an interval timer type| +|TIMEOUT|Constant for a timeout timer type| + +
+
+

+hilbish.timers.create(type, time, callback) -> Timer + + + +

+ +Creates a timer that runs based on the specified `time`. + +#### Parameters +`number` **`type`** +What kind of timer to create, can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT` + +`number` **`time`** +The amount of time the function should run in milliseconds. + +`function` **`callback`** +The function to run for the timer. + +
+ +
+
+

+hilbish.timers.get(id) -> Timer + + + +

+ +Retrieves a timer via its ID. + +#### Parameters +`number` **`id`** + + +
## Types +
+ ## Timer The Job type describes a Hilbish timer. -### Properties -- `type`: What type of timer it is -- `running`: If the timer is running -- `duration`: The duration in milliseconds that the timer will run +## Object properties +||| +|----|----| +|type|What type of timer it is| +|running|If the timer is running| +|duration|The duration in milliseconds that the timer will run| + ### Methods #### start() diff --git a/docs/api/hilbish/hilbish.userDir.md b/docs/api/hilbish/hilbish.userDir.md index 0b95057..a2b7337 100644 --- a/docs/api/hilbish/hilbish.userDir.md +++ b/docs/api/hilbish/hilbish.userDir.md @@ -1,5 +1,5 @@ --- -title: Interface hilbish.userDir +title: Module hilbish.userDir description: user-related directories layout: doc menu: @@ -12,7 +12,9 @@ This interface just contains properties to know about certain user directories. It is equivalent to XDG on Linux and gets the user's preferred directories for configs and data. -## Interface fields -- `config`: The user's config directory -- `data`: The user's directory for program data +## Static module fields +||| +|----|----| +|config|The user's config directory| +|data|The user's directory for program data| diff --git a/docs/api/terminal.md b/docs/api/terminal.md index 99d4b49..1bd4cc1 100644 --- a/docs/api/terminal.md +++ b/docs/api/terminal.md @@ -11,16 +11,71 @@ menu: The terminal library is a simple and lower level library for certain terminal interactions. ## Functions -### restoreState() -Restores the last saved state of the terminal +||| +|----|----| +|restoreState()|Restores the last saved state of the terminal| +|saveState()|Saves the current state of the terminal.| +|setRaw()|Puts the terminal into raw mode.| +|size()|Gets the dimensions of the terminal. Returns a table with `width` and `height`| -### saveState() -Saves the current state of the terminal +
+
+

+terminal.restoreState() + + + +

-### setRaw() -Puts the terminal in raw mode +Restores the last saved state of the terminal -### size() -Gets the dimensions of the terminal. Returns a table with `width` and `height` -Note: this is not the size in relation to the dimensions of the display +#### Parameters +This function has no parameters. +
+ +
+
+

+terminal.saveState() + + + +

+ +Saves the current state of the terminal. + +#### Parameters +This function has no parameters. +
+ +
+
+

+terminal.setRaw() + + + +

+ +Puts the terminal into raw mode. + +#### Parameters +This function has no parameters. +
+ +
+
+

+terminal.size() + + + +

+ +Gets the dimensions of the terminal. Returns a table with `width` and `height` +NOTE: The size refers to the amount of columns and rows of text that can fit in the terminal. + +#### Parameters +This function has no parameters. +
diff --git a/docs/completions.md b/docs/completions.md index c2de27a..59ead1b 100644 --- a/docs/completions.md +++ b/docs/completions.md @@ -1,56 +1,78 @@ -Hilbish has a pretty good completion system. It has a nice looking -menu, with 2 types of menus: grid (like file completions) or -list. +--- +title: Completions +description: Tab completion for commands. +layout: doc +menu: + docs: + parent: "Features" +--- +Completions for commands can be created with the [`hilbish.complete`](../api/hilbish#complete) +function. See the link for how to use it. + +To create completions for a command is simple. +The callback will be passed 3 parameters: +- `query` (string): The text that the user is currently trying to complete. +This should be used to match entries. +- `ctx` (string): Contains the entire line. Use this if +more text is needed to be parsed for context. +- `fields` (string): The `ctx` split up by spaces. + +In most cases, the completer just uses `fields` to check the amount +and `query` on what to match entries on. + +In order to return your results, it has to go within a "completion group." +Then you return a table of completion groups and a prefix. The prefix will +usually just be the `query`. + +Hilbish allows one to mix completion menus of different types, so +a grid menu and a list menu can be used and complete and display at the same time. +A completion group is a table with these keys: +- `type` (string): type of completion menu, either `grid` or `list`. +- `items` (table): a list of items. + +The requirements of the `items` table is different based on the +`type`. If it is a `grid`, it can simply be a table of strings. + +Otherwise if it is a `list` then each entry can +either be a string or a table. +Example: +```lua +local cg = { + items = { + 'list item 1', + ['--command-flag-here'] = {'this does a thing', '--the-flag-alias'} + }, + type = 'list' +} +local cg2 = { + items = {'just', 'a bunch', 'of items', 'here', 'hehe'}, + type = 'grid' +} + +return {cg, cg2}, prefix +``` + +Which looks like this: +{{< video src="https://safe.saya.moe/t4CiLK6dgPbD.mp4" >}} + +# Completion Handler Like most parts of Hilbish, it's made to be extensible and customizable. The default handler for completions in general can be overwritten to provide more advanced completions if needed. +This usually doesn't need to be done though, unless you know +what you're doing. -# Completion Handler -By default, it provides 3 things: for the first argument, +The default completion handler provides 3 things: binaries (with a plain name requested to complete, those in -$PATH), files, or command completions. With the default -completion handler, it will try to run a handler for the -command or fallback to file completions. +$PATH), files, or command completions. It will try to run a handler +for the command or fallback to file completions. -To overwrite it, just assign a function to -`hilbish.completion.handler` like so: +To overwrite it, just assign a function to `hilbish.completion.handler` like so: +```lua +-- line is the entire line as a string +-- pos is the position of the cursor. function hilbish.completion.handler(line, pos) -- do things end - -It is passed 2 arguments, the entire line, and the current -cursor position. The functions in the completion interface -take 3 arguments: query, ctx, and fields. - -- The `query`, which what the user is currently trying to complete -- `ctx`, being just the entire line -- `fields` being a table of arguments. It's just `ctx` split up, -delimited by spaces. - -It's expected to return 2 things: a table of completion groups, and -a prefix. A completion group is defined as a table with 2 keys: -`items` and `type`. - -- The `items` field is just a table of items to use for completions. -- The `type` is for the completion menu type, being either `grid` or -`list`. - -The prefix is what all the completions start with. It should be empty -if the user doesn't have a query. If the beginning of the completion -item does not match the prefix, it will be replaced and fixed -properly in the line. It is case sensitive. - -If you want to overwrite the functionality of the general completion -handler, or make your command completion have files as well -(and filter them), then there is the `files` function, which is -mentioned below. - -# Completion Interface -## Functions -- `files(query, ctx, fields)` -> table, prefix: get file completions, -based on the user's query. -- `bins(query, ctx, fields)` -> table, prefix: get binary/executable -completions, based on user query. -- `call(scope, query, ctx, fields)` -> table, prefix: call a completion -handler with `scope`, usually being in the form of `command.` +``` diff --git a/website/content/docs/faq.md b/docs/faq.md similarity index 74% rename from website/content/docs/faq.md rename to docs/faq.md index 997fbaa..f89f269 100644 --- a/website/content/docs/faq.md +++ b/docs/faq.md @@ -15,11 +15,12 @@ It compiles for Windows (CI ensures it does), but otherwise it is not directly supported. If you'd like to improve this situation, checkout [the discussion](https://github.com/Rosettea/Hilbish/discussions/165). -# Where is the API documentation? -The builtin `doc` command supplies all documentation of Hilbish provided -APIs. You can also check the sidebar. - # Why? Hilbish emerged from the desire of a Lua configured shell. It was the initial reason that it was created, but now it's more: to be hyper extensible, simpler and more user friendly. + +# Does it have "autocompletion" or "tab completion" +Of course! This is a modern shell. Hilbish provides a way for users +to write tab completion for any command and/or the whole shell. +Inline hinting and syntax highlighting are also available. diff --git a/website/content/docs/features/_index.md b/docs/features/_index.md similarity index 100% rename from website/content/docs/features/_index.md rename to docs/features/_index.md diff --git a/website/content/docs/features/notifications.md b/docs/features/notifications.md similarity index 100% rename from website/content/docs/features/notifications.md rename to docs/features/notifications.md diff --git a/docs/features/opts.md b/docs/features/opts.md new file mode 100644 index 0000000..2fb848d --- /dev/null +++ b/docs/features/opts.md @@ -0,0 +1,78 @@ +--- +title: Options +description: Simple customizable options. +layout: doc +menu: + docs: + parent: "Features" +--- + +Opts are simple toggle or value options a user can set in Hilbish. +As toggles, there are things like `autocd` or history saving. As values, +there is the `motd` which the user can either change to a custom string or disable. + +Opts are accessed from the `hilbish.opts` table. Here they can either +be read or modified + +### `autocd` +#### Value: `boolean` +#### Default: `false` + +The autocd opt makes it so that lone directories attempted to be executed are +instead set as the shell's directory. + +Example: +``` +~/Directory +∆ ~ +~ +∆ Downloads +~/Downloads +∆ ../Documents +~/Documents +∆ +``` + +
+ +### `history` +#### Value: `boolean` +#### Default: `true` +Sets whether command history will be saved or not. + +
+ +### `greeting` +#### Value: `boolean` or `string` +The greeting is the message that Hilbish shows on startup +(the one which says Welcome to Hilbish). + +This can be set to either true/false to enable/disable or a custom greeting string. + +
+ +### `motd` +#### Value: `boolean` +#### Default: `true` +The message of the day shows the current major.minor version and +includes a small range of things added in the current release. + +This can be set to `false` to disable the message. + +
+ +### `fuzzy` +#### Value: `boolean` +#### Default: `false` +Toggles the functionality of fuzzy history searching, usable +via the menu in Ctrl-R. Fuzzy searching is an approximate searching +method, which means results that match *closest* will be shown instead +of an exact match. + +
+ +### `notifyJobFinish` +#### Value: `boolean` +#### Default: `true` +If this is enabled, when a background job is finished, +a [notification](../notifications) will be sent. diff --git a/docs/runner-mode.md b/docs/features/runner-mode.md similarity index 50% rename from docs/runner-mode.md rename to docs/features/runner-mode.md index 0b5ce24..0f7a8dd 100644 --- a/docs/runner-mode.md +++ b/docs/features/runner-mode.md @@ -1,7 +1,24 @@ -Hilbish is *unique,* when interactive it first attempts to run input as -Lua and then tries shell script. But if you're normal, you wouldn't -really be using Hilbish anyway but you'd also not want this -(or maybe want Lua only in some cases.) +--- +title: Runner Mode +description: Customize the interactive script/command runner. +layout: doc +menu: + docs: + parent: "Features" +--- + +Hilbish allows you to change how interactive text can be interpreted. +This is mainly due to the fact that the default method Hilbish uses +is that it runs Lua first and then falls back to shell script. + +In some cases, someone might want to switch to just shell script to avoid +it while interactive but still have a Lua config, or go full Lua to use +Hilbish as a REPL. This also allows users to add alternative languages like +Fennel as the interactive script runner. + +Runner mode can also be used to handle specific kinds of input before +evaluating like normal, which is how [Link.hsh](https://github.com/TorchedSammy/Link.hsh) +handles links. The "runner mode" of Hilbish is customizable via `hilbish.runnerMode`, which determines how Hilbish will run user input. By default, this is @@ -11,28 +28,23 @@ set it to `hybridRev` and for isolated modes there is `sh` and `lua` respectively. You can also set it to a function, which will be called everytime Hilbish -needs to run interactive input. For example, you can set this to a simple -function to compile and evaluate Fennel, and now you can run Fennel. -You can even mix it with sh to make a hybrid mode with Lua replaced by -Fennel. - -An example: -hilbish.runnerMode(function(input) - local ok = pcall(fennel.eval, input) - if ok then - return input, 0, nil - end - - return hilbish.runner.sh(input) -end) +needs to run interactive input. For more detail, see the [API documentation](../../api/hilbish/hilbish.runner) The `hilbish.runner` interface is an alternative to using `hilbish.runnerMode` -and also provides the sh and Lua runner functions that Hilbish itself uses. -A runner function is expected to return 3 values: the input, exit code, and an error. -The input return is there incase you need to prompt for more input. -If you don't, just return the input passed to the runner function. -The exit code has to be a number, it will be 0 otherwise and the error can be -`nil` to indicate no error. +and also provides the shell script and Lua runner functions that Hilbish itself uses. + +A runner function is expected to return a table with the following values: +- `exitCode` (number): Exit code of the command +- `input` (string): The text input of the user. This is used by Hilbish to append extra input, in case +more is requested. +- `err` (string): A string that represents an error from the runner. +This should only be set when, for example, there is a syntax error. +It can be set to a few special values for Hilbish to throw the right +hooks and have a better looking message. + - `: not-found` will throw a `command.not-found` hook + based on what `` is. + - `: not-executable` will throw a `command.not-executable` hook. +- `continue` (boolean): Whether Hilbish should prompt the user for no input ## Functions These are the "low level" functions for the `hilbish.runner` interface. @@ -41,21 +53,6 @@ These are the "low level" functions for the `hilbish.runner` interface. + sh(input) -> table > Runs `input` in Hilbish's sh interpreter + lua(input) -> table > Evals `input` as Lua code -The table value that runners return can have at least 4 values: -+ input (string): The full input text. -+ exitCode (number): Exit code (usually from a command) -+ continue (boolean): Whether to prompt the user for more input -(in the case of incomplete syntax) -+ err (string): A string that represents an error from the runner. -This should only be set when, for example, there is a syntax error. -It can be set to a few special values for Hilbish to throw the right -hooks and have a better looking message. - -+ `: not-found` will throw a `command.not-found` hook -based on what `` is. -+ `: not-executable` will throw a `command.not-executable` hook. - -The others here are defined in Lua and have EmmyLua documentation. These functions should be preferred over the previous ones. + setCurrent(mode) > The same as `setMode`, but works with runners managed via the functions below. diff --git a/website/content/docs/getting-started.md b/docs/getting-started.md similarity index 100% rename from website/content/docs/getting-started.md rename to docs/getting-started.md diff --git a/docs/hooks/_index.md b/docs/hooks/_index.md index 6616b05..ec313c6 100644 --- a/docs/hooks/_index.md +++ b/docs/hooks/_index.md @@ -1,13 +1,11 @@ -Here is a list of bait hooks that are thrown by Hilbish. If a hook is related -to a command, it will have the `command` scope, as example. +--- +title: Signals +description: +layout: doc +weight: -50 +menu: + docs +--- -Here is the format for a doc for a hook: -+ -> > - -`` just means the arguments of the hook. If a hook doc has the format -of `arg...`, it means the hook can take/recieve any number of `arg`. - -+ error -> eventName, handler, err > Emitted when there is an error in -an event handler. The `eventName` is the name of the event the handler -is for, the `handler` is the callback function, and `err` is the error -message. +Signals are global events emitted with the [Bait](../api/bait) module. +For more detail on how to use these signals, you may check the Bait page. diff --git a/docs/hooks/command.md b/docs/hooks/command.md index 2d29f4b..2e3163e 100644 --- a/docs/hooks/command.md +++ b/docs/hooks/command.md @@ -1,12 +1,67 @@ -+ `command.preexec` -> input, cmdStr > Thrown before a command -is executed. The `input` is the user written command, while `cmdStr` -is what will be executed (`input` will have aliases while `cmdStr` -will have alias resolved input). +--- +title: Command +description: +layout: doc +menu: + docs: + parent: "Signals" +--- -+ `command.exit` -> code, cmdStr > Thrown when a command exits. -`code` is the exit code of the command, and `cmdStr` is the command that was run. +## command.preexec +Thrown right before a command is executed. -+ `command.not-found` -> cmdStr > Thrown when a command is not found. +#### Variables +`string` **`input`** +The raw string that the user typed. This will include the text +without changes applied to it (argument substitution, alias expansion, +etc.) -+ `command.not-executable` -> cmdStr > Thrown when Hilbish attempts to run a file -that is not executable. +`string` **`cmdStr`** +The command that will be directly executed by the current runner. + +
+ +## command.exit +Thrown after the user's ran command is finished. + +#### Variables +`number` **`code`** +The exit code of what was executed. + +`string` **`cmdStr`** +The command or code that was executed + +
+ +## command.not-found +Thrown if the command attempted to execute was not found. +This can be used to customize the text printed when a command is not found. +Example: +```lua +local bait = require 'bait' +-- Remove any present handlers on `command.not-found` + +local notFoundHooks = bait.hooks 'command.not-found' +for _, hook in ipairs(notFoundHooks) do + bait.release('command.not-found', hook) +end + +-- then assign custom +bait.catch('command.not-found', function(cmd) + print(string.format('The command "%s" was not found.', cmd)) +end) +``` + +#### Variables +`string` **`cmdStr`** +The name of the command. + +
+ +## command.not-executable +Thrown when the user attempts to run a file that is not executable +(like a text file, or Unix binary without +x permission). + +#### Variables +`string` **`cmdStr`** +The name of the command. diff --git a/docs/hooks/hilbish.md b/docs/hooks/hilbish.md index 7118901..d5d8a48 100644 --- a/docs/hooks/hilbish.md +++ b/docs/hooks/hilbish.md @@ -1,12 +1,47 @@ -+ `hilbish.exit` > Sent when Hilbish is about to exit. +--- +title: Hilbish +description: +layout: doc +menu: + docs: + parent: "Signals" +--- -+ `hilbish.vimMode` -> modeName > Sent when Hilbish's Vim mode is changed (example insert to normal mode), -`modeName` is the name of the mode changed to (can be `insert`, `normal`, `delete` or `replace`). +## hilbish.exit +Sent when Hilbish is going to exit. + +#### Variables +This signal returns no variables. + +
+ +## hilbish.vimMode +Sent when the Vim mode of Hilbish is changed (like from insert to normal mode). +This can be used to change the prompt and notify based on Vim mode. + +#### Variables +`string` **`modeName`** +The mode that has been set. +Can be these values: `insert`, `normal`, `delete` or `replace` + +
+ +## hilbish.cancel +Sent when the user cancels their command input with Ctrl-C + +#### Variables +This signal returns no variables. + +
+ +## hilbish.notification +Thrown when a [notification](../../features/notifications) is sent. + +#### Variables +`table` **`notification`** +The notification. The properties are defined in the link above. + +
+ `hilbish.vimAction` -> actionName, args > Sent when the user does a "vim action," being something like yanking or pasting text. See `doc vim-mode actions` for more info. - -+ `hilbish.cancel` > Sent when the user cancels their input with Ctrl-C. - -+ `hilbish.notification` -> message > Sent when a message is -sent. diff --git a/docs/hooks/signal.md b/docs/hooks/signal.md index ac5deed..63834b0 100644 --- a/docs/hooks/signal.md +++ b/docs/hooks/signal.md @@ -1,7 +1,40 @@ -+ `signal.sigint` > Sent when Hilbish receives SIGINT (on Ctrl-C). +--- +title: Signal +description: +layout: doc +menu: + docs: + parent: "Signals" +--- -+ `signal.resize` > Sent when the terminal is resized. +## signal.sigint +Thrown when Hilbish receive the SIGINT signal, +aka when Ctrl-C is pressed. -+ `signal.sigusr1` +#### Variables +This signal returns no variables. + +
+ +## signal.resize +Thrown when the terminal is resized. + +#### Variables +This signal returns no variables. + +
+ +## signal.sigusr1 +Thrown when SIGUSR1 is sent to Hilbish. + +#### Variables +This signal returns no variables. + +
+ +## signal.sigusr2 +Thrown when SIGUSR2 is sent to Hilbish. + +#### Variables +This signal returns no variables. -+ `signal.sigusr2` diff --git a/docs/jobs.md b/docs/jobs.md index a5fee9c..8651051 100644 --- a/docs/jobs.md +++ b/docs/jobs.md @@ -1,3 +1,5 @@ +(This has mainly been replaced by [hilbish.jobs](../api/hilbish.jobs)). + Hilbish has pretty standard job control. It's missing one or two things, but works well. One thing which is different from other shells (besides Hilbish) itself is the API for jobs, and of course it's in Lua. diff --git a/docs/lunacolors.md b/docs/lunacolors.md index e122fef..bde809c 100644 --- a/docs/lunacolors.md +++ b/docs/lunacolors.md @@ -1,3 +1,10 @@ +--- +title: Lunacolors +layout: doc +weight: -60 +menu: docs +--- + Lunacolors is an ANSI color/styling library for Lua. It is included by default in standard Hilbish distributions to provide easy styling for things like prompts and text. diff --git a/docs/nature/_index.md b/docs/nature/_index.md index 53df6ca..4a3bc35 100644 --- a/docs/nature/_index.md +++ b/docs/nature/_index.md @@ -1,10 +1,17 @@ +--- +title: Nature +layout: doc +weight: -90 +menu: docs +--- + A bit after creation, we have the outside nature. Little plants, seeds, growing to their final phase: a full plant. A lot of Hilbish itself is written in Go, but there are parts made in Lua, being most builtins (`doc`, `cd`, cdr), completions, and other things. -Hilbish's Lua core module is called `nature`. It's handled after everything -on the Go side initializes, which is what that first sentence was from. +Hilbish's Lua core module is called `nature`. +It runs after Hilbish's Go core does. # Nature Modules Currently, `nature` provides 1 intended public module: `nature.dirs`. diff --git a/docs/nature/dirs.md b/docs/nature/dirs.md new file mode 100644 index 0000000..3c707e6 --- /dev/null +++ b/docs/nature/dirs.md @@ -0,0 +1,79 @@ +--- +title: Module dirs +description: No description. +layout: doc +menu: + docs: + parent: "Nature" +--- + +
+
+

+dirs.setOld(d) + + + +

+ +Sets the old directory string. +#### Parameters +`d` **`string`** +
+ +
+
+

+dirs.push() + + + +

+ +Add `d` to the recent directories list. +#### Parameters +This function has no parameters. +
+ +
+
+

+dirs.peak(num) + + + +

+ +Look at `num` amount of recent directories, starting from the latest. +#### Parameters +`num` **`number`** +
+ +
+
+

+dirs.pop(num) + + + +

+ +Remove the specified amount of dirs from the recent directories list. +#### Parameters +`num` **`number`** +
+ +
+
+

+dirs.recent(idx) + + + +

+ +Get entry from recent directories list based on index. +#### Parameters +`idx` **`number`** +
+ diff --git a/docs/vim-mode/_index.md b/docs/vim-mode/_index.md index a30fe74..bda01e9 100644 --- a/docs/vim-mode/_index.md +++ b/docs/vim-mode/_index.md @@ -1,3 +1,10 @@ +--- +title: Vim Mode +layout: doc +weight: -90 +menu: docs +--- + Hilbish has a Vim binding input mode accessible for use. It can be enabled with the `hilbish.inputMode` function (check `doc hilbish`). diff --git a/docs/vim-mode/actions.md b/docs/vim-mode/actions.md index 9dfb7b2..9757827 100644 --- a/docs/vim-mode/actions.md +++ b/docs/vim-mode/actions.md @@ -1,3 +1,12 @@ +--- +title: Actions +layout: doc +weight: -80 +menu: + docs: + parent: "Vim Mode" +--- + Vim actions are essentially just when a user uses a Vim keybind. Things like yanking and pasting are Vim actions. This is not an "offical Vim thing," just a Hilbish thing. diff --git a/editor.go b/editor.go index d720a41..9c49440 100644 --- a/editor.go +++ b/editor.go @@ -27,7 +27,8 @@ func editorLoader(rtm *rt.Runtime) *rt.Table { // #interface editor // insert(text) -// Inserts text into the line. +// Inserts text into the Hilbish command line. +// #param text string func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -46,8 +47,8 @@ func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #interface editor // setVimRegister(register, text) // Sets the vim register at `register` to hold the passed text. -// --- @param register string -// --- @param text string +// #aram register string +// #param text string func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -71,7 +72,7 @@ func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #interface editor // getVimRegister(register) -> string // Returns the text that is at the register. -// --- @param register string +// #param register string func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -90,6 +91,7 @@ func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #interface editor // getLine() -> string // Returns the current input line. +// #returns string func editorGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { buf := lr.rl.GetLine() @@ -98,8 +100,7 @@ func editorGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #interface editor // getChar() -> string -// Reads a keystroke from the user. This is in a format -// of something like Ctrl-L.. +// Reads a keystroke from the user. This is in a format of something like Ctrl-L. func editorReadChar(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { buf := lr.rl.ReadChar() diff --git a/emmyLuaDocs/bait.lua b/emmyLuaDocs/bait.lua index 35a37ed..c38eea1 100644 --- a/emmyLuaDocs/bait.lua +++ b/emmyLuaDocs/bait.lua @@ -2,31 +2,27 @@ local bait = {} ---- Catches a hook with `name`. Runs the `cb` when it is thrown ---- @param name string ---- @param cb function +--- Catches an event. This function can be used to act on events. +--- +--- function bait.catch(name, cb) end ---- Same as catch, but only runs the `cb` once and then removes the hook ---- @param name string ---- @param cb function +--- Catches an event, but only once. This will remove the hook immediately after it runs for the first time. function bait.catchOnce(name, cb) end ---- Returns a table with hooks (callback functions) on the event with `name`. ---- @param name string ---- @returns table +--- Returns a table of functions that are hooked on an event with the corresponding `name`. function bait.hooks(name) end --- Removes the `catcher` for the event with `name`. --- For this to work, `catcher` has to be the same function used to catch --- an event, like one saved to a variable. ---- @param name string ---- @param catcher function +--- +--- function bait.release(name, catcher) end ---- Throws a hook with `name` with the provided `args` ---- @param name string ---- @vararg any +--- Throws a hook with `name` with the provided `args`. +--- +--- function bait.throw(name, ...args) end return bait diff --git a/emmyLuaDocs/commander.lua b/emmyLuaDocs/commander.lua index c6738dd..285c4b5 100644 --- a/emmyLuaDocs/commander.lua +++ b/emmyLuaDocs/commander.lua @@ -2,13 +2,13 @@ local commander = {} ---- Deregisters any command registered with `name` ---- @param name string +--- Removes the named command. Note that this will only remove Commander-registered commands. function commander.deregister(name) end ---- Register a command with `name` that runs `cb` when ran ---- @param name string ---- @param cb function +--- Adds a new command with the given `name`. When Hilbish has to run a command with a name, +--- it will run the function providing the arguments and sinks. +--- +--- function commander.register(name, cb) end return commander diff --git a/emmyLuaDocs/fs.lua b/emmyLuaDocs/fs.lua index e974ab9..89a418b 100644 --- a/emmyLuaDocs/fs.lua +++ b/emmyLuaDocs/fs.lua @@ -2,56 +2,49 @@ local fs = {} ---- Gives an absolute version of `path`. ---- @param path string ---- @returns string +--- Returns an absolute version of the `path`. +--- This can be used to resolve short paths like `..` to `/home/user`. function fs.abs(path) end ---- Gives the basename of `path`. For the rules, ---- see Go's filepath.Base ---- @returns string +--- Returns the "basename," or the last part of the provided `path`. If path is empty, +--- `.` will be returned. function fs.basename(path) end ---- Changes directory to `dir` ---- @param dir string +--- Changes Hilbish's directory to `dir`. function fs.cd(dir) end ---- Returns the directory part of `path`. For the rules, see Go's ---- filepath.Dir ---- @param path string ---- @returns string +--- Returns the directory part of `path`. If a file path like +--- `~/Documents/doc.txt` then this function will return `~/Documents`. function fs.dir(path) end ---- Glob all files and directories that match the pattern. ---- For the rules, see Go's filepath.Glob ---- @param pattern string ---- @returns table +--- Match all files based on the provided `pattern`. +--- For the syntax' refer to Go's filepath.Match function: https://pkg.go.dev/path/filepath#Match +--- +--- function fs.glob(pattern) end ---- Takes paths and joins them together with the OS's ---- directory separator (forward or backward slash). ---- @vararg string ---- @returns string -function fs.join(...) end +--- Takes any list of paths and joins them based on the operating system's path separator. +--- +--- +function fs.join(...path) end ---- Makes a directory called `name`. If `recursive` is true, it will create its parent directories. ---- @param name string ---- @param recursive boolean +--- Creates a new directory with the provided `name`. +--- With `recursive`, mkdir will create parent directories. +--- +--- function fs.mkdir(name, recursive) end ---- Returns a table of files in `dir`. ---- @param dir string ---- @return table -function fs.readdir(dir) end +--- Returns a list of all files and directories in the provided path. +function fs.readdir(path) end ---- Returns a table of info about the `path`. ---- It contains the following keys: +--- Returns the information about a given `path`. +--- The returned table contains the following values: --- name (string) - Name of the path ---- size (number) - Size of the path ---- mode (string) - Permission mode in an octal format string (with leading 0) +--- size (number) - Size of the path in bytes +--- mode (string) - Unix permission mode in an octal format string (with leading 0) --- isDir (boolean) - If the path is a directory ---- @param path string ---- @returns table +--- +--- function fs.stat(path) end return fs diff --git a/emmyLuaDocs/hilbish.lua b/emmyLuaDocs/hilbish.lua index 0d38c61..7cca355 100644 --- a/emmyLuaDocs/hilbish.lua +++ b/emmyLuaDocs/hilbish.lua @@ -2,96 +2,92 @@ local hilbish = {} ---- This is an alias (ha) for the `hilbish.alias` function. +--- This is an alias (ha) for the [hilbish.alias](../#alias) function. --- @param alias string --- @param cmd string function hilbish.aliases.add(alias, cmd) end ---- This is the same as the `hilbish.runnerMode` function. It takes a callback, ---- which will be used to execute all interactive input. +--- This is the same as the `hilbish.runnerMode` function. +--- It takes a callback, which will be used to execute all interactive input. --- In normal cases, neither callbacks should be overrided by the user, --- as the higher level functions listed below this will handle it. ---- @param cb function function hilbish.runner.setMode(cb) end ---- Calls a completer function. This is mainly used to call ---- a command completer, which will have a `name` in the form ---- of `command.name`, example: `command.git`. ---- You can check `doc completions` for info on the `completionGroups` return value. ---- @param name string ---- @param query string ---- @param ctx string ---- @param fields table -function hilbish.completion.call(name, query, ctx, fields) end - ---- The handler function is the callback for tab completion in Hilbish. ---- You can check the completions doc for more info. ---- @param line string ---- @param pos string -function hilbish.completion.handler(line, pos) end - --- Returns the current input line. function hilbish.editor.getLine() end --- Returns the text that is at the register. ---- @param register string function hilbish.editor.getVimRegister(register) end ---- Inserts text into the line. +--- Inserts text into the Hilbish command line. function hilbish.editor.insert(text) end ---- Reads a keystroke from the user. This is in a format ---- of something like Ctrl-L.. +--- Reads a keystroke from the user. This is in a format of something like Ctrl-L. function hilbish.editor.getChar() end --- Sets the vim register at `register` to hold the passed text. ---- @param register string ---- @param text string function hilbish.editor.setVimRegister(register, text) end ---- Sets an alias of `cmd` to `orig` ---- @param cmd string ---- @param orig string +--- Return binaries/executables based on the provided parameters. +--- This function is meant to be used as a helper in a command completion handler. +--- +--- +function hilbish.completion.bins(query, ctx, fields) end + +--- Calls a completer function. This is mainly used to call a command completer, which will have a `name` +--- in the form of `command.name`, example: `command.git`. +--- You can check the Completions doc or `doc completions` for info on the `completionGroups` return value. +function hilbish.completion.call(name, query, ctx, fields) end + +--- Returns file matches based on the provided parameters. +--- This function is meant to be used as a helper in a command completion handler. +function hilbish.completion.files(query, ctx, fields) end + +--- This function contains the general completion handler for Hilbish. This function handles +--- completion of everything, which includes calling other command handlers, binaries, and files. +--- This function can be overriden to supply a custom handler. Note that alias resolution is required to be done in this function. +--- +--- +function hilbish.completion.handler(line, pos) end + +--- Sets an alias, with a name of `cmd` to another command. +--- +--- function hilbish.alias(cmd, orig) end ---- Appends `dir` to $PATH ---- @param dir string|table +--- Appends the provided dir to the command path (`$PATH`) +--- +--- function hilbish.appendPath(dir) end ---- Registers a completion handler for `scope`. ---- A `scope` is currently only expected to be `command.`, +--- Registers a completion handler for the specified scope. +--- A `scope` is expected to be `command.`, --- replacing with the name of the command (for example `command.git`). ---- `cb` must be a function that returns a table of "completion groups." ---- Check `doc completions` for more information. ---- @param scope string ---- @param cb function +--- The documentation for completions, under Features/Completions or `doc completions` +--- provides more details. +--- +--- function hilbish.complete(scope, cb) end ---- Returns the current directory of the shell ---- @returns string +--- Returns the current directory of the shell. function hilbish.cwd() end ---- Replaces running hilbish with `cmd` ---- @param cmd string +--- Replaces the currently running Hilbish instance with the supplied command. +--- This can be used to do an in-place restart. function hilbish.exec(cmd) end ---- Puts `fn` in a goroutine ---- @param fn function +--- Puts `fn` in a Goroutine. +--- This can be used to run any function in another thread at the same time as other Lua code. +--- **NOTE: THIS FUNCTION MAY CRASH HILBISH IF OUTSIDE VARIABLES ARE ACCESSED.** +--- **This is a limitation of the Lua runtime.** function hilbish.goro(fn) end ---- Line highlighter handler. This is mainly for syntax highlighting, but in ---- reality could set the input of the prompt to *display* anything. The ---- callback is passed the current line and is expected to return a line that ---- will be used as the input display. +--- Line highlighter handler. +--- This is mainly for syntax highlighting, but in reality could set the input +--- of the prompt to *display* anything. The callback is passed the current line +--- and is expected to return a line that will be used as the input display. --- Note that to set a highlighter, one has to override this function. ---- Example: ---- ``` ---- function hilbish.highlighter(line) ---- return line:gsub('"%w+"', function(c) return lunacolors.green(c) end) ---- end ---- ``` ---- This code will highlight all double quoted strings in green. ---- @param line string +--- function hilbish.highlighter(line) end --- The command line hint handler. It gets called on every key insert to @@ -99,97 +95,71 @@ function hilbish.highlighter(line) end --- line and cursor position. It is expected to return a string which is used --- as the text for the hint. This is by default a shim. To set hints, --- override this function with your custom handler. ---- @param line string ---- @param pos number +--- +--- function hilbish.hinter(line, pos) end ---- Sets the input mode for Hilbish's line reader. Accepts either emacs or vim ---- @param mode string +--- Sets the input mode for Hilbish's line reader. +--- `emacs` is the default. Setting it to `vim` changes behavior of input to be +--- Vim-like with modes and Vim keybinds. function hilbish.inputMode(mode) end ---- Runs the `cb` function every `time` milliseconds. ---- This creates a timer that starts immediately. ---- @param cb function ---- @param time number ---- @return Timer +--- Runs the `cb` function every specified amount of `time`. +--- This creates a timer that ticking immediately. function hilbish.interval(cb, time) end ---- Changes the continued line prompt to `str` ---- @param str string +--- Changes the text prompt when Hilbish asks for more input. +--- This will show up when text is incomplete, like a missing quote +--- +--- function hilbish.multiprompt(str) end ---- Prepends `dir` to $PATH ---- @param dir string +--- Prepends `dir` to $PATH. function hilbish.prependPath(dir) end ---- Changes the shell prompt to `str` +--- Changes the shell prompt to the provided string. --- There are a few verbs that can be used in the prompt text. --- These will be formatted and replaced with the appropriate values. --- `%d` - Current working directory --- `%u` - Name of current user --- `%h` - Hostname of device ---- @param str string ---- @param typ? string Type of prompt, being left or right. Left by default. +--- function hilbish.prompt(str, typ) end --- Read input from the user, using Hilbish's line editor/input reader. --- This is a separate instance from the one Hilbish actually uses. ---- Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen) ---- @param prompt? string ---- @returns string|nil +--- Returns `input`, will be nil if Ctrl-D is pressed, or an error occurs. function hilbish.read(prompt) end ---- Runs `cmd` in Hilbish's sh interpreter. ---- If returnOut is true, the outputs of `cmd` will be returned as the 2nd and ---- 3rd values instead of being outputted to the terminal. ---- @param cmd string ---- @param returnOut boolean ---- @returns number, string, string +--- Runs `cmd` in Hilbish's shell script interpreter. function hilbish.run(cmd, returnOut) end ---- Sets the execution/runner mode for interactive Hilbish. This determines whether ---- Hilbish wll try to run input as Lua and/or sh or only do one of either. +--- Sets the execution/runner mode for interactive Hilbish. +--- This determines whether Hilbish wll try to run input as Lua +--- and/or sh or only do one of either. --- Accepted values for mode are hybrid (the default), hybridRev (sh first then Lua), --- sh, and lua. It also accepts a function, to which if it is passed one --- will call it to execute user input instead. ---- @param mode string|function +--- Read [about runner mode](../features/runner-mode) for more information. function hilbish.runnerMode(mode) end ---- Runs the `cb` function after `time` in milliseconds. ---- This creates a timer that starts immediately. ---- @param cb function ---- @param time number ---- @returns Timer +--- Executed the `cb` function after a period of `time`. +--- This creates a Timer that starts ticking immediately. function hilbish.timeout(cb, time) end --- Checks if `name` is a valid command. --- Will return the path of the binary, or a basename if it's a commander. ---- @param name string ---- @returns string function hilbish.which(name) end --- Puts a job in the background. This acts the same as initially running a job. function hilbish.jobs:background() end ---- Returns binary/executale completion candidates based on the provided query. ---- @param query string ---- @param ctx string ---- @param fields table -function hilbish.completion.bins(query, ctx, fields) end - ---- Returns file completion candidates based on the provided query. ---- @param query string ---- @param ctx string ---- @param fields table -function hilbish.completion.files(query, ctx, fields) end - --- Puts a job in the foreground. This will cause it to run like it was --- executed normally and wait for it to complete. function hilbish.jobs:foreground() end --- Evaluates `cmd` as Lua input. This is the same as using `dofile` --- or `load`, but is appropriated for the runner interface. ---- @param cmd string function hilbish.runner.lua(cmd) end --- Sets/toggles the option of automatically flushing output. @@ -226,7 +196,6 @@ function hilbish.module.load(path) end --- Runs a command in Hilbish's shell script interpreter. --- This is the equivalent of using `source`. ---- @param cmd string function hilbish.runner.sh(cmd) end --- Starts a timer. @@ -236,30 +205,26 @@ function hilbish.timers:start() end function hilbish.timers:stop() end --- Removes an alias. ---- @param name string function hilbish.aliases.delete(name) end --- Get a table of all aliases, with string keys as the alias and the value as the command. ---- @returns table +--- +--- function hilbish.aliases.list() end ---- Tries to resolve an alias to its command. ---- @param alias string ---- @returns string +--- Resolves an alias to its original command. Will thrown an error if the alias doesn't exist. function hilbish.aliases.resolve(alias) end ---- Adds a new job to the job table. Note that this does not immediately run it. ---- @param cmdstr string ---- @param args table ---- @param execPath string +--- Creates a new job. This function does not run the job. This function is intended to be +--- used by runners, but can also be used to create jobs via Lua. Commanders cannot be ran as jobs. +--- +--- function hilbish.jobs.add(cmdstr, args, execPath) end --- Returns a table of all job objects. ---- @returns table function hilbish.jobs.all() end ---- Disowns a job. This deletes it from the job table. ---- @param id number +--- Disowns a job. This simply deletes it from the list of jobs without stopping it. function hilbish.jobs.disown(id) end --- Get a job object via its ID. @@ -267,39 +232,28 @@ function hilbish.jobs.disown(id) end --- @returns Job function hilbish.jobs.get(id) end ---- Returns the last added job from the table. ---- @returns Job +--- Returns the last added job to the table. function hilbish.jobs.last() end --- Adds a command to the history. ---- @param cmd string function hilbish.history.add(cmd) end ---- Retrieves all history. ---- @returns table +--- Retrieves all history as a table. function hilbish.history.all() end --- Deletes all commands from the history. function hilbish.history.clear() end ---- Retrieves a command from the history based on the `idx`. ---- @param idx number -function hilbish.history.get(idx) end +--- Retrieves a command from the history based on the `index`. +function hilbish.history.get(index) end --- Returns the amount of commands in the history. ---- @returns number function hilbish.history.size() end ---- Creates a timer that runs based on the specified `time` in milliseconds. ---- The `type` can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT` ---- @param type number ---- @param time number ---- @param callback function +--- Creates a timer that runs based on the specified `time`. function hilbish.timers.create(type, time, callback) end --- Retrieves a timer via its ID. ---- @param id number ---- @returns Timer function hilbish.timers.get(id) end return hilbish diff --git a/emmyLuaDocs/terminal.lua b/emmyLuaDocs/terminal.lua index 2266ac6..ed0b9ef 100644 --- a/emmyLuaDocs/terminal.lua +++ b/emmyLuaDocs/terminal.lua @@ -5,14 +5,14 @@ local terminal = {} --- Restores the last saved state of the terminal function terminal.restoreState() end ---- Saves the current state of the terminal +--- Saves the current state of the terminal. function terminal.saveState() end ---- Puts the terminal in raw mode +--- Puts the terminal into raw mode. function terminal.setRaw() end --- Gets the dimensions of the terminal. Returns a table with `width` and `height` ---- Note: this is not the size in relation to the dimensions of the display +--- NOTE: The size refers to the amount of columns and rows of text that can fit in the terminal. function terminal.size() end return terminal diff --git a/go.mod b/go.mod index 1549f76..7bf5efd 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.17 require ( github.com/arnodel/golua v0.0.0-20220221163911-dfcf252b6f86 + github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504 github.com/blackfireio/osinfo v1.0.3 github.com/maxlandon/readline v0.1.0-beta.0.20211027085530-2b76cabb8036 github.com/pborman/getopt v1.1.0 diff --git a/go.sum b/go.sum index 7c36fa0..f82ef01 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/arnodel/edit v0.0.0-20220202110212-dfc8d7a13890/go.mod h1:AcpttpuZBaL github.com/arnodel/strftime v0.1.6 h1:0hc0pUvk8KhEMXE+htyaOUV42zNcf/csIbjzEFCJqsw= github.com/arnodel/strftime v0.1.6/go.mod h1:5NbK5XqYK8QpRZpqKNt4OlxLtIB8cotkLk4KTKzJfWs= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504 h1:R1/AOzdMbopSliUTTEHvHbyNmnZ3YxY5GvdhTkpPsSY= +github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504/go.mod h1:kHBCvAXJIatTX1pw6tLiOspjGc3MhUDRlog9yrCUS+k= github.com/blackfireio/osinfo v1.0.3 h1:Yk2t2GTPjBcESv6nDSWZKO87bGMQgO+Hi9OoXPpxX8c= github.com/blackfireio/osinfo v1.0.3/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= diff --git a/golibs/bait/bait.go b/golibs/bait/bait.go index 3f3c34e..1f85c76 100644 --- a/golibs/bait/bait.go +++ b/golibs/bait/bait.go @@ -1,9 +1,28 @@ // the event emitter -// Bait is the event emitter for Hilbish. Why name it bait? Why not. -// It throws hooks that you can catch. This is what you will use if -// you want to listen in on hooks to know when certain things have -// happened, like when you've changed directory, a command has failed, -// etc. To find all available hooks thrown by Hilbish, see doc hooks. +/* +Bait is the event emitter for Hilbish. Much like Node.js and +its `events` system, many actions in Hilbish emit events. +Unlike Node.js, Hilbish events are global. So make sure to +pick a unique name! + +Usage of the Bait module consists of userstanding +event-driven architecture, but it's pretty simple: +If you want to act on a certain event, you can `catch` it. +You can act on events via callback functions. + +Examples of this are in the Hilbish default config! +Consider this part of it: +```lua +bait.catch('command.exit', function(code) + running = false + doPrompt(code ~= 0) + doNotifyPrompt() +end) +``` + +What this does is, whenever the `command.exit` event is thrown, +this function will set the user prompt. +*/ package bait import ( @@ -228,31 +247,17 @@ func handleHook(t *rt.Thread, c *rt.GoCont, name string, catcher *rt.Closure, ar } } -// throw(name, ...args) -// Throws a hook with `name` with the provided `args` -// --- @param name string -// --- @vararg any -func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - if err := c.Check1Arg(); err != nil { - return nil, err - } - name, err := c.StringArg(0) - if err != nil { - return nil, err - } - ifaceSlice := make([]interface{}, len(c.Etc())) - for i, v := range c.Etc() { - ifaceSlice[i] = v - } - b.Emit(name, ifaceSlice...) - - return c.Next(), nil -} - // catch(name, cb) -// Catches a hook with `name`. Runs the `cb` when it is thrown -// --- @param name string -// --- @param cb function +// Catches an event. This function can be used to act on events. +// #param name string The name of the hook. +// #param cb function The function that will be called when the hook is thrown. +/* +#example +bait.catch('hilbish.exit', function() + print 'Goodbye Hilbish!' +end) +#example +*/ func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { name, catcher, err := util.HandleStrCallback(t, c) if err != nil { @@ -265,9 +270,9 @@ func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // catchOnce(name, cb) -// Same as catch, but only runs the `cb` once and then removes the hook -// --- @param name string -// --- @param cb function +// Catches an event, but only once. This will remove the hook immediately after it runs for the first time. +// #param name string The name of the event +// #param cb function The function that will be called when the event is thrown. func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { name, catcher, err := util.HandleStrCallback(t, c) if err != nil { @@ -279,27 +284,10 @@ func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), nil } -// release(name, catcher) -// Removes the `catcher` for the event with `name`. -// For this to work, `catcher` has to be the same function used to catch -// an event, like one saved to a variable. -// --- @param name string -// --- @param catcher function -func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - name, catcher, err := util.HandleStrCallback(t, c) - if err != nil { - return nil, err - } - - b.OffLua(name, catcher) - - return c.Next(), nil -} - // hooks(name) -> table -// Returns a table with hooks (callback functions) on the event with `name`. -// --- @param name string -// --- @returns table +// Returns a table of functions that are hooked on an event with the corresponding `name`. +// #param name string The name of the hook +// #returns table func (b *Bait) bhooks(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -327,3 +315,62 @@ func (b *Bait) bhooks(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.PushingNext1(t.Runtime, rt.TableValue(luaHandlers)), nil } + +// release(name, catcher) +// Removes the `catcher` for the event with `name`. +// For this to work, `catcher` has to be the same function used to catch +// an event, like one saved to a variable. +// #param name string Name of the event the hook is on +// #param catcher function Hook function to remove +/* +#example +local hookCallback = function() print 'hi' end + +bait.catch('event', hookCallback) + +-- a little while later.... +bait.release('event', hookCallback) +-- and now hookCallback will no longer be ran for the event. +#example +*/ +func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + name, catcher, err := util.HandleStrCallback(t, c) + if err != nil { + return nil, err + } + + b.OffLua(name, catcher) + + return c.Next(), nil +} + +// throw(name, ...args) +// #param name string The name of the hook. +// #param args ...any The arguments to pass to the hook. +// Throws a hook with `name` with the provided `args`. +/* +#example +bait.throw('greeting', 'world') + +-- This can then be listened to via +bait.catch('gretting', function(greetTo) + print('Hello ' .. greetTo) +end) +#example +*/ +func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + if err := c.Check1Arg(); err != nil { + return nil, err + } + name, err := c.StringArg(0) + if err != nil { + return nil, err + } + ifaceSlice := make([]interface{}, len(c.Etc())) + for i, v := range c.Etc() { + ifaceSlice[i] = v + } + b.Emit(name, ifaceSlice...) + + return c.Next(), nil +} diff --git a/golibs/commander/commander.go b/golibs/commander/commander.go index c639cf9..ea2da7a 100644 --- a/golibs/commander/commander.go +++ b/golibs/commander/commander.go @@ -1,10 +1,9 @@ // library for custom commands /* -Commander is a library for writing custom commands in Lua. -In order to make it easier to write commands for Hilbish, -not require separate scripts and to be able to use in a config, -the Commander library exists. This is like a very simple wrapper -that works with Hilbish for writing commands. Example: +Commander is the library which handles Hilbish commands. This makes +the user able to add Lua-written commands to their shell without making +a separate script in a bin folder. Instead, you may simply use the Commander +library in your Hilbish config. ```lua local commander = require 'commander' @@ -19,14 +18,14 @@ that will print `Hello world!` to output. One question you may have is: What is the `sinks` parameter? The `sinks` parameter is a table with 3 keys: `in`, `out`, -and `err`. The values of these is a @Sink. +and `err`. All of them are a @Sink. -- `in` is the standard input. You can read from this sink -to get user input. (**This is currently unimplemented.**) -- `out` is standard output. This is usually where text meant for -output should go. -- `err` is standard error. This sink is for writing errors, as the -name would suggest. +- `in` is the standard input. +You may use the read functions on this sink to get input from the user. +- `out` is standard output. +This is usually where command output should go. +- `err` is standard error. +This sink is for writing errors, as the name would suggest. */ package commander @@ -67,9 +66,22 @@ func (c *Commander) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { } // register(name, cb) -// Register a command with `name` that runs `cb` when ran -// --- @param name string -// --- @param cb function +// Adds a new command with the given `name`. When Hilbish has to run a command with a name, +// it will run the function providing the arguments and sinks. +// #param name string Name of the command +// #param cb function Callback to handle command invocation +/* +#example +-- When you run the command `hello` in the shell, it will print `Hello world`. +-- If you run it with, for example, `hello Hilbish`, it will print 'Hello Hilbish' +commander.register('hello', function(args, sinks) + local name = 'world' + if #args > 0 then name = args[1] end + + sinks.out:writeln('Hello ' .. name) +end) +#example +*/ func (c *Commander) cregister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) { cmdName, cmd, err := util.HandleStrCallback(t, ct) if err != nil { @@ -82,8 +94,8 @@ func (c *Commander) cregister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) { } // deregister(name) -// Deregisters any command registered with `name` -// --- @param name string +// Removes the named command. Note that this will only remove Commander-registered commands. +// #param name string Name of the command to remove. func (c *Commander) cderegister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) { if err := ct.Check1Arg(); err != nil { return nil, err diff --git a/golibs/fs/fs.go b/golibs/fs/fs.go index 1c1a5ca..5bd22c6 100644 --- a/golibs/fs/fs.go +++ b/golibs/fs/fs.go @@ -1,7 +1,10 @@ // filesystem interaction and functionality library -// The fs module provides easy and simple access to filesystem functions -// and other things, and acts an addition to the Lua standard library's -// I/O and filesystem functions. +/* +The fs module provides filesystem functions to Hilbish. While Lua's standard +library has some I/O functions, they're missing a lot of the basics. The `fs` +library offers more functions and will work on any operating system Hilbish does. +#field pathSep The operating system's path separator. +*/ package fs import ( @@ -42,9 +45,46 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { return rt.TableValue(mod), nil } +// abs(path) -> string +// Returns an absolute version of the `path`. +// This can be used to resolve short paths like `..` to `/home/user`. +// #param path string +// #returns string +func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + path, err := c.StringArg(0) + if err != nil { + return nil, err + } + path = util.ExpandHome(path) + + abspath, err := filepath.Abs(path) + if err != nil { + return nil, err + } + + return c.PushingNext1(t.Runtime, rt.StringValue(abspath)), nil +} + +// basename(path) -> string +// Returns the "basename," or the last part of the provided `path`. If path is empty, +// `.` will be returned. +// #param path string Path to get the base name of. +// #returns string +func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + if err := c.Check1Arg(); err != nil { + return nil, err + } + path, err := c.StringArg(0) + if err != nil { + return nil, err + } + + return c.PushingNext(t.Runtime, rt.StringValue(filepath.Base(path))), nil +} + // cd(dir) -// Changes directory to `dir` -// --- @param dir string +// Changes Hilbish's directory to `dir`. +// #param dir string Path to change directory to. func fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -63,10 +103,103 @@ func fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), err } +// dir(path) -> string +// Returns the directory part of `path`. If a file path like +// `~/Documents/doc.txt` then this function will return `~/Documents`. +// #param path string Path to get the directory for. +// #returns string +func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + if err := c.Check1Arg(); err != nil { + return nil, err + } + path, err := c.StringArg(0) + if err != nil { + return nil, err + } + + return c.PushingNext(t.Runtime, rt.StringValue(filepath.Dir(path))), nil +} + +// glob(pattern) -> matches (table) +// Match all files based on the provided `pattern`. +// For the syntax' refer to Go's filepath.Match function: https://pkg.go.dev/path/filepath#Match +// #param pattern string Pattern to compare files with. +// #returns table A list of file names/paths that match. +/* +#example +--[[ + Within a folder that contains the following files: + a.txt + init.lua + code.lua + doc.pdf +]]-- +local matches = fs.glob './*.lua' +print(matches) +-- -> {'init.lua', 'code.lua'} +#example +*/ +func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + if err := c.Check1Arg(); err != nil { + return nil, err + } + pattern, err := c.StringArg(0) + if err != nil { + return nil, err + } + + matches, err := filepath.Glob(pattern) + if err != nil { + return nil, err + } + + luaMatches := rt.NewTable() + + for i, match := range matches { + luaMatches.Set(rt.IntValue(int64(i + 1)), rt.StringValue(match)) + } + + return c.PushingNext(t.Runtime, rt.TableValue(luaMatches)), nil +} + +// join(...path) -> string +// Takes any list of paths and joins them based on the operating system's path separator. +// #param path ...string Paths to join together +// #returns string The joined path. +/* +#example +-- This prints the directory for Hilbish's config! +print(fs.join(hilbish.userDir.config, 'hilbish')) +-- -> '/home/user/.config/hilbish' on Linux +#example +*/ +func fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + strs := make([]string, len(c.Etc())) + for i, v := range c.Etc() { + if v.Type() != rt.StringType { + // +2; go indexes of 0 and first arg from above + return nil, fmt.Errorf("bad argument #%d to run (expected string, got %s)", i + 1, v.TypeName()) + } + strs[i] = v.AsString() + } + + res := filepath.Join(strs...) + + return c.PushingNext(t.Runtime, rt.StringValue(res)), nil +} + // mkdir(name, recursive) -// Makes a directory called `name`. If `recursive` is true, it will create its parent directories. -// --- @param name string -// --- @param recursive boolean +// Creates a new directory with the provided `name`. +// With `recursive`, mkdir will create parent directories. +// #param name string Name of the directory +// #param recursive boolean Whether to create parent directories for the provided name +/* +#example +-- This will create the directory foo, then create the directory bar in the +-- foo directory. If recursive is false in this case, it will fail. +fs.mkdir('./foo/bar', true) +#example +*/ func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.CheckNArgs(2); err != nil { return nil, err @@ -93,15 +226,58 @@ func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), err } +// readdir(path) -> table[string] +// Returns a list of all files and directories in the provided path. +// #param dir string +// #returns table +func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + if err := c.Check1Arg(); err != nil { + return nil, err + } + dir, err := c.StringArg(0) + if err != nil { + return nil, err + } + dir = util.ExpandHome(dir) + names := rt.NewTable() + + dirEntries, err := os.ReadDir(dir) + if err != nil { + return nil, err + } + for i, entry := range dirEntries { + names.Set(rt.IntValue(int64(i + 1)), rt.StringValue(entry.Name())) + } + + return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil +} + // stat(path) -> {} -// Returns a table of info about the `path`. -// It contains the following keys: +// Returns the information about a given `path`. +// The returned table contains the following values: // name (string) - Name of the path -// size (number) - Size of the path -// mode (string) - Permission mode in an octal format string (with leading 0) +// size (number) - Size of the path in bytes +// mode (string) - Unix permission mode in an octal format string (with leading 0) // isDir (boolean) - If the path is a directory -// --- @param path string -// --- @returns table +// #param path string +// #returns table +/* +#example +local inspect = require 'inspect' + +local stat = fs.stat '~' +print(inspect(stat)) +--[[ +Would print the following: +{ + isDir = true, + mode = "0755", + name = "username", + size = 12288 +} +]]-- +#example +*/ func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -125,128 +301,3 @@ func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.PushingNext1(t.Runtime, rt.TableValue(statTbl)), nil } -// readdir(dir) -> {} -// Returns a table of files in `dir`. -// --- @param dir string -// --- @return table -func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - if err := c.Check1Arg(); err != nil { - return nil, err - } - dir, err := c.StringArg(0) - if err != nil { - return nil, err - } - dir = util.ExpandHome(dir) - names := rt.NewTable() - - dirEntries, err := os.ReadDir(dir) - if err != nil { - return nil, err - } - for i, entry := range dirEntries { - names.Set(rt.IntValue(int64(i + 1)), rt.StringValue(entry.Name())) - } - - return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil -} - -// abs(path) -> string -// Gives an absolute version of `path`. -// --- @param path string -// --- @returns string -func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - path, err := c.StringArg(0) - if err != nil { - return nil, err - } - path = util.ExpandHome(path) - - abspath, err := filepath.Abs(path) - if err != nil { - return nil, err - } - - return c.PushingNext1(t.Runtime, rt.StringValue(abspath)), nil -} - -// basename(path) -> string -// Gives the basename of `path`. For the rules, -// see Go's filepath.Base -// --- @returns string -func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - if err := c.Check1Arg(); err != nil { - return nil, err - } - path, err := c.StringArg(0) - if err != nil { - return nil, err - } - - return c.PushingNext(t.Runtime, rt.StringValue(filepath.Base(path))), nil -} - -// dir(path) -> string -// Returns the directory part of `path`. For the rules, see Go's -// filepath.Dir -// --- @param path string -// --- @returns string -func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - if err := c.Check1Arg(); err != nil { - return nil, err - } - path, err := c.StringArg(0) - if err != nil { - return nil, err - } - - return c.PushingNext(t.Runtime, rt.StringValue(filepath.Dir(path))), nil -} - -// glob(pattern) -> matches (table) -// Glob all files and directories that match the pattern. -// For the rules, see Go's filepath.Glob -// --- @param pattern string -// --- @returns table -func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - if err := c.Check1Arg(); err != nil { - return nil, err - } - pattern, err := c.StringArg(0) - if err != nil { - return nil, err - } - - matches, err := filepath.Glob(pattern) - if err != nil { - return nil, err - } - - luaMatches := rt.NewTable() - - for i, match := range matches { - luaMatches.Set(rt.IntValue(int64(i + 1)), rt.StringValue(match)) - } - - return c.PushingNext(t.Runtime, rt.TableValue(luaMatches)), nil -} - -// join(...) -> string -// Takes paths and joins them together with the OS's -// directory separator (forward or backward slash). -// --- @vararg string -// --- @returns string -func fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - strs := make([]string, len(c.Etc())) - for i, v := range c.Etc() { - if v.Type() != rt.StringType { - // +2; go indexes of 0 and first arg from above - return nil, fmt.Errorf("bad argument #%d to run (expected string, got %s)", i + 1, v.TypeName()) - } - strs[i] = v.AsString() - } - - res := filepath.Join(strs...) - - return c.PushingNext(t.Runtime, rt.StringValue(res)), nil -} diff --git a/golibs/terminal/terminal.go b/golibs/terminal/terminal.go index 2040fac..954a4dd 100644 --- a/golibs/terminal/terminal.go +++ b/golibs/terminal/terminal.go @@ -34,7 +34,7 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { // size() // Gets the dimensions of the terminal. Returns a table with `width` and `height` -// Note: this is not the size in relation to the dimensions of the display +// NOTE: The size refers to the amount of columns and rows of text that can fit in the terminal. func termsize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { w, h, err := term.GetSize(int(os.Stdin.Fd())) if err != nil { @@ -49,7 +49,7 @@ func termsize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // saveState() -// Saves the current state of the terminal +// Saves the current state of the terminal. func termsaveState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { state, err := term.GetState(int(os.Stdin.Fd())) if err != nil { @@ -72,7 +72,7 @@ func termrestoreState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // setRaw() -// Puts the terminal in raw mode +// Puts the terminal into raw mode. func termsetRaw(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { _, err := term.MakeRaw(int(os.Stdin.Fd())) if err != nil { diff --git a/job.go b/job.go index 1beba9c..f5bd6f2 100644 --- a/job.go +++ b/job.go @@ -414,10 +414,16 @@ func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #interface jobs // add(cmdstr, args, execPath) -// Adds a new job to the job table. Note that this does not immediately run it. -// --- @param cmdstr string -// --- @param args table -// --- @param execPath string +// Creates a new job. This function does not run the job. This function is intended to be +// used by runners, but can also be used to create jobs via Lua. Commanders cannot be ran as jobs. +// #param cmdstr string String that a user would write for the job +// #param args table Arguments for the commands. Has to include the name of the command. +// #param execPath string Binary to use to run the command. Needs to be an absolute path. +/* +#example +hilbish.jobs.add('go build', {'go', 'build'}, '/usr/bin/go') +#example +*/ func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.CheckNArgs(3); err != nil { return nil, err @@ -448,9 +454,9 @@ func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } // #interface jobs -// all() -> table<@Job> +// all() -> table[@Job] // Returns a table of all job objects. -// --- @returns table +// #returns table[Job] func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { j.mu.RLock() defer j.mu.RUnlock() @@ -465,8 +471,8 @@ func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #interface jobs // disown(id) -// Disowns a job. This deletes it from the job table. -// --- @param id number +// Disowns a job. This simply deletes it from the list of jobs without stopping it. +// #param id number func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -486,8 +492,8 @@ func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #interface jobs // last() -> @Job -// Returns the last added job from the table. -// --- @returns Job +// Returns the last added job to the table. +// #returns Job func (j *jobHandler) luaLastJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { j.mu.RLock() defer j.mu.RUnlock() diff --git a/module.go b/module.go index 2ab55e8..bf4e32a 100644 --- a/module.go +++ b/module.go @@ -61,6 +61,7 @@ func moduleLoader(rtm *rt.Runtime) *rt.Table { // load(path) // Loads a module at the designated `path`. // It will throw if any error occurs. +// #param path string func moduleLoad(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.CheckNArgs(1); err != nil { return nil, err diff --git a/nature/commands/doc.lua b/nature/commands/doc.lua index ee1e37c..7263fe4 100644 --- a/nature/commands/doc.lua +++ b/nature/commands/doc.lua @@ -4,6 +4,39 @@ local fs = require 'fs' local lunacolors = require 'lunacolors' local Greenhouse = require 'nature.greenhouse' local Page = require 'nature.greenhouse.page' +local docfuncs = require 'nature.doc' + +local function strip(text, ...) + for _, pat in ipairs {...} do + text = text:gsub(pat, '\n') + end + + return text +end + +local function transformHTMLandMD(text) + return strip(text, '|||', '|%-%-%-%-|%-%-%-%-|') + :gsub('|(.-)|(.-)|', function(entry1, entry2) + return string.format('%s - %s', entry1, entry2) + end) + :gsub('
', '{separator}') + :gsub('<.->', '') + --:gsub('^\n\n', '\n') + :gsub('\n%s+\n', '\n\n') + --:gsub(' \n', '\n\n') + :gsub('{{< (%w+) `(.-)` >}}', function(shortcode, text) + return docfuncs.renderInfoBlock(shortcode, text) + end) + :gsub('```(%w+)\n(.-)```', function(lang, text) + return docfuncs.renderCodeBlock(text) + end) + :gsub('```\n(.-)\n```', function(text) + return docfuncs.renderCodeBlock(text) + end) + :gsub('`[^\n].-`', lunacolors.cyan) + :gsub('#+ (.-\n)', function(heading) return lunacolors.blue(lunacolors.bold('→ ' .. heading)) end) + :gsub('%*%*(.-)%*%*', lunacolors.bold) +end commander.register('doc', function(args, sinks) local moddocPath = hilbish.dataDir .. '/docs/' @@ -28,10 +61,13 @@ Available sections: ]] .. table.concat(modules, ', ') local vals = {} local docs = d - local valsStr = docs:match '%-%-%-\n([^%-%-%-]+)\n' - print(valsStr) + local valsStr = docs:match '^%-%-%-\n.-\n%-%-%-' if valsStr then - docs = docs:sub(valsStr:len() + 10, #docs) + docs = docs:sub(valsStr:len() + 2, #docs) + local pre = docs:sub(1, 1) + if pre == '\n' then + docs = docs:sub(2) + end -- parse vals local lines = string.split(valsStr, '\n') @@ -89,7 +125,7 @@ Available sections: ]] .. table.concat(modules, ', ') local size = terminal.size() self.region = { width = size.width, - height = size.height - 3 + height = size.height - 1 } end gh:resize() @@ -101,11 +137,13 @@ Available sections: ]] .. table.concat(modules, ', ') offset = self.specialOffset workingPage = self.specialPage end + local size = terminal.size() - self.sink:write(ansikit.getCSI(self.region.height + 2 .. ';1', 'H')) + self.sink:write(ansikit.getCSI(size.height - 1 .. ';1', 'H')) + self.sink:write(ansikit.getCSI(0, 'J')) if not self.isSpecial then if args[1] == 'api' then - self.sink:writeln(lunacolors.reset(string.format('%s', workingPage.title))) + self.sink:writeln(workingPage.title) self.sink:write(lunacolors.format(string.format('{grayBg} ↳ {white}{italic}%s {reset}', workingPage.description or 'No description.'))) else self.sink:write(lunacolors.reset(string.format('Viewing doc page %s', moddocPath))) @@ -114,21 +152,19 @@ Available sections: ]] .. table.concat(modules, ', ') end local backtickOccurence = 0 local function formatDocText(d) - return lunacolors.format(d:gsub('`', function() - backtickOccurence = backtickOccurence + 1 - if backtickOccurence % 2 == 0 then - return '{reset}' - else - return '{underline}{green}' - end + return transformHTMLandMD(d) + --[[ + return lunacolors.format(d:gsub('`(.-)`', function(t) + return docfuncs.renderCodeBlock(t) end):gsub('\n#+.-\n', function(t) local signature = t:gsub('<.->(.-)', '{underline}%1'):gsub('\\', '<') return '{bold}{yellow}' .. signature .. '{reset}' end)) + ]]-- end - local doc, vals = handleYamlInfo(#args == 0 and doc or formatDocText(f:read '*a':gsub('-([%d]+)', '%1'))) + local doc, vals = handleYamlInfo(#args == 0 and doc or formatDocText(f:read '*a')) if #moddocs ~= 0 and f then doc = doc .. '\nSubdocs: ' .. table.concat(subdocs, ', ') .. '\n\n' end @@ -146,8 +182,8 @@ Available sections: ]] .. table.concat(modules, ', ') end local f = io.open(moddocPath .. sdFile, 'rb') - local doc, vals = handleYamlInfo(f:read '*a':gsub('-([%d]+)', '%1')) - local page = Page(vals.title, formatDocText(doc)) + local doc, vals = handleYamlInfo(formatDocText(f:read '*a')) + local page = Page(vals.title or sdName, doc) page.description = vals.description gh:addPage(page) end diff --git a/nature/dirs.lua b/nature/dirs.lua index 5b7ec86..328b4b7 100644 --- a/nature/dirs.lua +++ b/nature/dirs.lua @@ -1,3 +1,4 @@ +-- @module dirs local fs = require 'fs' local dirs = {} @@ -11,8 +12,8 @@ dirs.recentDirs = {} dirs.recentSize = 10 --- Get (and remove) a `num` of entries from recent directories. ---- @param num number ---- @param remove boolean Whether to remove items +-- @param num number +-- @param remove boolean Whether to remove items function dirRecents(num, remove) num = num or 1 local entries = {} @@ -34,12 +35,12 @@ function dirRecents(num, remove) end --- Look at `num` amount of recent directories, starting from the latest. ---- @param num? number +-- @param num? number function dirs.peak(num) return dirRecents(num) end ---- Add `d` to the recent directories. +--- Add `d` to the recent directories list. function dirs.push(d) dirs.recentDirs[dirs.recentSize + 1] = nil if dirs.recentDirs[#dirs.recentDirs - 1] ~= d then @@ -50,20 +51,20 @@ function dirs.push(d) end end ---- Remove `num` amount of dirs from the recent directories. ---- @param num number +--- Remove the specified amount of dirs from the recent directories list. +-- @param num number function dirs.pop(num) return dirRecents(num, true) end ---- Get entry from recent directories. ---- @param idx number +--- Get entry from recent directories list based on index. +-- @param idx number function dirs.recent(idx) return dirs.recentDirs[idx] end ---- Sets the old directory. ---- @param d string +--- Sets the old directory string. +-- @param d string function dirs.setOld(d) ok, d = pcall(fs.abs, d) assert(ok, 'could not turn "' .. d .. '"into an absolute path') diff --git a/nature/doc.lua b/nature/doc.lua new file mode 100644 index 0000000..657af51 --- /dev/null +++ b/nature/doc.lua @@ -0,0 +1,47 @@ +local lunacolors = require 'lunacolors' + +local M = {} + +function M.highlight(text) + return text:gsub('\'.-\'', lunacolors.yellow) + --:gsub('%-%- .-', lunacolors.black) +end + +function M.renderCodeBlock(text) + local longest = 0 + local lines = string.split(text:gsub('\t', ' '), '\n') + + for i, line in ipairs(lines) do + local len = line:len() + if len > longest then longest = len end + end + + for i, line in ipairs(lines) do + lines[i] = ' ' .. M.highlight(line:sub(0, longest)) + .. string.rep(' ', longest - line:len()) .. ' ' + end + + return '\n' .. lunacolors.format('{greyBg}' .. table.concat(lines, '\n')) .. '\n' +end + +function M.renderInfoBlock(type, text) + local longest = 0 + local lines = string.split(text:gsub('\t', ' '), '\n') + + for i, line in ipairs(lines) do + local len = line:len() + if len > longest then longest = len end + end + + for i, line in ipairs(lines) do + lines[i] = ' ' .. M.highlight(line:sub(0, longest)) + .. string.rep(' ', longest - line:len()) .. ' ' + end + + local heading + if type == 'warning' then + heading = lunacolors.yellowBg(lunacolors.black(' ⚠ Warning ')) + end + return '\n' .. heading .. '\n' .. lunacolors.format('{greyBg}' .. table.concat(lines, '\n')) .. '\n' +end +return M diff --git a/nature/greenhouse/init.lua b/nature/greenhouse/init.lua index d5877e8..c64c11e 100644 --- a/nature/greenhouse/init.lua +++ b/nature/greenhouse/init.lua @@ -18,12 +18,20 @@ function Greenhouse:new(sink) self.contents = nil -- or can be a table self.start = 1 -- where to start drawing from (should replace with self.region.y) self.offset = 1 -- vertical text offset + self.horizOffset = 1 self.sink = sink self.pages = {} self.curPage = 1 + self.step = { + horizontal = 5, + vertical = 1 + } + self.separator = '─' self.keybinds = { ['Up'] = function(self) self:scroll 'up' end, ['Down'] = function(self) self:scroll 'down' end, + ['Left'] = function(self) self:scroll 'left' end, + ['Right'] = function(self) self:scroll 'right' end, ['Ctrl-Left'] = self.previous, ['Ctrl-Right'] = self.next, ['Ctrl-N'] = function(self) self:toc(true) end, @@ -51,7 +59,7 @@ function Greenhouse:updateCurrentPage(text) page:setText(text) end -local function sub(str, limit) +function Greenhouse:sub(str, offset, limit) local overhead = 0 local function addOverhead(s) overhead = overhead + string.len(s) @@ -63,7 +71,8 @@ local function sub(str, limit) :gsub('\x1b%[%d+;%d+%w', addOverhead) :gsub('\x1b%[%d+%w', addOverhead) - return s:sub(0, limit + overhead) + return s:sub(offset, utf8.offset(str, limit + overhead) or limit + overhead) + --return s:sub(offset, limit + overhead) end function Greenhouse:draw() @@ -82,14 +91,17 @@ function Greenhouse:draw() self.sink:write(ansikit.getCSI(self.start .. ';1', 'H')) self.sink:write(ansikit.getCSI(2, 'J')) + local writer = self.sink.writeln for i = offset, offset + self.region.height - 1 do if i > #lines then break end - local writer = self.sink.writeln if i == offset + self.region.height - 1 then writer = self.sink.write end - writer(self.sink, sub(lines[i]:gsub('\t', ' '), self.region.width)) + self.sink:write(ansikit.getCSI(self.start + i - offset .. ';1', 'H')) + local line = lines[i]:gsub('{separator}', function() return self.separator:rep(self.region.width - 1) end) + writer(self.sink, self:sub(line:gsub('\t', ' '), self.horizOffset, self.region.width)) end + writer(self.sink, '\27[0m') self:render() end @@ -109,13 +121,23 @@ function Greenhouse:scroll(direction) local lines = self.pages[self.curPage].lines local oldOffset = self.offset + local oldHorizOffset = self.horizOffset if direction == 'down' then - self.offset = math.min(self.offset + 1, math.max(1, #lines - self.region.height)) + self.offset = math.min(self.offset + self.step.vertical, math.max(1, #lines - self.region.height)) elseif direction == 'up' then - self.offset = math.max(self.offset - 1, 1) + self.offset = math.max(self.offset - self.step.vertical, 1) end +--[[ + if direction == 'left' then + self.horizOffset = math.max(self.horizOffset - self.step.horizontal, 1) + elseif direction == 'right' then + self.horizOffset = self.horizOffset + self.step.horizontal + end +]]-- + if self.offset ~= oldOffset then self:draw() end + if self.horizOffset ~= oldHorizOffset then self:draw() end end function Greenhouse:update() diff --git a/os.go b/os.go index da9eadd..46e3d3c 100644 --- a/os.go +++ b/os.go @@ -8,10 +8,9 @@ import ( ) // #interface os -// OS Info -// The `os` interface provides simple text information properties about -// the current OS on the systen. This mainly includes the name and -// version. +// operating system info +// Provides simple text information properties about the current operating system. +// This mainly includes the name and version. // #field family Family name of the current OS // #field name Pretty name of the current OS // #field version Version of the current OS diff --git a/rl.go b/rl.go index 17ea4df..7d5ed89 100644 --- a/rl.go +++ b/rl.go @@ -267,7 +267,7 @@ func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table { // #interface history // add(cmd) // Adds a command to the history. -// --- @param cmd string +// #param cmd string func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -284,15 +284,15 @@ func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) // #interface history // size() -> number // Returns the amount of commands in the history. -// --- @returns number +// #eturns number func (lr *lineReader) luaSize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.PushingNext1(t.Runtime, rt.IntValue(int64(lr.fileHist.Len()))), nil } // #interface history -// get(idx) -// Retrieves a command from the history based on the `idx`. -// --- @param idx number +// get(index) +// Retrieves a command from the history based on the `index`. +// #param index number func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -309,8 +309,8 @@ func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) // #interface history // all() -> table -// Retrieves all history. -// --- @returns table +// Retrieves all history as a table. +// #returns table func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { tbl := rt.NewTable() size := lr.fileHist.Len() diff --git a/runnermode.go b/runnermode.go index 8e9e7b9..55adfdc 100644 --- a/runnermode.go +++ b/runnermode.go @@ -8,11 +8,47 @@ import ( // #interface runner // interactive command runner customization -// The runner interface contains functions that allow the user to change -// how Hilbish interprets interactive input. -// Users can add and change the default runner for interactive input to any -// language or script of their choosing. A good example is using it to -// write command in Fennel. +/* The runner interface contains functions that allow the user to change +how Hilbish interprets interactive input. +Users can add and change the default runner for interactive input to any +language or script of their choosing. A good example is using it to +write command in Fennel. + +Runners are functions that evaluate user input. The default runners in +Hilbish can run shell script and Lua code. + +A runner is passed the input and has to return a table with these values. +All are not required, only the useful ones the runner needs to return. +(So if there isn't an error, just omit `err`.) + +- `exitCode` (number): A numerical code to indicate the exit result. +- `input` (string): The user input. This will be used to add +to the history. +- `err` (string): A string to indicate an interal error for the runner. +It can be set to a few special values for Hilbish to throw the right hooks and have a better looking message: + +`[command]: not-found` will throw a command.not-found hook based on what `[command]` is. + +`[command]: not-executable` will throw a command.not-executable hook. +- `continue` (boolean): Whether to prompt the user for more input. + +Here is a simple example of a fennel runner. It falls back to +shell script if fennel eval has an error. +```lua +local fennel = require 'fennel' + +hilbish.runnerMode(function(input) + local ok = pcall(fennel.eval, input) + if ok then + return { + input = input + } + end + + return hilbish.runner.sh(input) +end) +``` +*/ func runnerModeLoader(rtm *rt.Runtime) *rt.Table { exports := map[string]util.LuaExport{ "sh": {shRunner, 1, false}, @@ -28,18 +64,18 @@ func runnerModeLoader(rtm *rt.Runtime) *rt.Table { // #interface runner // setMode(cb) -// This is the same as the `hilbish.runnerMode` function. It takes a callback, -// which will be used to execute all interactive input. +// This is the same as the `hilbish.runnerMode` function. +// It takes a callback, which will be used to execute all interactive input. // In normal cases, neither callbacks should be overrided by the user, // as the higher level functions listed below this will handle it. -// --- @param cb function +// #param cb function func _runnerMode() {} // #interface runner // sh(cmd) // Runs a command in Hilbish's shell script interpreter. // This is the equivalent of using `source`. -// --- @param cmd string +// #param cmd string func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -67,7 +103,7 @@ func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // lua(cmd) // Evaluates `cmd` as Lua input. This is the same as using `dofile` // or `load`, but is appropriated for the runner interface. -// --- @param cmd string +// #param cmd string func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err diff --git a/timerhandler.go b/timerhandler.go index 0cb4197..0a8e34f 100644 --- a/timerhandler.go +++ b/timerhandler.go @@ -63,11 +63,10 @@ func (th *timersModule) get(id int) *timer { // #interface timers // create(type, time, callback) -> @Timer -// Creates a timer that runs based on the specified `time` in milliseconds. -// The `type` can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT` -// --- @param type number -// --- @param time number -// --- @param callback function +// Creates a timer that runs based on the specified `time`. +// #param type number What kind of timer to create, can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT` +// #param time number The amount of time the function should run in milliseconds. +// #param callback function The function to run for the timer. func (th *timersModule) luaCreate(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.CheckNArgs(3); err != nil { return nil, err @@ -93,8 +92,8 @@ func (th *timersModule) luaCreate(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #interface timers // get(id) -> @Timer // Retrieves a timer via its ID. -// --- @param id number -// --- @returns Timer +// #param id number +// #returns Timer func (th *timersModule) luaGet(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -122,15 +121,10 @@ a few seconds, you don't have to rely on timing tricks, as Hilbish has a timer API to set intervals and timeouts. These are the simple functions `hilbish.interval` and `hilbish.timeout` (doc -accessible with `doc hilbish`). But if you want slightly more control over -them, there is the `hilbish.timers` interface. It allows you to get -a timer via ID and control them. - -## Timer Object -All functions documented with the `Timer` type refer to a Timer object. +accessible with `doc hilbish`, or `Module hilbish` on the Website). An example of usage: -``` +```lua local t = hilbish.timers.create(hilbish.timers.TIMEOUT, 5000, function() print 'hello!' end) diff --git a/website/config.toml b/website/config.toml index 31f42d5..42f3d30 100644 --- a/website/config.toml +++ b/website/config.toml @@ -1,5 +1,5 @@ -baseURL = 'https://rosettea.github.io/Hilbish/' languageCode = 'en-us' +baseURL = 'https://rosettea.github.io/Hilbish/' title = 'Hilbish' theme = 'hsh' enableGitInfo = true @@ -29,6 +29,14 @@ enableGitInfo = true [markup.goldmark.renderer] unsafe = true +[markup.highlight] +lineNos = true +lineNumbersInTable = false +noClasses = false +codeFences = true +guessSyntax = true +tabWidth = 4 + [author] [author.sammyette] name = 'sammyette' diff --git a/website/content/docs b/website/content/docs new file mode 120000 index 0000000..92a7f82 --- /dev/null +++ b/website/content/docs @@ -0,0 +1 @@ +../../docs \ No newline at end of file diff --git a/website/content/docs/api b/website/content/docs/api deleted file mode 120000 index 1c5c360..0000000 --- a/website/content/docs/api +++ /dev/null @@ -1 +0,0 @@ -../../../docs/api/ \ No newline at end of file diff --git a/website/content/docs/features/runner-mode.md b/website/content/docs/features/runner-mode.md deleted file mode 100644 index 58b55dd..0000000 --- a/website/content/docs/features/runner-mode.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Runner Mode -description: Customize the interactive script/command runner. -layout: doc -menu: - docs: - parent: "Features" ---- - -Hilbish allows you to change how interactive text can be interpreted. -This is mainly due to the fact that the default method Hilbish uses -is that it runs Lua first and then falls back to shell script. - -In some cases, someone might want to switch to just shell script to avoid -it while interactive but still have a Lua config, or go full Lua to use -Hilbish as a REPL. This also allows users to add alternative languages like -Fennel as the interactive script runner. - -Runner mode can also be used to handle specific kinds of input before -evaluating like normal, which is how [Link.hsh](https://github.com/TorchedSammy/Link.hsh) -handles links. diff --git a/website/static/completion.mp4 b/website/static/completion.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..057f9ab4af41feb13c90a3532226cb0ddc438d09 GIT binary patch literal 34742 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

fJ#v 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%0A8<#)W_L{=hfpuv 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 + {{ $syntax := resources.Get "css/syntax.css" | resources.Minify | resources.Fingerprint }} + + diff --git a/website/themes/hsh/layouts/shortcodes/video.html b/website/themes/hsh/layouts/shortcodes/video.html new file mode 100644 index 0000000..d0f1314 --- /dev/null +++ b/website/themes/hsh/layouts/shortcodes/video.html @@ -0,0 +1,5 @@ + +