mirror of https://github.com/Hilbis/Hilbish
chore: update branch
commit
459c9baa89
|
@ -36,7 +36,7 @@ jobs:
|
|||
fetch-depth: 0
|
||||
- name: Download Task
|
||||
run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
|
||||
- uses: wangyoucao577/go-release-action@v1.25
|
||||
- uses: wangyoucao577/go-release-action@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
goos: ${{ matrix.goos }}
|
||||
|
|
55
CHANGELOG.md
55
CHANGELOG.md
|
@ -1,20 +1,66 @@
|
|||
# 🎀 Changelog
|
||||
|
||||
## Unreleased
|
||||
### Fixed
|
||||
- Line refresh fixes (less flicker)
|
||||
- Do more checks for a TTY
|
||||
- Panic if ENOTTY is thrown from readline
|
||||
- use `x/term` function to check if a terminal
|
||||
|
||||
### Added
|
||||
- Page Up/Down keybinds for Greenhouse will now scroll up and down the size of the region (a page)
|
||||
|
||||
### Changed
|
||||
- Remove usage of `hilbish.goro` in Greenhouse.
|
||||
- Values in `hilbish` table are no longer protected. This means
|
||||
they can be overridden. (#287)
|
||||
|
||||
## [2.2.1] - 2023-12-26
|
||||
### Fixed
|
||||
- Removed a left over debug print
|
||||
- Recover panic in `hilbish.goro`
|
||||
|
||||
## [2.2.0] - 2023-12-25
|
||||
### Added
|
||||
- [Native Modules](https://rosettea.github.io/Hilbish/docs/api/hilbish/hilbish.module/)
|
||||
- Made a few additions to the sink type:
|
||||
- `read()` method for retrieving input (so now the `in` sink of commanders is useful)
|
||||
- `flush()` and `autoFlush()` related to flushing outputs
|
||||
- `pipe` property to check if a sink with input is a pipe (like stdin)
|
||||
- Add fuzzy search to history search (enable via `hilbish.opts.fuzzy = true`)
|
||||
- Show indexes on cdr list
|
||||
- Show indexes on cdr list and use ~ for home directory.
|
||||
- Fix doc command not displaying correct subdocs when using shorthand api doc access (`doc api hilbish.jobs` as an example)
|
||||
- `hilbish.messages` interface (details in [#219])
|
||||
- `hilbish.notification` signal when a message/notification is sent
|
||||
- `notifyJobFinish` opt to send a notification when background jobs are
|
||||
- `hilbish.goVersion` for the version of Go used to compile Hilbish.
|
||||
completed.
|
||||
- Allow numbered arg substitutions in aliases.
|
||||
- Example: `hilbish.alias('hello', 'echo %1 says hello')` allows the user to run `hello hilbish`
|
||||
which will output `hilbish says hello`.
|
||||
- Greenhouse
|
||||
- Greenhouse is a pager library and program. Basic usage is `greenhouse <file>`
|
||||
- Using this also brings enhancements to the `doc` command like easy
|
||||
navigation of neighboring doc files.
|
||||
Ctrl-N can be used for the table of contents, which views adjacent documentation.
|
||||
|
||||
### 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
|
||||
|
||||
[#219]: https://github.com/Rosettea/Hilbish/issues/219
|
||||
### Fixed
|
||||
- Fix panic when runner doesn't return a table
|
||||
- Fix edge case of crash on empty alias resolve
|
||||
- File completion on Windows
|
||||
- Job management commands work now
|
||||
- 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)
|
||||
- Replaced `sed` in-place editing with `grep` and `mv` for compatibility with BSD utils
|
||||
|
||||
## [2.1.2] - 2022-04-10
|
||||
|
@ -662,6 +708,11 @@ This input for example will prompt for more input to complete:
|
|||
|
||||
First "stable" release of Hilbish.
|
||||
|
||||
[2.2.2]: https://github.com/Rosettea/Hilbish/compare/v2.2.1...v2.2.2
|
||||
[2.2.1]: https://github.com/Rosettea/Hilbish/compare/v2.2.0...v2.2.1
|
||||
[2.2.0]: https://github.com/Rosettea/Hilbish/compare/v2.1.0...v2.2.0
|
||||
[2.1.2]: https://github.com/Rosettea/Hilbish/compare/v2.1.1...v2.1.2
|
||||
[2.1.1]: https://github.com/Rosettea/Hilbish/compare/v2.1.0...v2.1.1
|
||||
[2.1.0]: https://github.com/Rosettea/Hilbish/compare/v2.0.1...v2.1.0
|
||||
[2.0.1]: https://github.com/Rosettea/Hilbish/compare/v2.0.0...v2.0.1
|
||||
[2.0.0]: https://github.com/Rosettea/Hilbish/compare/v1.2.0...v2.0.0
|
||||
|
|
|
@ -13,12 +13,22 @@ vars:
|
|||
|
||||
tasks:
|
||||
default:
|
||||
cmds:
|
||||
- go build {{.GOFLAGS}}
|
||||
vars:
|
||||
GOFLAGS: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}} -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"'
|
||||
|
||||
default-nocgo:
|
||||
cmds:
|
||||
- CGO_ENABLED=0 go build {{.GOFLAGS}}
|
||||
vars:
|
||||
GOFLAGS: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}} -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"'
|
||||
|
||||
build:
|
||||
cmds:
|
||||
- go build {{.GOFLAGS}}
|
||||
|
||||
build-nocgo:
|
||||
cmds:
|
||||
- CGO_ENABLED=0 go build {{.GOFLAGS}}
|
||||
|
||||
|
|
51
aliases.go
51
aliases.go
|
@ -1,6 +1,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
|
@ -46,9 +48,32 @@ func (a *aliasModule) Resolve(cmdstr string) string {
|
|||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
|
||||
args := strings.Split(cmdstr, " ")
|
||||
arg, _ := regexp.Compile(`[\\]?%\d+`)
|
||||
|
||||
args, _ := splitInput(cmdstr)
|
||||
if len(args) == 0 {
|
||||
// this shouldnt reach but...????
|
||||
return cmdstr
|
||||
}
|
||||
|
||||
for a.aliases[args[0]] != "" {
|
||||
alias := a.aliases[args[0]]
|
||||
alias = arg.ReplaceAllStringFunc(alias, func(a string) string {
|
||||
idx, _ := strconv.Atoi(a[1:])
|
||||
if strings.HasPrefix(a, "\\") || idx == 0 {
|
||||
return strings.TrimPrefix(a, "\\")
|
||||
}
|
||||
|
||||
if idx + 1 > len(args) {
|
||||
return a
|
||||
}
|
||||
val := args[idx]
|
||||
args = cut(args, idx)
|
||||
cmdstr = strings.Join(args, " ")
|
||||
|
||||
return val
|
||||
})
|
||||
|
||||
cmdstr = alias + strings.TrimPrefix(cmdstr, args[0])
|
||||
cmdArgs, _ := splitInput(cmdstr)
|
||||
args = cmdArgs
|
||||
|
@ -86,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<string, string>
|
||||
// list() -> table[string, string]
|
||||
// Get a table of all aliases, with string keys as the alias and the value as the command.
|
||||
// --- @returns table<string, string>
|
||||
// #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() {
|
||||
|
@ -107,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
|
||||
|
@ -122,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
|
||||
|
|
285
api.go
285
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 (
|
||||
|
@ -59,47 +59,8 @@ var hilbishLoader = packagelib.Loader{
|
|||
}
|
||||
|
||||
func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
||||
fakeMod := rt.NewTable()
|
||||
modmt := rt.NewTable()
|
||||
mod := rt.NewTable()
|
||||
|
||||
modIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
arg := c.Arg(1)
|
||||
val := mod.Get(arg)
|
||||
|
||||
return c.PushingNext1(t.Runtime, val), nil
|
||||
}
|
||||
modNewIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
k, err := c.StringArg(1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := c.Arg(2)
|
||||
if k == "highlighter" {
|
||||
var err error
|
||||
// fine to assign, since itll be either nil or a closure
|
||||
highlighter, err = c.ClosureArg(2)
|
||||
if err != nil {
|
||||
return nil, errors.New("hilbish.highlighter has to be a function")
|
||||
}
|
||||
} else if k == "hinter" {
|
||||
var err error
|
||||
hinter, err = c.ClosureArg(2)
|
||||
if err != nil {
|
||||
return nil, errors.New("hilbish.hinter has to be a function")
|
||||
}
|
||||
} else if modVal := mod.Get(rt.StringValue(k)); modVal != rt.NilValue {
|
||||
return nil, errors.New("not allowed to override in hilbish table")
|
||||
}
|
||||
mod.Set(rt.StringValue(k), v)
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
modmt.Set(rt.StringValue("__newindex"), rt.FunctionValue(rt.NewGoFunction(modNewIndex, "__newindex", 3, false)))
|
||||
modmt.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(modIndex, "__index", 2, false)))
|
||||
fakeMod.SetMetatable(modmt)
|
||||
|
||||
util.SetExports(rtm, mod, exports)
|
||||
hshMod = mod
|
||||
|
||||
|
@ -110,16 +71,16 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
|||
username = strings.Split(username, "\\")[1] // for some reason Username includes the hostname on windows
|
||||
}
|
||||
|
||||
util.SetFieldProtected(fakeMod, mod, "ver", rt.StringValue(getVersion()))
|
||||
util.SetFieldProtected(fakeMod, mod, "goVersion", rt.StringValue(runtime.Version()))
|
||||
util.SetFieldProtected(fakeMod, mod, "user", rt.StringValue(username))
|
||||
util.SetFieldProtected(fakeMod, mod, "host", rt.StringValue(host))
|
||||
util.SetFieldProtected(fakeMod, mod, "home", rt.StringValue(curuser.HomeDir))
|
||||
util.SetFieldProtected(fakeMod, mod, "dataDir", rt.StringValue(dataDir))
|
||||
util.SetFieldProtected(fakeMod, mod, "interactive", rt.BoolValue(interactive))
|
||||
util.SetFieldProtected(fakeMod, mod, "login", rt.BoolValue(login))
|
||||
util.SetFieldProtected(fakeMod, mod, "vimMode", rt.NilValue)
|
||||
util.SetFieldProtected(fakeMod, mod, "exitCode", rt.IntValue(0))
|
||||
util.SetField(rtm, mod, "ver", rt.StringValue(getVersion()))
|
||||
util.SetField(rtm, mod, "goVersion", rt.StringValue(runtime.Version()))
|
||||
util.SetField(rtm, mod, "user", rt.StringValue(username))
|
||||
util.SetField(rtm, mod, "host", rt.StringValue(host))
|
||||
util.SetField(rtm, mod, "home", rt.StringValue(curuser.HomeDir))
|
||||
util.SetField(rtm, mod, "dataDir", rt.StringValue(dataDir))
|
||||
util.SetField(rtm, mod, "interactive", rt.BoolValue(interactive))
|
||||
util.SetField(rtm, mod, "login", rt.BoolValue(login))
|
||||
util.SetField(rtm, mod, "vimMode", rt.NilValue)
|
||||
util.SetField(rtm, mod, "exitCode", rt.IntValue(0))
|
||||
|
||||
// hilbish.userDir table
|
||||
hshuser := userDirLoader(rtm)
|
||||
|
@ -140,7 +101,9 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
|||
|
||||
// hilbish.completion table
|
||||
hshcomp := completionLoader(rtm)
|
||||
// TODO: REMOVE "completion" AND ONLY USE "completions" WITH AN S
|
||||
mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp))
|
||||
mod.Set(rt.StringValue("completions"), rt.TableValue(hshcomp))
|
||||
|
||||
// hilbish.runner table
|
||||
runnerModule := runnerModeLoader(rtm)
|
||||
|
@ -166,7 +129,10 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
|||
util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName))
|
||||
mod.Set(rt.StringValue("version"), rt.TableValue(versionModule))
|
||||
|
||||
return rt.TableValue(fakeMod), nil
|
||||
pluginModule := moduleLoader(rtm)
|
||||
mod.Set(rt.StringValue("module"), rt.TableValue(pluginModule))
|
||||
|
||||
return rt.TableValue(mod), nil
|
||||
}
|
||||
|
||||
func getenv(key, fallback string) string {
|
||||
|
@ -187,12 +153,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
|
||||
|
@ -234,8 +198,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()
|
||||
|
||||
|
@ -246,9 +210,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 {
|
||||
|
@ -276,14 +240,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()
|
||||
|
@ -317,8 +288,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
|
||||
|
@ -333,9 +324,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
|
||||
|
@ -355,8 +356,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
|
||||
|
@ -390,8 +403,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
|
||||
|
@ -425,8 +439,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
|
||||
|
@ -438,6 +455,12 @@ func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
|
||||
// call fn
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// do something here?
|
||||
}
|
||||
}()
|
||||
|
||||
_, err := rt.Call1(l.MainThread(), rt.FunctionValue(fn), c.Etc()...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error in goro function:\n\n", err)
|
||||
|
@ -448,11 +471,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
|
||||
|
@ -474,11 +497,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
|
||||
|
@ -500,13 +523,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.<cmd>`,
|
||||
// Registers a completion handler for the specified scope.
|
||||
// A `scope` is expected to be `command.<cmd>`,
|
||||
// replacing <cmd> 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 {
|
||||
|
@ -518,8 +568,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
|
||||
|
@ -542,8 +592,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
|
||||
|
@ -573,8 +623,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
|
||||
|
@ -599,12 +651,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
|
||||
|
@ -630,26 +684,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
|
||||
}
|
||||
|
|
|
@ -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(`@\w+`)
|
||||
modDescription := typeTag.ReplaceAllStringFunc(strings.Replace(modu.Description, "<", `\<`, -1), func(typ string) string {
|
||||
typeTag, _ := regexp.Compile(`\B@\w+`)
|
||||
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(`<a href="%s" style="text-decoration: none;">%s</a>`, 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(`<a href="#%s">%s</a>`, 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("<hr>\n<div id='%s'>", 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(`<a href="%s" style="text-decoration: none;">%s</a>`, linkedTyp, typName)
|
||||
return fmt.Sprintf(`<a href="%s" style="text-decoration: none;" id="lol">%s</a>`, linkedTyp, typName)
|
||||
})
|
||||
f.WriteString(fmt.Sprintf("### %s\n", htmlSig))
|
||||
f.WriteString(fmt.Sprintf(`
|
||||
<h4 class='heading'>
|
||||
%s
|
||||
<a href="#%s" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
`, 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("</div>")
|
||||
f.WriteString("\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
if len(modu.Types) != 0 {
|
||||
f.WriteString("## Types\n")
|
||||
for _, dps := range modu.Types {
|
||||
f.WriteString("<hr>\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")
|
||||
|
|
|
@ -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 <close> = 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('<hr>\n<div id=\'%s\'>', 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([[
|
||||
<h4 class='heading'>
|
||||
%s
|
||||
<a href="#%s" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
]], 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('</div>')
|
||||
f:write('\n\n')
|
||||
end
|
||||
end
|
141
complete.go
141
complete.go
|
@ -188,15 +188,15 @@ func escapeFilename(fname string) string {
|
|||
return escapeReplaer.Replace(fname)
|
||||
}
|
||||
|
||||
// #interface completions
|
||||
// #interface completion
|
||||
// tab completions
|
||||
// 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()
|
||||
|
@ -205,27 +205,58 @@ func completionLoader(rtm *rt.Runtime) *rt.Table {
|
|||
return mod
|
||||
}
|
||||
|
||||
// #interface completions
|
||||
// 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
|
||||
// #interface completion
|
||||
// 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 completions
|
||||
// #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
|
||||
}
|
||||
|
@ -253,24 +284,26 @@ func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
}
|
||||
|
||||
// we must keep the holy 80 cols
|
||||
completerReturn, err := rt.Call1(l.MainThread(),
|
||||
rt.FunctionValue(completecb), rt.StringValue(query),
|
||||
rt.StringValue(ctx), rt.TableValue(fields))
|
||||
cont := c.Next()
|
||||
err = rt.Call(l.MainThread(), rt.FunctionValue(completecb),
|
||||
[]rt.Value{rt.StringValue(query), rt.StringValue(ctx), rt.TableValue(fields)},
|
||||
cont)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, completerReturn), nil
|
||||
return cont, nil
|
||||
}
|
||||
|
||||
// #interface completions
|
||||
// #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
|
||||
|
@ -286,28 +319,32 @@ func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil
|
||||
}
|
||||
|
||||
// #interface completions
|
||||
// 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
|
||||
}
|
||||
// #interface 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.
|
||||
// #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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: API
|
||||
layout: doc
|
||||
weight: -50
|
||||
weight: -100
|
||||
menu: docs
|
||||
---
|
||||
|
||||
|
|
167
docs/api/bait.md
167
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)
|
||||