chore: merge from master

native-modules
sammyette 2023-02-19 15:27:00 -04:00
commit f8d66f4b81
Signed by: sammyette
GPG Key ID: 904FC49417B44DCD
130 changed files with 3367 additions and 815 deletions

6
.editorconfig 100644
View File

@ -0,0 +1,6 @@
root = true
[*]
charset = utf-8
indent_size = 4
indent_style = tab

View File

@ -25,7 +25,7 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: '1.17.7' go-version: '1.18.8'
- name: Download Task - name: Download Task
run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d' run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
- name: Build - name: Build

View File

@ -2,13 +2,14 @@ name: Generate docs
on: on:
push: push:
branches: [master] branches:
- master
jobs: jobs:
gen: gen:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/setup-go@v2 - uses: actions/setup-go@v2
- name: Run docgen - name: Run docgen
run: go run cmd/docgen/docgen.go run: go run cmd/docgen/docgen.go

View File

@ -33,10 +33,14 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
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.25
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }} goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }} goarch: ${{ matrix.goarch }}
ldflags: '-s -w'
binary_name: hilbish binary_name: hilbish
extra_files: LICENSE README.md CHANGELOG.md .hilbishrc.lua nature libs docs emmyLuaDocs extra_files: LICENSE README.md CHANGELOG.md .hilbishrc.lua nature libs docs emmyLuaDocs

31
.github/workflows/website.yml vendored 100644
View File

@ -0,0 +1,31 @@
name: Build website
on:
push:
branches:
- master
- docs-refactor
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: true
fetch-depth: 0
- name: Setup Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: 'latest'
extended: true
- name: Build
run: 'cd website && hugo --minify'
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./website/public

3
.gitignore vendored
View File

@ -1,6 +1,9 @@
*.exe *.exe
hilbish hilbish
!docs/api/hilbish
docgen docgen
!cmd/docgen
.vim .vim
petals/ petals/
.hugo_build.lock

View File

@ -1,7 +1,44 @@
# 🎀 Changelog # 🎀 Changelog
## Unreleased ## Unreleased
**NOTE:** Hilbish now uses [Task] insead of Make for builds. ### Fixed
- Replaced `sed` in-place editing with `grep` and `mv` for compatibility with BSD utils
## [2.1.0] - 2022-02-10
### Added
- Documented custom userdata types (Job and Timer Objects)
- Coming with this fix is also adding the return types for some functions that were missing it
- Added a dedicated input and dedicated outputs for commanders (sinks - info at `doc api commander`).
- Local docs is used if one of Hilbish's branches is found
- Return 1 exit code on doc not found
- `hilbish.runner.getCurrent()` to get the current runner
- Initialize Hilbish Lua API before handling signals
### Fixed
- `index` or `_index` subdocs should not show up anymore
- `hilbish.which` not working correctly with aliases
- Commanders not being able to pipe with commands or any related operator.
- Resolve symlinks in completions
- Updated `runner-mode` docs
- Fix `hilbish.completion` functions panicking when empty input is provided
## [2.0.1] - 2022-12-28
### Fixed
- Corrected documentation for hooks, removing outdated `command.no-perm`
- Fixed an issue where `cd` with no args would not update the old pwd
- Tiny documentation enhancements for the `hilbish.timer` interface
## [2.0.0] - 2022-12-20
**NOTES FOR USERS/PACKAGERS UPDATING:**
- Hilbish now uses [Task] insead of Make for builds.
- The doc format has been changed from plain text to markdown.
**YOU MUST reinstall Hilbish to remove the duplicate, old docs.**
- Hilbish will by default install to **`/usr/local`** instead of just `/usr/`
when building via Task. This is mainly to avoid conflict of distro packages
and local installs, and is the correct place when building from git either way.
To keep Hilbish in `/usr`, you must have `PREFIX="/usr/"` when running `task build` or `task install`
- Windows is no longer supported. It will build and run, but **will** have problems.
If you want to help fix the situation, start a discussion or open an issue and contribute.
[Task]: https://taskfile.dev/#/ [Task]: https://taskfile.dev/#/
@ -39,7 +76,7 @@ without arguments will disown the last job.
fields on a job object. fields on a job object.
- Documentation for jobs is now available via `doc jobs`. - Documentation for jobs is now available via `doc jobs`.
- `hilbish.alias.resolve(cmdstr)` to resolve a command alias. - `hilbish.alias.resolve(cmdstr)` to resolve a command alias.
- `hilbish.opts` for shell options. Currently, the only opt is `autocd`. - `hilbish.opts` for shell options.
- `hilbish.editor` interface for interacting with the line editor that - `hilbish.editor` interface for interacting with the line editor that
Hilbish uses. Hilbish uses.
- `hilbish.vim` interface to dynamically get/set vim registers. - `hilbish.vim` interface to dynamically get/set vim registers.
@ -73,12 +110,20 @@ disables commands being added to history.
- A new and "safer" event emitter has been added. This causes a performance deficit, but avoids a lot of - A new and "safer" event emitter has been added. This causes a performance deficit, but avoids a lot of
random errors introduced with the new Lua runtime (see [#197]) random errors introduced with the new Lua runtime (see [#197])
- `bait.release(name, catcher)` removes `handler` for the named `event` - `bait.release(name, catcher)` removes `handler` for the named `event`
- `exec`, `clear` and `cat` builtin commands
- `hilbish.cancel` hook thrown when user cancels input with Ctrl-C
- 1st item on history is now inserted when history search menu is opened ([#148])
- Documentation has been improved vastly!
[#148]: https://github.com/Rosettea/Hilbish/issues/148
[#197]: https://github.com/Rosettea/Hilbish/issues/197 [#197]: https://github.com/Rosettea/Hilbish/issues/197
### Changed ### Changed
- **Breaking Change:** Upgraded to Lua 5.4. - **Breaking Change:** Upgraded to Lua 5.4.
This is probably one of (if not the) biggest things in this release. This is probably one of (if not the) biggest things in this release.
To recap quickly on what matters (mostly):
- `os.execute` returns 3 values instead of 1 (but you should be using `hilbish.run`)
- I/O operations must be flushed (`io.flush()`)
- **Breaking Change:** MacOS config paths now match Linux. - **Breaking Change:** MacOS config paths now match Linux.
- Overrides on the `hilbish` table are no longer permitted. - Overrides on the `hilbish` table are no longer permitted.
- **Breaking Change:** Runner functions are now required to return a table. - **Breaking Change:** Runner functions are now required to return a table.
@ -97,6 +142,7 @@ of a dot. (ie. `job.stop()` -> `job:stop()`)
- All `fs` module functions which take paths now implicitly expand ~ to home. - All `fs` module functions which take paths now implicitly expand ~ to home.
- **Breaking Change:** `hilbish.greeting` has been moved to an opt (`hilbish.opts.greeting`) and is - **Breaking Change:** `hilbish.greeting` has been moved to an opt (`hilbish.opts.greeting`) and is
always printed by default. To disable it, set the opt to false. always printed by default. To disable it, set the opt to false.
- **Breaking Change:** `command.no-perm` hook has been replaced with `command.not-executable`
- History is now fetched from Lua, which means users can override `hilbish.history` - History is now fetched from Lua, which means users can override `hilbish.history`
methods to make it act how they want. methods to make it act how they want.
- `guide` has been removed. See the [website](https://rosettea.github.io/Hilbish/) - `guide` has been removed. See the [website](https://rosettea.github.io/Hilbish/)
@ -112,7 +158,7 @@ replacing the last character.
- `hilbish.login` being the wrong value. - `hilbish.login` being the wrong value.
- Put full input in history if prompted for continued input - Put full input in history if prompted for continued input
- Don't put alias expanded command in history (sound familiar?) - Don't put alias expanded command in history (sound familiar?)
- Handle cases of stdin being nonblocking (in the case of [#130](https://github.com/Rosettea/Hilbish/issues/130)) - Handle cases of stdin being nonblocking (in the case of [#136](https://github.com/Rosettea/Hilbish/issues/136))
- Don't prompt for continued input if non interactive - Don't prompt for continued input if non interactive
- Don't insert unhandled control keys. - Don't insert unhandled control keys.
- Handle sh syntax error in alias - Handle sh syntax error in alias
@ -122,11 +168,12 @@ certain color rules.
- Home/End keys now go to the actual start/end of the input. - Home/End keys now go to the actual start/end of the input.
- Input getting cut off on enter in certain cases. - Input getting cut off on enter in certain cases.
- Go to the next line properly if input reaches end of terminal width. - Go to the next line properly if input reaches end of terminal width.
- Cursor position with CJK characters. ([#145](https://github.com/Rosettea/Hilbish/pull/145)) - Cursor position with CJK characters has been corrected ([#145](https://github.com/Rosettea/Hilbish/pull/145))
- Files with same name as parent folder in completions getting cut off [#136](https://github.com/Rosettea/Hilbish/issues/136)) - Files with same name as parent folder in completions getting cut off [#130](https://github.com/Rosettea/Hilbish/issues/130))
- `hilbish.which` now works with commanders and aliases. - `hilbish.which` now works with commanders and aliases.
- Background jobs no longer take stdin so they do not interfere with shell - Background jobs no longer take stdin so they do not interfere with shell
input. input.
- Full name of completion entry is used instead of being cut off
- Completions are fixed in cases where the query/line is an alias alone - Completions are fixed in cases where the query/line is an alias alone
where it can also resolve to the beginning of command names. where it can also resolve to the beginning of command names.
(reference [this commit](https://github.com/Rosettea/Hilbish/commit/2790982ad123115c6ddbc5764677fdca27668cea)) (reference [this commit](https://github.com/Rosettea/Hilbish/commit/2790982ad123115c6ddbc5764677fdca27668cea))
@ -145,6 +192,29 @@ menu is open.
- Escape codes now work. - Escape codes now work.
- Escape percentage symbols in completion entries, so you will no longer see - Escape percentage symbols in completion entries, so you will no longer see
an error of missing format variable an error of missing format variable
- Fix an error with sh syntax in aliases
- Prompt now works with east asian characters (CJK)
- Set back the prompt to normal after exiting the continue prompt with ctrl-d
- Take into account newline in input when calculating input width. Prevents
extra reprinting of the prompt, but input with newlines inserted is still a problem
- Put cursor at the end of input when exiting $EDITOR with Vim mode bind
- Calculate width of virtual input properly (completion candidates)
- Users can now tab complete files with spaces while quoted or with escaped spaces.
This means a query of `Files\ to\ ` with file names of `Files to tab complete` and `Files to complete`
will result in the files being completed.
- Fixed grid menu display if cell width ends up being the width of the terminal
- Cut off item names in grid menu if its longer than cell width
- Fix completion search menu disappearing
- Make binary completion work with bins that have spaces in the name
- Completion paths having duplicated characters if it's escaped
- Get custom completion command properly to call from Lua
- Put proper command on the line when using up and down arrow keys to go through command history
- Don't do anything if length of input rune slice is 0 ([commit for explanation](https://github.com/Rosettea/Hilbish/commit/8d40179a73fe5942707cd43f9c0463dee53eedd8))
## [2.0.0-rc1] - 2022-09-14
This is a pre-release version of Hilbish for testing. To see the changelog,
refer to the `Unreleased` section of the [full changelog](CHANGELOG.md)
(version 2.0.0 for future reference).
## [1.2.0] - 2022-03-17 ## [1.2.0] - 2022-03-17
### Added ### Added
@ -569,6 +639,11 @@ This input for example will prompt for more input to complete:
First "stable" release of Hilbish. First "stable" release of Hilbish.
[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
[2.0.0-rc1]: https://github.com/Rosettea/Hilbish/compare/v1.2.0...v2.0.0-rc1
[1.2.0]: https://github.com/Rosettea/Hilbish/compare/v1.1.4...v1.2.0
[1.1.0]: https://github.com/Rosettea/Hilbish/compare/v1.0.4...v1.1.0 [1.1.0]: https://github.com/Rosettea/Hilbish/compare/v1.0.4...v1.1.0
[1.0.4]: https://github.com/Rosettea/Hilbish/compare/v1.0.3...v1.0.4 [1.0.4]: https://github.com/Rosettea/Hilbish/compare/v1.0.3...v1.0.4
[1.0.3]: https://github.com/Rosettea/Hilbish/compare/v1.0.2...v1.0.3 [1.0.3]: https://github.com/Rosettea/Hilbish/compare/v1.0.2...v1.0.3

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2022 Rosettea Copyright (c) 2021-2023 Rosettea
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -26,49 +26,30 @@ and aims to be infinitely configurable. If something isn't, open an issue!
# Table of Contents # Table of Contents
- [Screenshots](#Screenshots) - [Screenshots](#Screenshots)
- [Installation](#Installation) - [Getting Hilbish](#Getting-Hilbish)
- [Prebuilt Bins](#Prebuilt-binaries)
- [AUR](#AUR)
- [Nixpkgs](#Nixpkgs)
- [Manual Build](#Manual-Build)
- [Getting Started](#Getting-Started)
- [Contributing](#Contributing) - [Contributing](#Contributing)
# Screenshots # Screenshots
<div align="center"> <div align="center">
<img src="gallery/default.png"><br><br>
<img src="gallery/terminal.png"><br><br> <img src="gallery/terminal.png"><br><br>
<img src="gallery/tab.png"><br><br>
<img src="gallery/pillprompt.png"> <img src="gallery/pillprompt.png">
</div> </div>
# Installation # Getting Hilbish
## Prebuilt binaries **NOTE:** Hilbish is not guaranteed to work properly on Windows, starting
Go [here](https://nightly.link/Rosettea/Hilbish/workflows/build/master) for from the 2.0 version. It will still be able to compile, but functionality
builds on the master branch. may be lacking.
## AUR You can check the [install page](https://rosettea.github.io/Hilbish/install/)
[![AUR maintainer](https://img.shields.io/aur/maintainer/hilbish?logo=arch-linux&style=flat-square)](https://aur.archlinux.org/packages/hilbish) on the website for distributed binaries from GitHub or other package repositories.
Arch Linux users can install Hilbish from the AUR with the following command: Otherwise, continue reading for steps on compiling.
```sh
yay -S hilbish
```
[![AUR maintainer](https://img.shields.io/aur/maintainer/hilbish?logo=arch-linux&style=flat-square)](https://aur.archlinux.org/packages/hilbish-git) ## Prerequisites
Or from the latest `master` commit with:
```sh
yay -S hilbish-git
```
## Nixpkgs
Nix/NixOS users can install Hilbish from the central repository, nixpkgs, through the usual ways.
If you're new to nix you should probably read up on how to do that [here](https://nixos.wiki/wiki/Cheatsheet).
## Manual Build
### Prerequisites
- [Go 1.17+](https://go.dev) - [Go 1.17+](https://go.dev)
- [Task](https://taskfile.dev/#/) - [Task](https://taskfile.dev/installation/) (**Go on the hyperlink here to see Task's install method for your OS.**)
### Build ## Build
First, clone Hilbish. The recursive is required, as some Lua libraries First, clone Hilbish. The recursive is required, as some Lua libraries
are submodules. are submodules.
```sh ```sh

View File

@ -3,23 +3,24 @@
version: '3' version: '3'
vars: vars:
PREFIX: '{{default "/usr" .PREFIX}}' PREFIX: '{{default "/usr/local" .PREFIX}}'
bindir__: '{{.PREFIX}}/bin' bindir__: '{{.PREFIX}}/bin'
BINDIR: '{{default .bindir__ .BINDIR}}' BINDIR: '{{default .bindir__ .BINDIR}}'
libdir__: '{{.PREFIX}}/share/hilbish' libdir__: '{{.PREFIX}}/share/hilbish'
LIBDIR: '{{default .libdir__ .LIBDIR}}' LIBDIR: '{{default .libdir__ .LIBDIR}}'
GOFLAGS: '-ldflags "-s -w"' goflags__: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}}"'
GOFLAGS: '{{default .goflags__ .GOFLAGS}}'
tasks: tasks:
default: default:
cmds: cmds:
- go build {{.GOFLAGS}} - CGO_ENABLED=0 go build {{.GOFLAGS}}
vars: vars:
GOFLAGS: '-ldflags "-s -w -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"' 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: build:
cmds: cmds:
- go build {{.GOFLAGS}} - CGO_ENABLED=0 go build {{.GOFLAGS}}
install: install:
cmds: cmds:
@ -33,4 +34,4 @@ tasks:
- rm -vrf - rm -vrf
"{{.DESTDIR}}{{.BINDIR}}/hilbish" "{{.DESTDIR}}{{.BINDIR}}/hilbish"
"{{.DESTDIR}}{{.LIBDIR}}" "{{.DESTDIR}}{{.LIBDIR}}"
- sed -i '/hilbish/d' /etc/shells - grep -v 'hilbish' /etc/shells > /tmp/shells.hilbish_uninstall && mv /tmp/shells.hilbish_uninstall /etc/shells

View File

@ -9,40 +9,40 @@ import (
rt "github.com/arnodel/golua/runtime" rt "github.com/arnodel/golua/runtime"
) )
var aliases *aliasHandler var aliases *aliasModule
type aliasHandler struct { type aliasModule struct {
aliases map[string]string aliases map[string]string
mu *sync.RWMutex mu *sync.RWMutex
} }
// initialize aliases map // initialize aliases map
func newAliases() *aliasHandler { func newAliases() *aliasModule {
return &aliasHandler{ return &aliasModule{
aliases: make(map[string]string), aliases: make(map[string]string),
mu: &sync.RWMutex{}, mu: &sync.RWMutex{},
} }
} }
func (a *aliasHandler) Add(alias, cmd string) { func (a *aliasModule) Add(alias, cmd string) {
a.mu.Lock() a.mu.Lock()
defer a.mu.Unlock() defer a.mu.Unlock()
a.aliases[alias] = cmd a.aliases[alias] = cmd
} }
func (a *aliasHandler) All() map[string]string { func (a *aliasModule) All() map[string]string {
return a.aliases return a.aliases
} }
func (a *aliasHandler) Delete(alias string) { func (a *aliasModule) Delete(alias string) {
a.mu.Lock() a.mu.Lock()
defer a.mu.Unlock() defer a.mu.Unlock()
delete(a.aliases, alias) delete(a.aliases, alias)
} }
func (a *aliasHandler) Resolve(cmdstr string) string { func (a *aliasModule) Resolve(cmdstr string) string {
a.mu.RLock() a.mu.RLock()
defer a.mu.RUnlock() defer a.mu.RUnlock()
@ -66,7 +66,10 @@ func (a *aliasHandler) Resolve(cmdstr string) string {
// lua section // lua section
func (a *aliasHandler) Loader(rtm *rt.Runtime) *rt.Table { // #interface aliases
// command aliasing
// The alias interface deals with all command aliases in Hilbish.
func (a *aliasModule) Loader(rtm *rt.Runtime) *rt.Table {
// create a lua module with our functions // create a lua module with our functions
hshaliasesLua := map[string]util.LuaExport{ hshaliasesLua := map[string]util.LuaExport{
"add": util.LuaExport{hlalias, 2, false}, "add": util.LuaExport{hlalias, 2, false},
@ -81,7 +84,18 @@ func (a *aliasHandler) Loader(rtm *rt.Runtime) *rt.Table {
return mod return mod
} }
func (a *aliasHandler) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #interface aliases
// add(alias, cmd)
// This is an alias (ha) for the `hilbish.alias` function.
// --- @param alias string
// --- @param cmd string
func _hlalias() {}
// #interface aliases
// 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>
func (a *aliasModule) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
aliasesList := rt.NewTable() aliasesList := rt.NewTable()
for k, v := range a.All() { for k, v := range a.All() {
aliasesList.Set(rt.StringValue(k), rt.StringValue(v)) aliasesList.Set(rt.StringValue(k), rt.StringValue(v))
@ -90,7 +104,11 @@ func (a *aliasHandler) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.TableValue(aliasesList)), nil return c.PushingNext1(t.Runtime, rt.TableValue(aliasesList)), nil
} }
func (a *aliasHandler) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #interface aliases
// delete(name)
// Removes an alias.
// --- @param name string
func (a *aliasModule) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
} }
@ -103,7 +121,12 @@ func (a *aliasHandler) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
func (a *aliasHandler) luaResolve(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
func (a *aliasModule) luaResolve(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
} }

139
api.go
View File

@ -1,6 +1,14 @@
// Here is the core api for the hilbi shell itself // the core Hilbish API
// Basically, stuff about the shell itself and other functions // The Hilbish module includes the core API, containing
// go here. // interfaces and functions which directly relate to shell functionality.
// #field ver The version of Hilbish
// #field user Username of the user
// #field host Hostname of the machine
// #field dataDir Directory for Hilbish data files, including the docs and default modules
// #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
package main package main
import ( import (
@ -19,7 +27,6 @@ import (
rt "github.com/arnodel/golua/runtime" rt "github.com/arnodel/golua/runtime"
"github.com/arnodel/golua/lib/packagelib" "github.com/arnodel/golua/lib/packagelib"
"github.com/maxlandon/readline" "github.com/maxlandon/readline"
"github.com/blackfireio/osinfo"
"mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/interp"
) )
@ -102,78 +109,59 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
username = strings.Split(username, "\\")[1] // for some reason Username includes the hostname on windows username = strings.Split(username, "\\")[1] // for some reason Username includes the hostname on windows
} }
util.SetFieldProtected(fakeMod, mod, "ver", rt.StringValue(getVersion()), "Hilbish version") util.SetFieldProtected(fakeMod, mod, "ver", rt.StringValue(getVersion()))
util.SetFieldProtected(fakeMod, mod, "user", rt.StringValue(username), "Username of user") util.SetFieldProtected(fakeMod, mod, "user", rt.StringValue(username))
util.SetFieldProtected(fakeMod, mod, "host", rt.StringValue(host), "Host name of the machine") util.SetFieldProtected(fakeMod, mod, "host", rt.StringValue(host))
util.SetFieldProtected(fakeMod, mod, "home", rt.StringValue(curuser.HomeDir), "Home directory of the user") util.SetFieldProtected(fakeMod, mod, "home", rt.StringValue(curuser.HomeDir))
util.SetFieldProtected(fakeMod, mod, "dataDir", rt.StringValue(dataDir), "Directory for Hilbish's data files") util.SetFieldProtected(fakeMod, mod, "dataDir", rt.StringValue(dataDir))
util.SetFieldProtected(fakeMod, mod, "interactive", rt.BoolValue(interactive), "If this is an interactive shell") util.SetFieldProtected(fakeMod, mod, "interactive", rt.BoolValue(interactive))
util.SetFieldProtected(fakeMod, mod, "login", rt.BoolValue(login), "Whether this is a login shell") util.SetFieldProtected(fakeMod, mod, "login", rt.BoolValue(login))
util.SetFieldProtected(fakeMod, mod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)") util.SetFieldProtected(fakeMod, mod, "vimMode", rt.NilValue)
util.SetFieldProtected(fakeMod, mod, "exitCode", rt.IntValue(0), "Exit code of last exected command") util.SetFieldProtected(fakeMod, mod, "exitCode", rt.IntValue(0))
util.Document(fakeMod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.")
// hilbish.userDir table // hilbish.userDir table
hshuser := rt.NewTable() hshuser := userDirLoader(rtm)
util.SetField(rtm, hshuser, "config", rt.StringValue(confDir), "User's config directory")
util.SetField(rtm, hshuser, "data", rt.StringValue(userDataDir), "XDG data directory")
util.Document(hshuser, "User directories to store configs and/or modules.")
mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser)) mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser))
// hilbish.os table // hilbish.os table
hshos := rt.NewTable() hshos := hshosLoader(rtm)
info, _ := osinfo.GetOSInfo()
util.SetField(rtm, hshos, "family", rt.StringValue(info.Family), "Family name of the current OS")
util.SetField(rtm, hshos, "name", rt.StringValue(info.Name), "Pretty name of the current OS")
util.SetField(rtm, hshos, "version", rt.StringValue(info.Version), "Version of the current OS")
util.Document(hshos, "OS info interface")
mod.Set(rt.StringValue("os"), rt.TableValue(hshos)) mod.Set(rt.StringValue("os"), rt.TableValue(hshos))
// hilbish.aliases table // hilbish.aliases table
aliases = newAliases() aliases = newAliases()
aliasesModule := aliases.Loader(rtm) aliasesModule := aliases.Loader(rtm)
util.Document(aliasesModule, "Alias inferface for Hilbish.")
mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule)) mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule))
// hilbish.history table // hilbish.history table
historyModule := lr.Loader(rtm) historyModule := lr.Loader(rtm)
mod.Set(rt.StringValue("history"), rt.TableValue(historyModule)) mod.Set(rt.StringValue("history"), rt.TableValue(historyModule))
util.Document(historyModule, "History interface for Hilbish.")
// hilbish.completion table // hilbish.completion table
hshcomp := completionLoader(rtm) hshcomp := completionLoader(rtm)
util.Document(hshcomp, "Completions interface for Hilbish.")
mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp)) mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp))
// hilbish.runner table // hilbish.runner table
runnerModule := runnerModeLoader(rtm) runnerModule := runnerModeLoader(rtm)
util.Document(runnerModule, "Runner/exec interface for Hilbish.")
mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule)) mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule))
// hilbish.jobs table // hilbish.jobs table
jobs = newJobHandler() jobs = newJobHandler()
jobModule := jobs.loader(rtm) jobModule := jobs.loader(rtm)
util.Document(jobModule, "(Background) job interface.")
mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule)) mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule))
// hilbish.timers table // hilbish.timers table
timers = newTimerHandler() timers = newTimersModule()
timerModule := timers.loader(rtm) timersModule := timers.loader(rtm)
util.Document(timerModule, "Timer interface, for control of all intervals and timeouts.") mod.Set(rt.StringValue("timers"), rt.TableValue(timersModule))
mod.Set(rt.StringValue("timers"), rt.TableValue(timerModule))
editorModule := editorLoader(rtm) editorModule := editorLoader(rtm)
util.Document(editorModule, "")
mod.Set(rt.StringValue("editor"), rt.TableValue(editorModule)) mod.Set(rt.StringValue("editor"), rt.TableValue(editorModule))
versionModule := rt.NewTable() versionModule := rt.NewTable()
util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch), "Git branch Hilbish was compiled from") util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch))
util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion()), "Full version info, including release name") util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion()))
util.SetField(rtm, versionModule, "commit", rt.StringValue(gitCommit), "Git commit Hilbish was compiled from") util.SetField(rtm, versionModule, "commit", rt.StringValue(gitCommit))
util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName), "Release name") util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName))
util.Document(versionModule, "Version info interface.")
mod.Set(rt.StringValue("version"), rt.TableValue(versionModule)) mod.Set(rt.StringValue("version"), rt.TableValue(versionModule))
pluginModule := moduleLoader(rtm) pluginModule := moduleLoader(rtm)
@ -192,19 +180,21 @@ func getenv(key, fallback string) string {
} }
func setVimMode(mode string) { func setVimMode(mode string) {
util.SetField(l, hshMod, "vimMode", rt.StringValue(mode), "Current Vim mode of Hilbish (nil if not in Vim mode)") util.SetField(l, hshMod, "vimMode", rt.StringValue(mode))
hooks.Emit("hilbish.vimMode", mode) hooks.Emit("hilbish.vimMode", mode)
} }
func unsetVimMode() { func unsetVimMode() {
util.SetField(l, hshMod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)") util.SetField(l, hshMod, "vimMode", rt.NilValue)
} }
// run(cmd, returnOut) -> exitCode, stdout, stderr // run(cmd, returnOut) -> exitCode (number), stdout (string), stderr (string)
// Runs `cmd` in Hilbish's sh interpreter. // Runs `cmd` in Hilbish's sh interpreter.
// If returnOut is true, the outputs of `cmd` will be returned as the 2nd and // If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
// 3rd values instead of being outputted to the terminal. // 3rd values instead of being outputted to the terminal.
// --- @param cmd string // --- @param cmd string
// --- @param returnOut boolean
// --- @returns number, string, string
func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -245,8 +235,9 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitcode)), rt.StringValue(stdoutStr), rt.StringValue(stderrStr)), nil return c.PushingNext(t.Runtime, rt.IntValue(int64(exitcode)), rt.StringValue(stdoutStr), rt.StringValue(stderrStr)), nil
} }
// cwd() // cwd() -> string
// Returns the current directory of the shell // Returns the current directory of the shell
// --- @returns string
func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
cwd, _ := os.Getwd() cwd, _ := os.Getwd()
@ -254,21 +245,28 @@ func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
} }
// read(prompt) -> input? // read(prompt) -> input (string)
// Read input from the user, using Hilbish's line editor/input reader. // Read input from the user, using Hilbish's line editor/input reader.
// This is a separate instance from the one Hilbish actually uses. // 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) // Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
// --- @param prompt string // --- @param prompt? string
// --- @returns string|nil
func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { luaprompt := c.Arg(0)
return nil, err if typ := luaprompt.Type(); typ != rt.StringType && typ != rt.NilType {
return nil, errors.New("expected #1 to be a string")
} }
luaprompt, err := c.StringArg(0) prompt, ok := luaprompt.TryString()
if err != nil { if !ok {
return nil, err // if we are here and `luaprompt` is not a string, it's nil
// substitute with an empty string
prompt = ""
} }
lualr := newLineReader("", true)
lualr.SetPrompt(luaprompt) lualr := &lineReader{
rl: readline.NewInstance(),
}
lualr.SetPrompt(prompt)
input, err := lualr.Read() input, err := lualr.Read()
if err != nil { if err != nil {
@ -279,7 +277,7 @@ func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
} }
/* /*
prompt(str, typ?) prompt(str, typ)
Changes the shell prompt to `str` Changes the shell prompt to `str`
There are a few verbs that can be used in the prompt text. There are a few verbs that can be used in the prompt text.
These will be formatted and replaced with the appropriate values. These will be formatted and replaced with the appropriate values.
@ -287,7 +285,7 @@ These will be formatted and replaced with the appropriate values.
`%u` - Name of current user `%u` - Name of current user
`%h` - Hostname of device `%h` - Hostname of device
--- @param str string --- @param str string
--- @param typ string Type of prompt, being left or right. Left by default. --- @param typ? string Type of prompt, being left or right. Left by default.
*/ */
func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
err := c.Check1Arg() err := c.Check1Arg()
@ -451,12 +449,12 @@ func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// timeout(cb, time) // timeout(cb, time) -> @Timer
// Runs the `cb` function after `time` in milliseconds // Runs the `cb` function after `time` in milliseconds.
// Returns a `timer` object (see `doc timers`). // This creates a timer that starts immediately.
// --- @param cb function // --- @param cb function
// --- @param time number // --- @param time number
// --- @return table // --- @returns Timer
func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil { if err := c.CheckNArgs(2); err != nil {
return nil, err return nil, err
@ -477,12 +475,12 @@ func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil
} }
// interval(cb, time) // interval(cb, time) -> @Timer
// Runs the `cb` function every `time` milliseconds. // Runs the `cb` function every `time` milliseconds.
// Returns a `timer` object (see `doc timers`). // This creates a timer that starts immediately.
// --- @param cb function // --- @param cb function
// --- @param time number // --- @param time number
// --- @return table // --- @return Timer
func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil { if err := c.CheckNArgs(2); err != nil {
return nil, err return nil, err
@ -543,9 +541,11 @@ func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// which(name) // which(name) -> string
// Checks if `name` is a valid command // Checks if `name` is a valid command.
// --- @param binName string // Will return the path of the binary, or a basename if it's a commander.
// --- @param name string
// --- @returns string
func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -555,7 +555,10 @@ func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return nil, err return nil, err
} }
cmd := aliases.Resolve(name) // itll return either the original command or what was passed
// if name isnt empty its not an issue
alias := aliases.Resolve(name)
cmd := strings.Split(alias, " ")[0]
// check for commander // check for commander
if commands[cmd] != nil { if commands[cmd] != nil {
@ -630,7 +633,7 @@ func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
// as the text for the hint. This is by default a shim. To set hints, // as the text for the hint. This is by default a shim. To set hints,
// override this function with your custom handler. // override this function with your custom handler.
// --- @param line string // --- @param line string
// --- @param pos int // --- @param pos number
func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }

View File

@ -7,29 +7,267 @@ import (
"go/doc" "go/doc"
"go/parser" "go/parser"
"go/token" "go/token"
"regexp"
"strings" "strings"
"os" "os"
"sync"
) )
type EmmyPiece struct { var header = `---
FuncName string title: %s %s
Docs []string description: %s
layout: doc
menu:
docs:
parent: "API"
---
`
type emmyPiece struct {
DocPiece *docPiece
Annotations []string
Params []string // we only need to know param name to put in function Params []string // we only need to know param name to put in function
FuncName string
} }
type DocPiece struct {
type module struct {
Types []docPiece
Docs []docPiece
Fields []docPiece
Properties []docPiece
ShortDescription string
Description string
ParentModule string
HasInterfaces bool
HasTypes bool
}
type docPiece struct {
Doc []string Doc []string
FuncSig string FuncSig string
FuncName string FuncName string
Interfacing string
ParentModule string
GoFuncName string
IsInterface bool
IsMember bool
IsType bool
Fields []docPiece
Properties []docPiece
}
type tag struct {
id string
fields []string
}
var docs = make(map[string]module)
var interfaceDocs = make(map[string]module)
var emmyDocs = make(map[string][]emmyPiece)
var typeTable = make(map[string][]string) // [0] = parentMod, [1] = interfaces
var prefix = map[string]string{
"main": "hl",
"hilbish": "hl",
"fs": "f",
"commander": "c",
"bait": "b",
"terminal": "term",
}
func getTagsAndDocs(docs string) (map[string][]tag, []string) {
pts := strings.Split(docs, "\n")
parts := []string{}
tags := make(map[string][]tag)
for _, part := range pts {
if strings.HasPrefix(part, "#") {
tagParts := strings.Split(strings.TrimPrefix(part, "#"), " ")
if tags[tagParts[0]] == nil {
var id string
if len(tagParts) > 1 {
id = tagParts[1]
}
tags[tagParts[0]] = []tag{
{id: id},
}
if len(tagParts) >= 2 {
tags[tagParts[0]][0].fields = tagParts[2:]
}
} else {
fleds := []string{}
if len(tagParts) >= 2 {
fleds = tagParts[2:]
}
tags[tagParts[0]] = append(tags[tagParts[0]], tag{
id: tagParts[1],
fields: fleds,
})
}
} else {
parts = append(parts, part)
}
}
return tags, parts
}
func docPieceTag(tagName string, tags map[string][]tag) []docPiece {
dps := []docPiece{}
for _, tag := range tags[tagName] {
dps = append(dps, docPiece{
FuncName: tag.id,
Doc: tag.fields,
})
}
return dps
}
func setupDocType(mod string, typ *doc.Type) *docPiece {
docs := strings.TrimSpace(typ.Doc)
tags, doc := getTagsAndDocs(docs)
if tags["type"] == nil {
return nil
}
inInterface := tags["interface"] != nil
var interfaces string
typeName := strings.ToUpper(string(typ.Name[0])) + typ.Name[1:]
typeDoc := []string{}
if inInterface {
interfaces = tags["interface"][0].id
}
fields := docPieceTag("field", tags)
properties := docPieceTag("property", tags)
for _, d := range doc {
if strings.HasPrefix(d, "---") {
// TODO: document types in lua
/*
emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
emmyLinePieces := strings.Split(emmyLine, " ")
emmyType := emmyLinePieces[0]
if emmyType == "@param" {
em.Params = append(em.Params, emmyLinePieces[1])
}
if emmyType == "@vararg" {
em.Params = append(em.Params, "...") // add vararg
}
em.Annotations = append(em.Annotations, d)
*/
} else {
typeDoc = append(typeDoc, d)
}
}
var isMember bool
if tags["member"] != nil {
isMember = true
}
parentMod := mod
dps := &docPiece{
Doc: typeDoc,
FuncName: typeName,
Interfacing: interfaces,
IsInterface: inInterface,
IsMember: isMember,
IsType: true,
ParentModule: parentMod,
Fields: fields,
Properties: properties,
}
typeTable[strings.ToLower(typeName)] = []string{parentMod, interfaces}
return dps
}
func setupDoc(mod string, fun *doc.Func) *docPiece {
docs := strings.TrimSpace(fun.Doc)
tags, parts := getTagsAndDocs(docs)
// i couldnt fit this into the condition below for some reason so here's a goto!
if tags["member"] != nil {
goto start
}
if (!strings.HasPrefix(fun.Name, prefix[mod]) && tags["interface"] == nil) || (strings.ToLower(fun.Name) == "loader" && tags["interface"] == nil) {
return nil
}
start:
inInterface := tags["interface"] != nil
var interfaces string
funcsig := parts[0]
doc := parts[1:]
funcName := strings.TrimPrefix(fun.Name, prefix[mod])
funcdoc := []string{}
if inInterface {
interfaces = tags["interface"][0].id
funcName = interfaces + "." + strings.Split(funcsig, "(")[0]
}
em := emmyPiece{FuncName: funcName}
fields := docPieceTag("field", tags)
properties := docPieceTag("property", tags)
for _, d := range doc {
if strings.HasPrefix(d, "---") {
emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
emmyLinePieces := strings.Split(emmyLine, " ")
emmyType := emmyLinePieces[0]
if emmyType == "@param" {
em.Params = append(em.Params, emmyLinePieces[1])
}
if emmyType == "@vararg" {
em.Params = append(em.Params, "...") // add vararg
}
em.Annotations = append(em.Annotations, d)
} else {
funcdoc = append(funcdoc, d)
}
}
var isMember bool
if tags["member"] != nil {
isMember = true
}
var parentMod string
if inInterface {
parentMod = mod
}
dps := &docPiece{
Doc: funcdoc,
FuncSig: funcsig,
FuncName: funcName,
Interfacing: interfaces,
GoFuncName: strings.ToLower(fun.Name),
IsInterface: inInterface,
IsMember: isMember,
ParentModule: parentMod,
Fields: fields,
Properties: properties,
}
if strings.HasSuffix(dps.GoFuncName, strings.ToLower("loader")) {
dps.Doc = parts
}
em.DocPiece = dps
emmyDocs[mod] = append(emmyDocs[mod], em)
return dps
} }
// feel free to clean this up
// it works, dont really care about the code
func main() { func main() {
fset := token.NewFileSet() fset := token.NewFileSet()
os.Mkdir("docs", 0777) os.Mkdir("docs", 0777)
os.Mkdir("docs/api", 0777)
os.Mkdir("emmyLuaDocs", 0777) os.Mkdir("emmyLuaDocs", 0777)
dirs := []string{"./"} dirs := []string{"./"}
filepath.Walk("golibs/", func (path string, info os.FileInfo, err error) error { filepath.Walk("golibs/", func (path string, info os.FileInfo, err error) error {
if !info.IsDir() { if !info.IsDir() {
@ -51,94 +289,182 @@ func main() {
} }
} }
prefix := map[string]string{ interfaceModules := make(map[string]*module)
"hilbish": "hl",
"fs": "f",
"commander": "c",
"bait": "b",
"terminal": "term",
}
docs := make(map[string][]DocPiece)
emmyDocs := make(map[string][]EmmyPiece)
for l, f := range pkgs { for l, f := range pkgs {
p := doc.New(f, "./", doc.AllDecls) p := doc.New(f, "./", doc.AllDecls)
for _, t := range p.Funcs { pieces := []docPiece{}
typePieces := []docPiece{}
mod := l mod := l
if strings.HasPrefix(t.Name, "hl") { mod = "hilbish" } if mod == "main" {
if !strings.HasPrefix(t.Name, prefix[mod]) || t.Name == "Loader" { continue } mod = "hilbish"
parts := strings.Split(strings.TrimSpace(t.Doc), "\n")
funcsig := parts[0]
doc := parts[1:]
funcdoc := []string{}
em := EmmyPiece{FuncName: strings.TrimPrefix(t.Name, prefix[mod])}
for _, d := range doc {
if strings.HasPrefix(d, "---") {
emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
emmyLinePieces := strings.Split(emmyLine, " ")
emmyType := emmyLinePieces[0]
if emmyType == "@param" {
em.Params = append(em.Params, emmyLinePieces[1])
}
if emmyType == "@vararg" {
em.Params = append(em.Params, "...") // add vararg
}
em.Docs = append(em.Docs, d)
} else {
funcdoc = append(funcdoc, d)
} }
var hasInterfaces bool
for _, t := range p.Funcs {
piece := setupDoc(mod, t)
if piece == nil {
continue
} }
dps := DocPiece{ pieces = append(pieces, *piece)
Doc: funcdoc, if piece.IsInterface {
FuncSig: funcsig, hasInterfaces = true
FuncName: strings.TrimPrefix(t.Name, prefix[mod]),
} }
docs[mod] = append(docs[mod], dps)
emmyDocs[mod] = append(emmyDocs[mod], em)
} }
for _, t := range p.Types { for _, t := range p.Types {
for _, m := range t.Methods { typePiece := setupDocType(mod, t)
if !strings.HasPrefix(m.Name, prefix[l]) || m.Name == "Loader" { continue } if typePiece != nil {
parts := strings.Split(strings.TrimSpace(m.Doc), "\n") typePieces = append(typePieces, *typePiece)
funcsig := parts[0] if typePiece.IsInterface {
doc := parts[1:] hasInterfaces = true
funcdoc := []string{}
em := EmmyPiece{FuncName: strings.TrimPrefix(m.Name, prefix[l])}
for _, d := range doc {
if strings.HasPrefix(d, "---") {
emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
emmyLinePieces := strings.Split(emmyLine, " ")
emmyType := emmyLinePieces[0]
if emmyType == "@param" {
em.Params = append(em.Params, emmyLinePieces[1])
} }
if emmyType == "@vararg" {
em.Params = append(em.Params, "...") // add vararg
}
em.Docs = append(em.Docs, d)
} else {
funcdoc = append(funcdoc, d)
}
}
dps := DocPiece{
Doc: funcdoc,
FuncSig: funcsig,
FuncName: strings.TrimPrefix(m.Name, prefix[l]),
} }
docs[l] = append(docs[l], dps) for _, m := range t.Methods {
emmyDocs[l] = append(emmyDocs[l], em) piece := setupDoc(mod, m)
if piece == nil {
continue
}
pieces = append(pieces, *piece)
if piece.IsInterface {
hasInterfaces = true
} }
} }
} }
tags, descParts := getTagsAndDocs(strings.TrimSpace(p.Doc))
shortDesc := descParts[0]
desc := descParts[1:]
filteredPieces := []docPiece{}
filteredTypePieces := []docPiece{}
for _, piece := range pieces {
if !piece.IsInterface {
filteredPieces = append(filteredPieces, piece)
continue
}
modname := piece.ParentModule + "." + piece.Interfacing
if interfaceModules[modname] == nil {
interfaceModules[modname] = &module{
ParentModule: piece.ParentModule,
}
}
if strings.HasSuffix(piece.GoFuncName, strings.ToLower("loader")) {
shortDesc := piece.Doc[0]
desc := piece.Doc[1:]
interfaceModules[modname].ShortDescription = shortDesc
interfaceModules[modname].Description = strings.Join(desc, "\n")
interfaceModules[modname].Fields = piece.Fields
interfaceModules[modname].Properties = piece.Properties
continue
}
interfaceModules[modname].Docs = append(interfaceModules[modname].Docs, piece)
}
for _, piece := range typePieces {
if !piece.IsInterface {
filteredTypePieces = append(filteredTypePieces, piece)
continue
}
modname := piece.ParentModule + "." + piece.Interfacing
if interfaceModules[modname] == nil {
interfaceModules[modname] = &module{
ParentModule: piece.ParentModule,
}
}
interfaceModules[modname].Types = append(interfaceModules[modname].Types, piece)
}
docs[mod] = module{
Types: filteredTypePieces,
Docs: filteredPieces,
ShortDescription: shortDesc,
Description: strings.Join(desc, "\n"),
HasInterfaces: hasInterfaces,
Properties: docPieceTag("property", tags),
Fields: docPieceTag("field", tags),
}
}
for key, mod := range interfaceModules {
docs[key] = *mod
}
var wg sync.WaitGroup
wg.Add(len(docs) * 2)
for mod, v := range docs { for mod, v := range docs {
if mod == "main" { continue } docPath := "docs/api/" + mod + ".md"
f, _ := os.Create("docs/" + mod + ".txt") if v.HasInterfaces {
for _, dps := range v { os.Mkdir("docs/api/" + mod, 0777)
f.WriteString(dps.FuncSig + " > ") os.Remove(docPath) // remove old doc path if it exists
docPath = "docs/api/" + mod + "/_index.md"
}
if v.ParentModule != "" {
docPath = "docs/api/" + v.ParentModule + "/" + mod + ".md"
}
go func(modname, docPath string, modu module) {
defer wg.Done()
modOrIface := "Module"
if modu.ParentModule != "" {
modOrIface = "Interface"
}
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 {
typName := typ[1:]
typLookup := typeTable[strings.ToLower(typName)]
ifaces := typLookup[0] + "." + typLookup[1] + "/"
if typLookup[1] == "" {
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)
})
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")
for _, dps := range modu.Docs {
if dps.IsMember {
continue
}
htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(dps.FuncSig, "<", `\<`, -1), func(typ string) string {
typName := typ[1:]
typLookup := typeTable[strings.ToLower(typName)]
ifaces := typLookup[0] + "." + typLookup[1] + "/"
if typLookup[1] == "" {
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)
})
f.WriteString(fmt.Sprintf("### %s\n", htmlSig))
for _, doc := range dps.Doc { for _, doc := range dps.Doc {
if !strings.HasPrefix(doc, "---") { if !strings.HasPrefix(doc, "---") {
f.WriteString(doc + "\n") f.WriteString(doc + "\n")
@ -148,23 +474,81 @@ func main() {
} }
} }
for mod, v := range emmyDocs { if len(modu.Types) != 0 {
if mod == "main" { continue } f.WriteString("## Types\n")
f, _ := os.Create("emmyLuaDocs/" + mod + ".lua") for _, dps := range modu.Types {
f.WriteString("--- @meta\n\nlocal " + mod + " = {}\n\n") f.WriteString(fmt.Sprintf("## %s\n", dps.FuncName))
for _, em := range v { for _, doc := range dps.Doc {
var funcdocs []string if !strings.HasPrefix(doc, "---") {
for _, dps := range docs[mod] { f.WriteString(doc + "\n")
if dps.FuncName == em.FuncName {
funcdocs = dps.Doc
} }
} }
f.WriteString("--- " + strings.Join(funcdocs, "\n--- ") + "\n") if len(dps.Properties) != 0 {
if len(em.Docs) != 0 { f.WriteString("### Properties\n")
f.WriteString(strings.Join(em.Docs, "\n") + "\n") for _, dps := range dps.Properties {
f.WriteString(fmt.Sprintf("- `%s`: ", dps.FuncName))
f.WriteString(strings.Join(dps.Doc, " "))
f.WriteString("\n")
} }
f.WriteString("function " + mod + "." + em.FuncName + "(" + strings.Join(em.Params, ", ") + ") end\n\n")
} }
f.WriteString("return " + mod + "\n") f.WriteString("\n")
f.WriteString("### Methods\n")
for _, dps := range modu.Docs {
if !dps.IsMember {
continue
} }
htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(dps.FuncSig, "<", `\<`, -1), func(typ string) string {
typName := regexp.MustCompile(`\w+`).FindString(typ[1:])
typLookup := typeTable[strings.ToLower(typName)]
fmt.Printf("%+q, \n", typLookup)
linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s/#%s", typLookup[0], typLookup[0] + "." + typLookup[1], strings.ToLower(typName))
return fmt.Sprintf(`<a href="#%s" style="text-decoration: none;">%s</a>`, linkedTyp, typName)
})
f.WriteString(fmt.Sprintf("#### %s\n", htmlSig))
for _, doc := range dps.Doc {
if !strings.HasPrefix(doc, "---") {
f.WriteString(doc + "\n")
}
}
f.WriteString("\n")
}
}
}
}(mod, docPath, v)
go func(md, modname string, modu module) {
defer wg.Done()
if modu.ParentModule != "" {
return
}
ff, _ := os.Create("emmyLuaDocs/" + modname + ".lua")
ff.WriteString("--- @meta\n\nlocal " + modname + " = {}\n\n")
for _, em := range emmyDocs[modname] {
if strings.HasSuffix(em.DocPiece.GoFuncName, strings.ToLower("loader")) {
continue
}
dps := em.DocPiece
funcdocs := dps.Doc
ff.WriteString("--- " + strings.Join(funcdocs, "\n--- ") + "\n")
if len(em.Annotations) != 0 {
ff.WriteString(strings.Join(em.Annotations, "\n") + "\n")
}
accessor := "."
if dps.IsMember {
accessor = ":"
}
signature := strings.Split(dps.FuncSig, " ->")[0]
var intrface string
if dps.IsInterface {
intrface = "." + dps.Interfacing
}
ff.WriteString("function " + modname + intrface + accessor + signature + " end\n\n")
}
ff.WriteString("return " + modname + "\n")
}(mod, mod, v)
}
wg.Wait()
} }

View File

@ -11,15 +11,49 @@ import (
rt "github.com/arnodel/golua/runtime" rt "github.com/arnodel/golua/runtime"
) )
func splitQuote(str string) []string { var charEscapeMap = []string{
"\"", "\\\"",
"'", "\\'",
"`", "\\`",
" ", "\\ ",
"(", "\\(",
")", "\\)",
"[", "\\[",
"]", "\\]",
"$", "\\$",
"&", "\\&",
"*", "\\*",
">", "\\>",
"<", "\\<",
"|", "\\|",
}
var charEscapeMapInvert = invert(charEscapeMap)
var escapeReplaer = strings.NewReplacer(charEscapeMap...)
var escapeInvertReplaer = strings.NewReplacer(charEscapeMapInvert...)
func invert(m []string) []string {
newM := make([]string, len(charEscapeMap))
for i := range m {
if (i + 1) % 2 == 0 {
newM[i] = m[i - 1]
newM[i - 1] = m[i]
}
}
return newM
}
func splitForFile(str string) []string {
split := []string{} split := []string{}
sb := &strings.Builder{} sb := &strings.Builder{}
quoted := false quoted := false
for _, r := range str { for i, r := range str {
if r == '"' { if r == '"' {
quoted = !quoted quoted = !quoted
sb.WriteRune(r) sb.WriteRune(r)
} else if r == ' ' && str[i - 1] == '\\' {
sb.WriteRune(r)
} else if !quoted && r == ' ' { } else if !quoted && r == ' ' {
split = append(split, sb.String()) split = append(split, sb.String())
sb.Reset() sb.Reset()
@ -39,12 +73,22 @@ func splitQuote(str string) []string {
} }
func fileComplete(query, ctx string, fields []string) ([]string, string) { func fileComplete(query, ctx string, fields []string) ([]string, string) {
q := splitQuote(ctx) q := splitForFile(ctx)
path := ""
if len(q) != 0 {
path = q[len(q) - 1]
}
return matchPath(q[len(q) - 1]) return matchPath(path)
} }
func binaryComplete(query, ctx string, fields []string) ([]string, string) { func binaryComplete(query, ctx string, fields []string) ([]string, string) {
q := splitForFile(ctx)
query = ""
if len(q) != 0 {
query = q[len(q) - 1]
}
var completions []string var completions []string
prefixes := []string{"./", "../", "/", "~/"} prefixes := []string{"./", "../", "/", "~/"}
@ -54,7 +98,7 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
if len(fileCompletions) != 0 { if len(fileCompletions) != 0 {
for _, f := range fileCompletions { for _, f := range fileCompletions {
fullPath, _ := filepath.Abs(util.ExpandHome(query + strings.TrimPrefix(f, filePref))) fullPath, _ := filepath.Abs(util.ExpandHome(query + strings.TrimPrefix(f, filePref)))
if err := findExecutable(fullPath, false, true); err != nil { if err := findExecutable(escapeInvertReplaer.Replace(fullPath), false, true); err != nil {
continue continue
} }
completions = append(completions, f) completions = append(completions, f)
@ -66,7 +110,6 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
// filter out executables, but in path // filter out executables, but in path
for _, dir := range filepath.SplitList(os.Getenv("PATH")) { for _, dir := range filepath.SplitList(os.Getenv("PATH")) {
// print dir to stderr for debugging
// search for an executable which matches our query string // search for an executable which matches our query string
if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil { if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil {
// get basename from matches // get basename from matches
@ -102,6 +145,7 @@ func matchPath(query string) ([]string, string) {
var entries []string var entries []string
var baseName string var baseName string
query = escapeInvertReplaer.Replace(query)
path, _ := filepath.Abs(util.ExpandHome(filepath.Dir(query))) path, _ := filepath.Abs(util.ExpandHome(filepath.Dir(query)))
if string(query) == "" { if string(query) == "" {
// filepath base below would give us "." // filepath base below would give us "."
@ -112,7 +156,16 @@ func matchPath(query string) ([]string, string) {
} }
files, _ := os.ReadDir(path) files, _ := os.ReadDir(path)
for _, file := range files { for _, entry := range files {
// should we handle errors here?
file, err := entry.Info()
if err == nil && file.Mode() & os.ModeSymlink != 0 {
path, err := filepath.EvalSymlinks(filepath.Join(path, file.Name()))
if err == nil {
file, err = os.Lstat(path)
}
}
if strings.HasPrefix(file.Name(), baseName) { if strings.HasPrefix(file.Name(), baseName) {
entry := file.Name() entry := file.Name()
if file.IsDir() { if file.IsDir() {
@ -124,32 +177,20 @@ func matchPath(query string) ([]string, string) {
entries = append(entries, entry) entries = append(entries, entry)
} }
} }
if !strings.HasPrefix(oldQuery, "\"") {
baseName = escapeFilename(baseName)
}
return entries, baseName return entries, baseName
} }
func escapeFilename(fname string) string { func escapeFilename(fname string) string {
args := []string{ return escapeReplaer.Replace(fname)
"\"", "\\\"",
"'", "\\'",
"`", "\\`",
" ", "\\ ",
"(", "\\(",
")", "\\)",
"[", "\\[",
"]", "\\]",
"$", "\\$",
"&", "\\&",
"*", "\\*",
">", "\\>",
"<", "\\<",
"|", "\\|",
}
r := strings.NewReplacer(args...)
return r.Replace(fname)
} }
// #interface completions
// tab completions
// The completions interface deals with tab completions.
func completionLoader(rtm *rt.Runtime) *rt.Table { func completionLoader(rtm *rt.Runtime) *rt.Table {
exports := map[string]util.LuaExport{ exports := map[string]util.LuaExport{
"files": {luaFileComplete, 3, false}, "files": {luaFileComplete, 3, false},
@ -164,11 +205,26 @@ func completionLoader(rtm *rt.Runtime) *rt.Table {
return mod return mod
} }
// left as a shim, might doc in the same way as hilbish functions // #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) { func completionHandler(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// #interface completions
// 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) { func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(4); err != nil { if err := c.CheckNArgs(4); err != nil {
return nil, err return nil, err
@ -208,6 +264,12 @@ func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, completerReturn), nil return c.PushingNext1(t.Runtime, completerReturn), nil
} }
// #interface completions
// 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) { func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
query, ctx, fds, err := getCompleteParams(t, c) query, ctx, fds, err := getCompleteParams(t, c)
if err != nil { if err != nil {
@ -224,6 +286,12 @@ func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil 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) { func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
query, ctx, fds, err := getCompleteParams(t, c) query, ctx, fds, err := getCompleteParams(t, c)
if err != nil { if err != nil {

View File

@ -0,0 +1,9 @@
---
title: API
layout: doc
weight: -50
menu: docs
---
Welcome to the API documentation for Hilbish. This documents Lua functions
provided by Hilbish.

34
docs/api/bait.md 100644
View File

@ -0,0 +1,34 @@
---
title: Module bait
description: the event emitter
layout: doc
menu:
docs:
parent: "API"
---
## 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.
## Functions
### catch(name, cb)
Catches a hook with `name`. Runs the `cb` when it is thrown
### catchOnce(name, cb)
Same as catch, but only runs the `cb` once and then removes the hook
### hooks(name) -> table
Returns a table with hooks (callback functions) on the event with `name`.
### 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.
### throw(name, ...args)
Throws a hook with `name` with the provided `args`

View File

@ -0,0 +1,46 @@
---
title: Module commander
description: library for custom commands
layout: doc
menu:
docs:
parent: "API"
---
## 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:
```lua
local commander = require 'commander'
commander.register('hello', function(args, sinks)
sinks.out:writeln 'Hello world!'
end)
```
In this example, a command with the name of `hello` is created
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 <a href="/Hilbish/docs/api/hilbish/#sink" style="text-decoration: none;">Sink</a>.
- `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.
## Functions
### deregister(name)
Deregisters any command registered with `name`
### register(name, cb)
Register a command with `name` that runs `cb` when ran

51
docs/api/fs.md 100644
View File

@ -0,0 +1,51 @@
---
title: Module fs
description: filesystem interaction and functionality library
layout: doc
menu:
docs:
parent: "API"
---
## 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.
## Functions
### abs(path) -> string
Gives an absolute version of `path`.
### basename(path) -> string
Gives the basename of `path`. For the rules,
see Go's filepath.Base
### cd(dir)
Changes directory to `dir`
### dir(path) -> string
Returns the directory part of `path`. For the rules, see Go's
filepath.Dir
### glob(pattern) -> matches (table)
Glob all files and directories that match the pattern.
For the rules, see Go's filepath.Glob
### 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`.
### 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

View File

@ -0,0 +1,117 @@
---
title: Module hilbish
description: the core Hilbish API
layout: doc
menu:
docs:
parent: "API"
---
## Introduction
The Hilbish module includes the core API, containing
interfaces and functions which directly relate to shell functionality.
## Interface fields
- `ver`: The version of Hilbish
- `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`
### appendPath(dir)
Appends `dir` to $PATH
### complete(scope, cb)
Registers a completion handler for `scope`.
A `scope` is currently only 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.
### cwd() -> string
Returns the current directory of the shell
### exec(cmd)
Replaces running hilbish with `cmd`
### goro(fn)
Puts `fn` in a goroutine
### 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.
### 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.
### inputMode(mode)
Sets the input mode for Hilbish's line reader. Accepts either emacs or vim
### interval(cb, time) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;">Timer</a>
Runs the `cb` function every `time` milliseconds.
This creates a timer that starts immediately.
### multiprompt(str)
Changes the continued line prompt to `str`
### prependPath(dir)
Prepends `dir` to $PATH
### 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
### 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)
### 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.
### 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.
### timeout(cb, time) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;">Timer</a>
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.
## Types
## Sink
A sink is a structure that has input and/or output to/from
a desination.
### Methods
#### write(str)
Writes data to a sink.
#### writeln(str)
Writes data to a sink with a newline at the end.

View File

@ -0,0 +1,25 @@
---
title: Interface hilbish.aliases
description: command aliasing
layout: doc
menu:
docs:
parent: "API"
---
## Introduction
The alias interface deals with all command aliases in Hilbish.
## Functions
### add(alias, cmd)
This is an alias (ha) for the `hilbish.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) -> command (string)
Tries to resolve an alias to its command.

View File

@ -0,0 +1,29 @@
---
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.

View File

@ -0,0 +1,26 @@
---
title: Interface hilbish.editor
description: interactions for Hilbish's line reader
layout: doc
menu:
docs:
parent: "API"
---
## Introduction
The hilbish.editor interface provides functions to
directly interact with the line editor in use.
## Functions
### getLine() -> string
Returns the current input line.
### getVimRegister(register) -> string
Returns the text that is at the register.
### insert(text)
Inserts text into the line.
### setVimRegister(register, text)
Sets the vim register at `register` to hold the passed text.

View File

@ -0,0 +1,30 @@
---
title: Interface hilbish.history
description: command history
layout: doc
menu:
docs:
parent: "API"
---
## Introduction
The history interface deals with command history.
This includes the ability to override functions to change the main
method of saving history.
## Functions
### add(cmd)
Adds a command to the history.
### all() -> table
Retrieves all history.
### clear()
Deletes all commands from the history.
### get(idx)
Retrieves a command from the history based on the `idx`.
### size() -> number
Returns the amount of commands in the history.

View File

@ -0,0 +1,58 @@
---
title: Interface hilbish.jobs
description: background job management
layout: doc
menu:
docs:
parent: "API"
---
## Introduction
Manage interactive jobs in Hilbish via Lua.
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.
### all() -> table\<<a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;">Job</a>>
Returns a table of all job objects.
### disown(id)
Disowns a job. This deletes it from the job table.
### get(id) -> <a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;">Job</a>
Get a job object via its ID.
### last() -> <a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;">Job</a>
Returns the last added job from the table.
## 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.
### Methods
#### background()
Puts a job in the background. This acts the same as initially running a job.
#### foreground()
Puts a job in the foreground. This will cause it to run like it was
executed normally and wait for it to complete.
#### start()
Starts running the job.
#### stop()
Stops the job from running.

View File

@ -0,0 +1,19 @@
---
title: Interface hilbish.os
description: OS Info
layout: doc
menu:
docs:
parent: "API"
---
## Introduction
The `os` interface provides simple text information properties about
the current OS on the systen. 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

View File

@ -0,0 +1,31 @@
---
title: Interface hilbish.runner
description: interactive command runner customization
layout: doc
menu:
docs:
parent: "API"
---
## Introduction
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.
## 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.
### lua(cmd)
Evaluates `cmd` as Lua input. This is the same as using `dofile`
or `load`, but is appropriated for the runner interface.
### sh(cmd)
Runs a command in Hilbish's shell script interpreter.
This is the equivalent of using `source`.

View File

@ -0,0 +1,59 @@
---
title: Interface hilbish.timers
description: timeout and interval API
layout: doc
menu:
docs:
parent: "API"
---
## Introduction
If you ever want to run a piece of code on a timed interval, or want to wait
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.
An example of usage:
```
local t = hilbish.timers.create(hilbish.timers.TIMEOUT, 5000, function()
print 'hello!'
end)
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) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;">Timer</a>
Creates a timer that runs based on the specified `time` in milliseconds.
The `type` can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT`
### get(id) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;">Timer</a>
Retrieves a timer via its 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
### Methods
#### start()
Starts a timer.
#### stop()
Stops a timer.

View File

@ -0,0 +1,18 @@
---
title: Interface hilbish.userDir
description: user-related directories
layout: doc
menu:
docs:
parent: "API"
---
## Introduction
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

View File

@ -0,0 +1,26 @@
---
title: Module terminal
description: low level terminal library
layout: doc
menu:
docs:
parent: "API"
---
## Introduction
The terminal library is a simple and lower level library for certain terminal interactions.
## Functions
### restoreState()
Restores the last saved state of the terminal
### saveState()
Saves the current state of the terminal
### setRaw()
Puts the terminal in raw mode
### 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

View File

@ -1,10 +0,0 @@
catch(name, cb) > Catches a hook with `name`. Runs the `cb` when it is thrown
catchOnce(name, cb) > Same as catch, but only runs the `cb` once and then removes the hook
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.
throw(name, ...args) > Throws a hook with `name` with the provided `args`

View File

@ -1,4 +0,0 @@
deregister(name) > Deregisters any command registered with `name`
register(name, cb) > Register a command with `name` that runs `cb` when ran

View File

@ -0,0 +1,56 @@
Hilbish has a pretty good completion system. It has a nice looking
menu, with 2 types of menus: grid (like file completions) or
list.
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.
# Completion Handler
By default, it provides 3 things: for the first argument,
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.
To overwrite it, just assign a function to
`hilbish.completion.handler` like so:
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.<name>`

View File

@ -1,44 +0,0 @@
Hilbish has a pretty good completion system. It has a nice looking menu,
with 2 types of menus: grid (like file completions) or list.
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.
# Completion Handler
By default, it provides 3 things: for the first argument, 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.
To overwrite it, just assign a function to `hilbish.completion.handler`
like so:
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, and `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.<name>`

View File

@ -1,22 +0,0 @@
abs(path) > Gives an absolute version of `path`.
basename(path) > Gives the basename of `path`. For the rules,
see Go's filepath.Base
cd(dir) > Changes directory to `dir`
dir(path) > Returns the directory part of `path`. For the rules, see Go's
filepath.Dir
glob(pattern) > Glob all files and directories that match the pattern.
For the rules, see Go's filepath.Glob
join(paths...) > 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`
stat(path) > Returns info about `path`

View File

@ -1,62 +0,0 @@
alias(cmd, orig) > Sets an alias of `cmd` to `orig`
appendPath(dir) > Appends `dir` to $PATH
complete(scope, cb) > Registers a completion handler for `scope`.
A `scope` is currently only 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.
cwd() > Returns the current directory of the shell
exec(cmd) > Replaces running hilbish with `cmd`
goro(fn) > Puts `fn` in a goroutine
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.
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.
inputMode(mode) > Sets the input mode for Hilbish's line reader. Accepts either emacs or vim
interval(cb, time) > Runs the `cb` function every `time` milliseconds.
Returns a `timer` object (see `doc timers`).
multiprompt(str) > Changes the continued line prompt to `str`
prependPath(dir) > Prepends `dir` to $PATH
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
read(prompt) -> input? > 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)
run(cmd, returnOut) -> exitCode, stdout, stderr > 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.
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.
timeout(cb, time) > Runs the `cb` function after `time` in milliseconds
Returns a `timer` object (see `doc timers`).
which(name) > Checks if `name` is a valid command

View File

@ -6,3 +6,8 @@ Here is the format for a doc for a hook:
`<args>` just means the arguments of the hook. If a hook doc has the format `<args>` 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`. 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.

View File

@ -3,5 +3,5 @@
+ `command.not-found` -> cmdStr > Thrown when a command is not found. + `command.not-found` -> cmdStr > Thrown when a command is not found.
+ `command.no-perm` -> cmdStr > Thrown when Hilbish attempts to execute a file but + `command.not-executable` -> cmdStr > Thrown when Hilbish attempts to run a file
has no permission. that is not executable.

View File

@ -5,3 +5,5 @@
+ `hilbish.vimAction` -> actionName, args > Sent when the user does a "vim action," being something + `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. 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.

View File

@ -38,8 +38,22 @@ The exit code has to be a number, it will be 0 otherwise and the error can be
These are the "low level" functions for the `hilbish.runner` interface. These are the "low level" functions for the `hilbish.runner` interface.
+ setMode(mode) > The same as `hilbish.runnerMode` + setMode(mode) > The same as `hilbish.runnerMode`
+ sh(input) -> input, code, err > Runs `input` in Hilbish's sh interpreter + sh(input) -> table > Runs `input` in Hilbish's sh interpreter
+ lua(input) -> input, code, err > Evals `input` as Lua code + 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.
+ `<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.
The others here are defined in Lua and have EmmyLua documentation. The others here are defined in Lua and have EmmyLua documentation.
These functions should be preferred over the previous ones. These functions should be preferred over the previous ones.

View File

@ -1,9 +0,0 @@
restoreState() > Restores the last saved state of the terminal
saveState() > Saves the current state of the terminal
setRaw() > Puts the terminal in raw mode
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

1
docs/timers.md 100644
View File

@ -0,0 +1 @@
This has been moved to the `hilbish.timers` API doc (accessible by `doc api hilbish.timers`)

View File

@ -1,38 +0,0 @@
If you ever want to run a piece of code on a timed interval, or want to wait
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.
# Timer Interface
## Functions
- `get(id)` -> timer: get a timer via its id
- `create(type, ms, callback)` -> timer: creates a timer, adding it to the timer pool.
`type` is the type of timer it will be. 0 is an interval, 1 is a timeout.
`ms` is the time it will run for in seconds. callback is the function called
when the timer is triggered.
# Timer Object
All those previously mentioned functions return a `timer` object, to which
you can stop and start a timer again.
An example of usage:
local t = hilbish.timers.create(1, 5000, function()
print 'hello!'
end)
t:stop()
print(t.running, t.duration, t.type)
t:start()
## Properties
- `duration`: amount of time the timer runs for in milliseconds
- `running`: whether the timer is running or not
- `type`: the type of timer (0 is interval, 1 is timeout)
## Functions
- `stop()`: stops the timer. returns an error if it's already stopped
- `start()`: starts the timer. returns an error if it's already started

View File

@ -6,6 +6,10 @@ import (
rt "github.com/arnodel/golua/runtime" rt "github.com/arnodel/golua/runtime"
) )
// #interface editor
// interactions for Hilbish's line reader
// The hilbish.editor interface provides functions to
// directly interact with the line editor in use.
func editorLoader(rtm *rt.Runtime) *rt.Table { func editorLoader(rtm *rt.Runtime) *rt.Table {
exports := map[string]util.LuaExport{ exports := map[string]util.LuaExport{
"insert": {editorInsert, 1, false}, "insert": {editorInsert, 1, false},
@ -20,6 +24,9 @@ func editorLoader(rtm *rt.Runtime) *rt.Table {
return mod return mod
} }
// #interface editor
// insert(text)
// Inserts text into the line.
func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -35,6 +42,11 @@ func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// #interface editor
// setVimRegister(register, text)
// Sets the vim register at `register` to hold the passed text.
// --- @param register string
// --- @param text string
func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -55,6 +67,10 @@ func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// #interface editor
// getVimRegister(register) -> string
// Returns the text that is at the register.
// --- @param register string
func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -70,6 +86,9 @@ func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
} }
// #interface editor
// getLine() -> string
// Returns the current input line.
func editorGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func editorGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
buf := lr.rl.GetLine() buf := lr.rl.GetLine()

View File

@ -12,14 +12,21 @@ function bait.catch(name, cb) end
--- @param cb function --- @param cb function
function bait.catchOnce(name, cb) end function bait.catchOnce(name, cb) end
--- Removes the `catcher` for the event with `name` --- Returns a table with hooks (callback functions) on the event with `name`.
--- @param name string
--- @returns table<function>
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 --- For this to work, `catcher` has to be the same function used to catch
--- an event, like one saved to a variable. --- an event, like one saved to a variable.
function bait.release() end --- @param name string
--- @param catcher function
function bait.release(name, catcher) end
--- Throws a hook with `name` with the provided `args` --- Throws a hook with `name` with the provided `args`
--- @param name string --- @param name string
--- @vararg any --- @vararg any
function bait.throw(name, ...) end function bait.throw(name, ...args) end
return bait return bait

View File

@ -4,11 +4,13 @@ local fs = {}
--- Gives an absolute version of `path`. --- Gives an absolute version of `path`.
--- @param path string --- @param path string
--- @returns string
function fs.abs(path) end function fs.abs(path) end
--- Gives the basename of `path`. For the rules, --- Gives the basename of `path`. For the rules,
--- see Go's filepath.Base --- see Go's filepath.Base
function fs.basename() end --- @returns string
function fs.basename(path) end
--- Changes directory to `dir` --- Changes directory to `dir`
--- @param dir string --- @param dir string
@ -16,28 +18,40 @@ function fs.cd(dir) end
--- Returns the directory part of `path`. For the rules, see Go's --- Returns the directory part of `path`. For the rules, see Go's
--- filepath.Dir --- filepath.Dir
function fs.dir() end --- @param path string
--- @returns string
function fs.dir(path) end
--- Glob all files and directories that match the pattern. --- Glob all files and directories that match the pattern.
--- For the rules, see Go's filepath.Glob --- For the rules, see Go's filepath.Glob
function fs.glob() end --- @param pattern string
--- @returns table
function fs.glob(pattern) end
--- Takes paths and joins them together with the OS's --- Takes paths and joins them together with the OS's
--- directory separator (forward or backward slash). --- directory separator (forward or backward slash).
function fs.join() end --- @vararg string
--- @returns string
function fs.join(...) end
--- Makes a directory called `name`. If `recursive` is true, it will create its parent directories. --- Makes a directory called `name`. If `recursive` is true, it will create its parent directories.
--- @param name string --- @param name string
--- @param recursive boolean --- @param recursive boolean
function fs.mkdir(name, recursive) end function fs.mkdir(name, recursive) end
--- Returns a table of files in `dir` --- Returns a table of files in `dir`.
--- @param dir string --- @param dir string
--- @return table --- @return table
function fs.readdir(dir) end function fs.readdir(dir) end
--- Returns info about `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
--- @param path string --- @param path string
--- @returns table
function fs.stat(path) end function fs.stat(path) end
return fs return fs

View File

@ -2,6 +2,49 @@
local hilbish = {} local hilbish = {}
--- This is an alias (ha) for the `hilbish.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.
--- 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.completions.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.completions.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.
function hilbish.editor.insert(text) 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` --- Sets an alias of `cmd` to `orig`
--- @param cmd string --- @param cmd string
--- @param orig string --- @param orig string
@ -21,6 +64,7 @@ function hilbish.appendPath(dir) end
function hilbish.complete(scope, cb) end function hilbish.complete(scope, cb) end
--- Returns the current directory of the shell --- Returns the current directory of the shell
--- @returns string
function hilbish.cwd() end function hilbish.cwd() end
--- Replaces running hilbish with `cmd` --- Replaces running hilbish with `cmd`
@ -44,7 +88,7 @@ function hilbish.highlighter(line) end
--- as the text for the hint. This is by default a shim. To set hints, --- as the text for the hint. This is by default a shim. To set hints,
--- override this function with your custom handler. --- override this function with your custom handler.
--- @param line string --- @param line string
--- @param pos int --- @param pos number
function hilbish.hinter(line, pos) end function hilbish.hinter(line, pos) end
--- Sets the input mode for Hilbish's line reader. Accepts either emacs or vim --- Sets the input mode for Hilbish's line reader. Accepts either emacs or vim
@ -52,10 +96,10 @@ function hilbish.hinter(line, pos) end
function hilbish.inputMode(mode) end function hilbish.inputMode(mode) end
--- Runs the `cb` function every `time` milliseconds. --- Runs the `cb` function every `time` milliseconds.
--- Returns a `timer` object (see `doc timers`). --- This creates a timer that starts immediately.
--- @param cb function --- @param cb function
--- @param time number --- @param time number
--- @return table --- @return Timer
function hilbish.interval(cb, time) end function hilbish.interval(cb, time) end
--- Changes the continued line prompt to `str` --- Changes the continued line prompt to `str`
@ -73,20 +117,23 @@ function hilbish.prependPath(dir) end
--- `%u` - Name of current user --- `%u` - Name of current user
--- `%h` - Hostname of device --- `%h` - Hostname of device
--- @param str string --- @param str string
--- @param typ string Type of prompt, being left or right. Left by default. --- @param typ? string Type of prompt, being left or right. Left by default.
function hilbish.prompt(str, typ) end function hilbish.prompt(str, typ) end
--- Read input from the user, using Hilbish's line editor/input reader. --- Read input from the user, using Hilbish's line editor/input reader.
--- This is a separate instance from the one Hilbish actually uses. --- 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) --- Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
--- @param prompt string --- @param prompt? string
--- @returns string|nil
function hilbish.read(prompt) end function hilbish.read(prompt) end
--- Runs `cmd` in Hilbish's sh interpreter. --- Runs `cmd` in Hilbish's sh interpreter.
--- If returnOut is true, the outputs of `cmd` will be returned as the 2nd and --- If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
--- 3rd values instead of being outputted to the terminal. --- 3rd values instead of being outputted to the terminal.
--- @param cmd string --- @param cmd string
function hilbish.run(cmd) end --- @param returnOut boolean
--- @returns number, string, string
function hilbish.run(cmd, returnOut) end
--- Sets the execution/runner mode for interactive Hilbish. This determines whether --- 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. --- Hilbish wll try to run input as Lua and/or sh or only do one of either.
@ -96,15 +143,131 @@ function hilbish.run(cmd) end
--- @param mode string|function --- @param mode string|function
function hilbish.runnerMode(mode) end function hilbish.runnerMode(mode) end
--- Runs the `cb` function after `time` in milliseconds --- Runs the `cb` function after `time` in milliseconds.
--- Returns a `timer` object (see `doc timers`). --- This creates a timer that starts immediately.
--- @param cb function --- @param cb function
--- @param time number --- @param time number
--- @return table --- @returns Timer
function hilbish.timeout(cb, time) end function hilbish.timeout(cb, time) end
--- Checks if `name` is a valid command --- Checks if `name` is a valid command.
--- @param binName string --- Will return the path of the binary, or a basename if it's a commander.
function hilbish.which(binName) end --- @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.completions.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.completions.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
--- Writes data to a sink.
function hilbish:write(str) end
--- Writes data to a sink with a newline at the end.
function hilbish:writeln(str) end
--- Starts running the job.
function hilbish.jobs:start() end
--- Stops the job from running.
function hilbish.jobs:stop() 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.
function hilbish.timers:start() end
--- Stops a timer.
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<string, string>
function hilbish.aliases.list() end
--- Tries to resolve an alias to its command.
--- @param alias string
--- @returns string
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
function hilbish.jobs.add(cmdstr, args, execPath) end
--- Returns a table of all job objects.
--- @returns table<Job>
function hilbish.jobs.all() end
--- Disowns a job. This deletes it from the job table.
--- @param id number
function hilbish.jobs.disown(id) end
--- Get a job object via its ID.
--- @param id number
--- @returns Job
function hilbish.jobs.get(id) end
--- Returns the last added job from the table.
--- @returns Job
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
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
--- 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
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 return hilbish

32
exec.go
View File

@ -96,23 +96,23 @@ func runInput(input string, priv bool) {
if currentRunner.Type() == rt.StringType { if currentRunner.Type() == rt.StringType {
switch currentRunner.AsString() { switch currentRunner.AsString() {
case "hybrid": case "hybrid":
_, _, err = handleLua(cmdString) _, _, err = handleLua(input)
if err == nil { if err == nil {
cmdFinish(0, input, priv) cmdFinish(0, input, priv)
return return
} }
input, exitCode, cont, err = handleSh(cmdString) input, exitCode, cont, err = handleSh(input)
case "hybridRev": case "hybridRev":
_, _, _, err = handleSh(input) _, _, _, err = handleSh(input)
if err == nil { if err == nil {
cmdFinish(0, input, priv) cmdFinish(0, input, priv)
return return
} }
input, exitCode, err = handleLua(cmdString) input, exitCode, err = handleLua(input)
case "lua": case "lua":
input, exitCode, err = handleLua(cmdString) input, exitCode, err = handleLua(input)
case "sh": case "sh":
input, exitCode, cont, err = handleSh(cmdString) input, exitCode, cont, err = handleSh(input)
} }
} else { } else {
// can only be a string or function so // can only be a string or function so
@ -141,10 +141,10 @@ func runInput(input string, priv bool) {
if err != nil { if err != nil {
if exErr, ok := isExecError(err); ok { if exErr, ok := isExecError(err); ok {
hooks.Emit("command." + exErr.typ, exErr.cmd) hooks.Emit("command." + exErr.typ, exErr.cmd)
err = exErr.sprint() } else {
}
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
} }
}
cmdFinish(exitCode, input, priv) cmdFinish(exitCode, input, priv)
} }
@ -195,7 +195,8 @@ func runLuaRunner(runr rt.Value, userInput string) (input string, exitCode uint8
return return
} }
func handleLua(cmdString string) (string, uint8, error) { func handleLua(input string) (string, uint8, error) {
cmdString := aliases.Resolve(input)
// First try to load input, essentially compiling to bytecode // First try to load input, essentially compiling to bytecode
chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv())) chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv()))
if err != nil && noexecute { if err != nil && noexecute {
@ -322,8 +323,18 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str)) luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str))
} }
hc := interp.HandlerCtx(ctx)
if commands[args[0]] != nil { if commands[args[0]] != nil {
luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs)) stdin := newSinkInput(hc.Stdin)
stdout := newSinkOutput(hc.Stdout)
stderr := newSinkOutput(hc.Stderr)
sinks := rt.NewTable()
sinks.Set(rt.StringValue("in"), rt.UserDataValue(stdin.ud))
sinks.Set(rt.StringValue("out"), rt.UserDataValue(stdout.ud))
sinks.Set(rt.StringValue("err"), rt.UserDataValue(stderr.ud))
luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs), rt.TableValue(sinks))
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error()) fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error())
return interp.NewExitStatus(1) return interp.NewExitStatus(1)
@ -363,7 +374,6 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
killTimeout := 2 * time.Second killTimeout := 2 * time.Second
// from here is basically copy-paste of the default exec handler from // from here is basically copy-paste of the default exec handler from
// sh/interp but with our job handling // sh/interp but with our job handling
hc := interp.HandlerCtx(ctx)
path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0]) path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0])
if err != nil { if err != nil {
fmt.Fprintln(hc.Stderr, err) fmt.Fprintln(hc.Stderr, err)
@ -551,7 +561,7 @@ func splitInput(input string) ([]string, string) {
} }
func cmdFinish(code uint8, cmdstr string, private bool) { func cmdFinish(code uint8, cmdstr string, private bool) {
util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code)), "Exit code of last exected command") util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code)))
// using AsValue (to convert to lua type) on an interface which is an int // using AsValue (to convert to lua type) on an interface which is an int
// results in it being unknown in lua .... ???? // results in it being unknown in lua .... ????
// so we allow the hook handler to take lua runtime Values // so we allow the hook handler to take lua runtime Values

BIN
gallery/tab.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

16
go.mod
View File

@ -3,11 +3,8 @@ module hilbish
go 1.17 go 1.17
require ( require (
github.com/Rosettea/Malvales v0.0.0-20220707042337-37a9ee7758f9
github.com/arnodel/golua v0.0.0-20220221163911-dfcf252b6f86 github.com/arnodel/golua v0.0.0-20220221163911-dfcf252b6f86
github.com/blackfireio/osinfo v1.0.3 github.com/blackfireio/osinfo v1.0.3
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9
github.com/hashicorp/go-plugin v1.4.4
github.com/maxlandon/readline v0.1.0-beta.0.20211027085530-2b76cabb8036 github.com/maxlandon/readline v0.1.0-beta.0.20211027085530-2b76cabb8036
github.com/pborman/getopt v1.1.0 github.com/pborman/getopt v1.1.0
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
@ -19,21 +16,10 @@ require (
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/arnodel/strftime v0.1.6 // indirect github.com/arnodel/strftime v0.1.6 // indirect
github.com/evilsocket/islazy v1.10.6 // indirect github.com/evilsocket/islazy v1.10.6 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/golang/protobuf v1.3.4 // indirect
github.com/hashicorp/go-hclog v0.14.1 // indirect
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.10 // indirect
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect
golang.org/x/net v0.0.0-20190311183353-d8887717615a // indirect
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect
google.golang.org/grpc v1.27.1 // indirect
) )
replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b
@ -42,4 +28,4 @@ replace github.com/maxlandon/readline => ./readline
replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10 replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10
replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3 replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345

89
go.sum
View File

@ -1,9 +1,5 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345 h1:QNYjYDogUSiNUkffbhFSrSCtpZhofeiVYGFN2FI4wSs=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
github.com/Rosettea/Malvales v0.0.0-20220707042337-37a9ee7758f9 h1:iVIheUFcp/EhUYaCo67eG4fhW5EHxQDUNYq319DPNmc=
github.com/Rosettea/Malvales v0.0.0-20220707042337-37a9ee7758f9/go.mod h1:vZdkshJ9RK/Hu1aUNa6suOC1XfaFOzxqQQ7OZFpNYbw=
github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3 h1:I/wWr40FFLFF9pbT3wLb1FAEZhKb/hUWE+nJ5uHBK2g=
github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b h1:s5eDMhBk6H1BgipgLub/gv9qeyBaTuiHM0k3h2/9TSE= github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b h1:s5eDMhBk6H1BgipgLub/gv9qeyBaTuiHM0k3h2/9TSE=
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs= github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
@ -14,44 +10,15 @@ github.com/arnodel/strftime v0.1.6/go.mod h1:5NbK5XqYK8QpRZpqKNt4OlxLtIB8cotkLk4
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/blackfireio/osinfo v1.0.3 h1:Yk2t2GTPjBcESv6nDSWZKO87bGMQgO+Hi9OoXPpxX8c= github.com/blackfireio/osinfo v1.0.3 h1:Yk2t2GTPjBcESv6nDSWZKO87bGMQgO+Hi9OoXPpxX8c=
github.com/blackfireio/osinfo v1.0.3/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA= github.com/blackfireio/osinfo v1.0.3/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 h1:xz6Nv3zcwO2Lila35hcb0QloCQsc38Al13RNEzWRpX4=
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9/go.mod h1:2wSM9zJkl1UQEFZgSd68NfCgRz1VL1jzy/RjCg+ULrs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc= github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc=
github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evilsocket/islazy v1.10.6 h1:MFq000a1ByoumoJWlytqg0qon0KlBeUfPsDjY0hK0bo= github.com/evilsocket/islazy v1.10.6 h1:MFq000a1ByoumoJWlytqg0qon0KlBeUfPsDjY0hK0bo=
github.com/evilsocket/islazy v1.10.6/go.mod h1:OrwQGYg3DuZvXUfmH+KIZDjwTCbrjy48T24TUpGqVVw= github.com/evilsocket/islazy v1.10.6/go.mod h1:OrwQGYg3DuZvXUfmH+KIZDjwTCbrjy48T24TUpGqVVw=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk= github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-plugin v1.4.4 h1:NVdrSdFRt3SkZtNckJ6tog7gbpRrcbOjQi/rgF7JYWQ=
github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
@ -60,56 +27,21 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw= github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw=
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0= github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0=
github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0= github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0=
github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk= github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I= github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I=
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4= golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4=
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -126,23 +58,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0= mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=

View File

@ -1,6 +1,14 @@
// 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.
package bait package bait
import ( import (
"errors"
"hilbish/util" "hilbish/util"
rt "github.com/arnodel/golua/runtime" rt "github.com/arnodel/golua/runtime"
@ -72,8 +80,12 @@ func (b *Bait) Emit(event string, args ...interface{}) {
} }
_, err := rt.Call1(b.rtm.MainThread(), funcVal, luaArgs...) _, err := rt.Call1(b.rtm.MainThread(), funcVal, luaArgs...)
if err != nil { if err != nil {
// panicking here won't actually cause hilbish to panic and instead will if event != "error" {
// print the error and remove the hook. reference the recoverer function in lua.go b.Emit("error", event, handle.luaCaller, err.Error())
return
}
// if there is an error in an error event handler, panic instead
// (calls the go recoverer function)
panic(err) panic(err)
} }
} else { } else {
@ -187,19 +199,11 @@ func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
"catchOnce": util.LuaExport{b.bcatchOnce, 2, false}, "catchOnce": util.LuaExport{b.bcatchOnce, 2, false},
"throw": util.LuaExport{b.bthrow, 1, true}, "throw": util.LuaExport{b.bthrow, 1, true},
"release": util.LuaExport{b.brelease, 2, false}, "release": util.LuaExport{b.brelease, 2, false},
"hooks": util.LuaExport{b.bhooks, 1, false},
} }
mod := rt.NewTable() mod := rt.NewTable()
util.SetExports(rtm, mod, exports) util.SetExports(rtm, mod, exports)
util.Document(mod,
`Bait is the event emitter for Hilbish. Why name it bait?
Because it throws hooks that you can catch (emits events
that you can listen to) and because why not, fun naming
is fun. 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, see doc hooks.`)
return rt.TableValue(mod), nil return rt.TableValue(mod), nil
} }
@ -276,9 +280,11 @@ func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
} }
// release(name, catcher) // release(name, catcher)
// Removes the `catcher` for the event with `name` // Removes the `catcher` for the event with `name`.
// For this to work, `catcher` has to be the same function used to catch // For this to work, `catcher` has to be the same function used to catch
// an event, like one saved to a variable. // 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) { func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
name, catcher, err := util.HandleStrCallback(t, c) name, catcher, err := util.HandleStrCallback(t, c)
if err != nil { if err != nil {
@ -289,3 +295,35 @@ func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// hooks(name) -> table
// Returns a table with hooks (callback functions) on the event with `name`.
// --- @param name string
// --- @returns table<function>
func (b *Bait) bhooks(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
}
evName, err := c.StringArg(0)
if err != nil {
return nil, err
}
noHooks := errors.New("no hooks for event " + evName)
handlers := b.handlers[evName]
if handlers == nil {
return nil, noHooks
}
luaHandlers := rt.NewTable()
for _, handler := range handlers {
if handler.typ != luaListener { continue }
luaHandlers.Set(rt.IntValue(luaHandlers.Len() + 1), rt.FunctionValue(handler.luaCaller))
}
if luaHandlers.Len() == 0 {
return nil, noHooks
}
return c.PushingNext1(t.Runtime, rt.TableValue(luaHandlers)), nil
}

View File

@ -1,3 +1,33 @@
// 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:
```lua
local commander = require 'commander'
commander.register('hello', function(args, sinks)
sinks.out:writeln 'Hello world!'
end)
```
In this example, a command with the name of `hello` is created
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.
- `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.
*/
package commander package commander
import ( import (
@ -32,7 +62,6 @@ func (c *Commander) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
} }
mod := rt.NewTable() mod := rt.NewTable()
util.SetExports(rtm, mod, exports) util.SetExports(rtm, mod, exports)
util.Document(mod, "Commander is Hilbish's custom command library, a way to write commands in Lua.")
return rt.TableValue(mod), nil return rt.TableValue(mod), nil
} }

View File

@ -1,3 +1,7 @@
// 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.
package fs package fs
import ( import (
@ -35,10 +39,6 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
mod.Set(rt.StringValue("pathSep"), rt.StringValue(string(os.PathSeparator))) mod.Set(rt.StringValue("pathSep"), rt.StringValue(string(os.PathSeparator)))
mod.Set(rt.StringValue("pathListSep"), rt.StringValue(string(os.PathListSeparator))) mod.Set(rt.StringValue("pathListSep"), rt.StringValue(string(os.PathListSeparator)))
util.Document(mod, `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.`)
return rt.TableValue(mod), nil return rt.TableValue(mod), nil
} }
@ -93,9 +93,15 @@ func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), err return c.Next(), err
} }
// stat(path) // stat(path) -> {}
// Returns info about `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
// --- @param path string // --- @param path string
// --- @returns table
func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -119,8 +125,8 @@ func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.TableValue(statTbl)), nil return c.PushingNext1(t.Runtime, rt.TableValue(statTbl)), nil
} }
// readdir(dir) // readdir(dir) -> {}
// Returns a table of files in `dir` // Returns a table of files in `dir`.
// --- @param dir string // --- @param dir string
// --- @return table // --- @return table
func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
@ -145,9 +151,10 @@ func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil
} }
// abs(path) // abs(path) -> string
// Gives an absolute version of `path`. // Gives an absolute version of `path`.
// --- @param path string // --- @param path string
// --- @returns string
func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
path, err := c.StringArg(0) path, err := c.StringArg(0)
if err != nil { if err != nil {
@ -163,9 +170,10 @@ func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.StringValue(abspath)), nil return c.PushingNext1(t.Runtime, rt.StringValue(abspath)), nil
} }
// basename(path) // basename(path) -> string
// Gives the basename of `path`. For the rules, // Gives the basename of `path`. For the rules,
// see Go's filepath.Base // see Go's filepath.Base
// --- @returns string
func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -178,9 +186,11 @@ func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext(t.Runtime, rt.StringValue(filepath.Base(path))), nil return c.PushingNext(t.Runtime, rt.StringValue(filepath.Base(path))), nil
} }
// dir(path) // dir(path) -> string
// Returns the directory part of `path`. For the rules, see Go's // Returns the directory part of `path`. For the rules, see Go's
// filepath.Dir // filepath.Dir
// --- @param path string
// --- @returns string
func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -193,9 +203,11 @@ func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext(t.Runtime, rt.StringValue(filepath.Dir(path))), nil return c.PushingNext(t.Runtime, rt.StringValue(filepath.Dir(path))), nil
} }
// glob(pattern) // glob(pattern) -> matches (table)
// Glob all files and directories that match the pattern. // Glob all files and directories that match the pattern.
// For the rules, see Go's filepath.Glob // For the rules, see Go's filepath.Glob
// --- @param pattern string
// --- @returns table
func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -219,9 +231,11 @@ func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext(t.Runtime, rt.TableValue(luaMatches)), nil return c.PushingNext(t.Runtime, rt.TableValue(luaMatches)), nil
} }
// join(paths...) // join(...) -> string
// Takes paths and joins them together with the OS's // Takes paths and joins them together with the OS's
// directory separator (forward or backward slash). // directory separator (forward or backward slash).
// --- @vararg string
// --- @returns string
func fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
strs := make([]string, len(c.Etc())) strs := make([]string, len(c.Etc()))
for i, v := range c.Etc() { for i, v := range c.Etc() {

View File

@ -1,3 +1,5 @@
// low level terminal library
// The terminal library is a simple and lower level library for certain terminal interactions.
package terminal package terminal
import ( import (
@ -26,7 +28,6 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
mod := rt.NewTable() mod := rt.NewTable()
util.SetExports(rtm, mod, exports) util.SetExports(rtm, mod, exports)
util.Document(mod, "The terminal library is a simple and lower level library for certain terminal interactions.")
return rt.TableValue(mod), nil return rt.TableValue(mod), nil
} }

59
hilbish-git.spec 100644
View File

@ -0,0 +1,59 @@
%global _missing_build_ids_terminate_build 0
%global debug_package %{nil}
Name: hilbish-git
Version: {{{ git_tag_version }}}.{{{ git_short_hash }}}
Release: 1%{?dist}
Summary: The flower shell. A comfy and nice little shell for Lua fans!
License: MIT
Source: {{{ git_dir_pack }}}
BuildRequires: git golang go-task
Requires: inspect succulent lunacolors
Url: https://github.com/Rosettea/Hilbish
VCS: {{{ git_dir_vcs }}}
%description
Hilbish is a extensible shell (framework). It was made to be very customizable
via the Lua programming language. It aims to be easy to use for the casual
people but powerful for those who want to tinker more with their shell,
the thing used to interface with most of the system.
The motivation for choosing Lua was that its simpler and better to use
than old shell script. It's fine for basic interactive shell uses,
but that's the only place Hilbish has shell script; everything else is Lua
and aims to be infinitely configurable. If something isn't, open an issue!
%prep
{{{ git_dir_setup_macro }}}
sed -i '\|/etc/shells|d' Taskfile.yaml
%build
go-task
%install
go-task install PREFIX=%{buildroot}/usr BINDIR=%{buildroot}/%{_bindir}
%post
if [ "$1" = 1 ]; then
if [ ! -f %{_sysconfdir}/shells ] ; then
echo "%{_bindir}/hilbish" > %{_sysconfdir}/shells
echo "/bin/hilbish" >> %{_sysconfdir}/shells
else
grep -q "^%{_bindir}/hilbish$" %{_sysconfdir}/shells || echo "%{_bindir}/hilbish" >> %{_sysconfdir}/shells
grep -q "^/bin/hilbish$" %{_sysconfdir}/shells || echo "/bin/hilbish" >> %{_sysconfdir}/shells
fi
fi
%postun
if [ "$1" = 0 ] && [ -f %{_sysconfdir}/shells ] ; then
sed -i '\!^%{_bindir}/hilbish$!d' %{_sysconfdir}/shells
sed -i '\!^/bin/hilbish$!d' %{_sysconfdir}/shells
fi
%files
%doc README.md
%license LICENSE
%{_bindir}/hilbish
%{_datadir}/hilbish

View File

@ -73,13 +73,13 @@ func newFileHistory(path string) *fileHistory {
} }
} }
itms := []string{""}
lines := strings.Split(string(data), "\n") lines := strings.Split(string(data), "\n")
itms := make([]string, len(lines) - 1)
for i, l := range lines { for i, l := range lines {
if i == len(lines) - 1 { if i == len(lines) - 1 {
continue continue
} }
itms = append(itms, l) itms[i] = l
} }
f, err := os.OpenFile(path, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0755) f, err := os.OpenFile(path, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0755)
if err != nil { if err != nil {

57
job.go
View File

@ -18,6 +18,16 @@ import (
var jobs *jobHandler var jobs *jobHandler
var jobMetaKey = rt.StringValue("hshjob") var jobMetaKey = rt.StringValue("hshjob")
// #type
// #interface jobs
// #property cmd The user entered command string for the job.
// #property running Whether the job is running or not.
// #property id The ID of the job in the job table
// #property pid The Process ID
// #property exitCode The last exit code of the job.
// #property stdout The standard output of the job. This just means the normal logs of the process.
// #property stderr The standard error stream of the process. This (usually) includes error messages of the job.
// The Job type describes a Hilbish job.
type job struct { type job struct {
cmd string cmd string
running bool running bool
@ -110,6 +120,10 @@ func (j *job) getProc() *os.Process {
return nil return nil
} }
// #interface jobs
// #member
// start()
// Starts running the job.
func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -130,6 +144,10 @@ func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// #interface jobs
// #member
// stop()
// Stops the job from running.
func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -148,6 +166,11 @@ func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// #interface jobs
// #member
// foreground()
// Puts a job in the foreground. This will cause it to run like it was
// executed normally and wait for it to complete.
func luaForegroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func luaForegroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -180,6 +203,10 @@ func luaForegroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// #interface jobs
// #member
// background()
// Puts a job in the background. This acts the same as initially running a job.
func luaBackgroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func luaBackgroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -276,6 +303,13 @@ func (j *jobHandler) stopAll() {
} }
} }
// #interface jobs
// background job management
/*
Manage interactive jobs in Hilbish via Lua.
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. */
func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table { func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table {
jobMethods := rt.NewTable() jobMethods := rt.NewTable()
jFuncs := map[string]util.LuaExport{ jFuncs := map[string]util.LuaExport{
@ -353,6 +387,11 @@ func jobUserData(j *job) *rt.UserData {
return rt.NewUserData(j, jobMeta.AsTable()) return rt.NewUserData(j, jobMeta.AsTable())
} }
// #interface jobs
// get(id) -> @Job
// Get a job object via its ID.
// --- @param id number
// --- @returns Job
func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
j.mu.RLock() j.mu.RLock()
defer j.mu.RUnlock() defer j.mu.RUnlock()
@ -373,6 +412,12 @@ func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext(t.Runtime, rt.UserDataValue(job.ud)), nil return c.PushingNext(t.Runtime, rt.UserDataValue(job.ud)), nil
} }
// #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
func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(3); err != nil { if err := c.CheckNArgs(3); err != nil {
return nil, err return nil, err
@ -402,6 +447,10 @@ func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.UserDataValue(jb.ud)), nil return c.PushingNext1(t.Runtime, rt.UserDataValue(jb.ud)), nil
} }
// #interface jobs
// all() -> table<@Job>
// Returns a table of all job objects.
// --- @returns table<Job>
func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
j.mu.RLock() j.mu.RLock()
defer j.mu.RUnlock() defer j.mu.RUnlock()
@ -414,6 +463,10 @@ func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.TableValue(jobTbl)), nil return c.PushingNext1(t.Runtime, rt.TableValue(jobTbl)), nil
} }
// #interface jobs
// disown(id)
// Disowns a job. This deletes it from the job table.
// --- @param id number
func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -431,6 +484,10 @@ func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// #interface jobs
// last() -> @Job
// Returns the last added job from the table.
// --- @returns Job
func (j *jobHandler) luaLastJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (j *jobHandler) luaLastJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
j.mu.RLock() j.mu.RLock()
defer j.mu.RUnlock() defer j.mu.RUnlock()

@ -1 +1 @@
Subproject commit 8467b87dd8d49c68b4100b2d129d5f071544b8cf Subproject commit 34a57c964590f89aa065188a588c7b38aff99c28

3
lua.go
View File

@ -23,6 +23,7 @@ func luaInit() {
MessageHandler: debuglib.Traceback, MessageHandler: debuglib.Traceback,
}) })
lib.LoadAll(l) lib.LoadAll(l)
setupSinkType(l)
lib.LoadLibs(l, hilbishLoader) lib.LoadLibs(l, hilbishLoader)
// yes this is stupid, i know // yes this is stupid, i know
@ -49,7 +50,7 @@ func luaInit() {
hooks = bait.New(l) hooks = bait.New(l)
hooks.SetRecoverer(func(event string, handler *bait.Listener, err interface{}) { hooks.SetRecoverer(func(event string, handler *bait.Listener, err interface{}) {
fmt.Println("Error in", event, "event:", err) fmt.Println("Error in `error` hook handler:", err)
hooks.Off(event, handler) hooks.Off(event, handler)
}) })

11
main.go
View File

@ -115,9 +115,11 @@ func main() {
os.Setenv("SHELL", os.Args[0]) os.Setenv("SHELL", os.Args[0])
} }
go handleSignals()
lr = newLineReader("", false) lr = newLineReader("", false)
luaInit() luaInit()
go handleSignals()
// If user's config doesn't exixt, // If user's config doesn't exixt,
if _, err := os.Stat(defaultConfPath); os.IsNotExist(err) && *configflag == defaultConfPath { if _, err := os.Stat(defaultConfPath); os.IsNotExist(err) && *configflag == defaultConfPath {
// Read default from current directory // Read default from current directory
@ -181,11 +183,14 @@ input:
break break
} }
if err != nil { if err != nil {
if err != readline.CtrlC { if err == readline.CtrlC {
fmt.Println("^C")
hooks.Emit("hilbish.cancel")
} else {
// If we get a completely random error, print // If we get a completely random error, print
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
} }
fmt.Println("^C") // TODO: Halt if any other error occurs
continue continue
} }
var priv bool var priv bool

View File

@ -1,15 +1,15 @@
local commander = require 'commander' local commander = require 'commander'
commander.register('bg', function() commander.register('bg', function(_, sinks)
local job = hilbish.jobs.last() local job = hilbish.jobs.last()
if not job then if not job then
print 'bg: no last job' sinks.out:writeln 'bg: no last job'
return 1 return 1
end end
local err = job.background() local err = job:background()
if err then if err then
print('bg: ' .. err) sinks.out:writeln('bg: ' .. err)
return 2 return 2
end end
end) end)

View File

@ -0,0 +1,25 @@
local commander = require 'commander'
local fs = require 'fs'
commander.register('cat', function(args, sinks)
local exit = 0
if #args == 0 then
sinks.out:writeln [[
usage: cat [file]...]]
end
for _, fName in ipairs(args) do
local f = io.open(fName)
if f == nil then
exit = 1
sinks.out:writeln(string.format('cat: %s: no such file or directory', fName))
goto continue
end
sinks.out:writeln(f:read '*a')
::continue::
end
io.flush()
return exit
end)

View File

@ -4,32 +4,25 @@ local fs = require 'fs'
local dirs = require 'nature.dirs' local dirs = require 'nature.dirs'
dirs.old = hilbish.cwd() dirs.old = hilbish.cwd()
commander.register('cd', function (args) commander.register('cd', function (args, sinks)
if #args > 1 then if #args > 1 then
print("cd: too many arguments") sinks.out:writeln("cd: too many arguments")
return 1 return 1
elseif #args > 0 then end
local path = args[1]:gsub('$%$','\0'):gsub('${([%w_]+)}', os.getenv)
:gsub('$([%w_]+)', os.getenv):gsub('%z','$'):gsub('^%s*(.-)%s*$', '%1')
local path = args[1] and args[1] or hilbish.home
if path == '-' then if path == '-' then
path = dirs.old path = dirs.old
print(path) sinks.out:writeln(path)
end end
dirs.setOld(hilbish.cwd()) dirs.setOld(hilbish.cwd())
dirs.push(path) dirs.push(path)
local ok, err = pcall(function() fs.cd(path) end) local ok, err = pcall(function() fs.cd(path) end)
if not ok then if not ok then
print(err) sinks.out:writeln(err)
return 1 return 1
end end
bait.throw('cd', path) bait.throw('cd', path)
return
end
fs.cd(hilbish.home)
bait.throw('cd', hilbish.home)
dirs.push(hilbish.home)
end) end)

View File

@ -3,9 +3,9 @@ local fs = require 'fs'
local lunacolors = require 'lunacolors' local lunacolors = require 'lunacolors'
local dirs = require 'nature.dirs' local dirs = require 'nature.dirs'
commander.register('cdr', function(args) commander.register('cdr', function(args, sinks)
if not args[1] then if not args[1] then
print(lunacolors.format [[ sinks.out:writeln(lunacolors.format [[
cdr: change directory to one which has been recently visied cdr: change directory to one which has been recently visied
usage: cdr <index> usage: cdr <index>
@ -17,21 +17,21 @@ to get a list of recent directories, use {green}{underline}cdr list{reset}]])
if args[1] == 'list' then if args[1] == 'list' then
local recentDirs = dirs.recentDirs local recentDirs = dirs.recentDirs
if #recentDirs == 0 then if #recentDirs == 0 then
print 'No directories have been visited.' sinks.out:writeln 'No directories have been visited.'
return 1 return 1
end end
print(table.concat(recentDirs, '\n')) sinks.out:writeln(table.concat(recentDirs, '\n'))
return return
end end
local index = tonumber(args[1]) local index = tonumber(args[1])
if not index then if not index then
print(string.format('Received %s as index, which isn\'t a number.', index)) sinks.out:writeln(string.format('Received %s as index, which isn\'t a number.', index))
return 1 return 1
end end
if not dirs.recent(index) then if not dirs.recent(index) then
print(string.format('No recent directory found at index %s.', index)) sinks.out:writeln(string.format('No recent directory found at index %s.', index))
return 1 return 1
end end

View File

@ -0,0 +1,7 @@
local ansikit = require 'ansikit'
local commander = require 'commander'
commander.register('clear', function()
ansikit.clear(true)
ansikit.cursorTo(0, 0)
end)

View File

@ -1,8 +1,8 @@
local commander = require 'commander' local commander = require 'commander'
commander.register('disown', function(args) commander.register('disown', function(args, sinks)
if #hilbish.jobs.all() == 0 then if #hilbish.jobs.all() == 0 then
print 'disown: no current job' sinks.out:writeln 'disown: no current job'
return 1 return 1
end end
@ -10,7 +10,7 @@ commander.register('disown', function(args)
if #args < 0 then if #args < 0 then
id = tonumber(args[1]) id = tonumber(args[1])
if not id then if not id then
print 'disown: invalid id for job' sinks.out:writeln 'disown: invalid id for job'
return 1 return 1
end end
else else
@ -19,7 +19,7 @@ commander.register('disown', function(args)
local ok = pcall(hilbish.jobs.disown, id) local ok = pcall(hilbish.jobs.disown, id)
if not ok then if not ok then
print 'disown: job does not exist' sinks.out:writeln 'disown: job does not exist'
return 2 return 2
end end
end) end)

View File

@ -2,94 +2,99 @@ local commander = require 'commander'
local fs = require 'fs' local fs = require 'fs'
local lunacolors = require 'lunacolors' local lunacolors = require 'lunacolors'
commander.register('doc', function(args) commander.register('doc', function(args, sinks)
local moddocPath = hilbish.dataDir .. '/docs/' local moddocPath = hilbish.dataDir .. '/docs/'
local modDocFormat = [[ local stat = pcall(fs.stat, '.git/refs/heads/extended-job-api')
%s if stat then
%s -- hilbish git
# Functions moddocPath = './docs/'
end
local apidocHeader = [[
# %s
{grayBg} {white}{italic}%s {reset}
]] ]]
local modules = table.map(fs.readdir(moddocPath), function(f)
return lunacolors.underline(lunacolors.blue(string.gsub(f, '.md', '')))
end)
local doc = [[
Welcome to Hilbish's documentation viewer! Here you can find
documentation for builtin functions and other things related
to Hilbish.
Usage: doc <section> [subdoc]
Available sections: ]] .. table.concat(modules, ', ')
if #args > 0 then if #args > 0 then
local mod = args[1] local mod = args[1]
local f = io.open(moddocPath .. mod .. '.txt', 'rb') local f = io.open(moddocPath .. mod .. '.md', 'rb')
local funcdocs = nil local funcdocs = nil
local subdocName = args[2]
if not f then if not f then
-- assume subdir -- assume subdir
-- dataDir/docs/<mod>/<mod>.txt -- dataDir/docs/<mod>/<mod>.md
moddocPath = moddocPath .. mod .. '/' moddocPath = moddocPath .. mod .. '/'
local subdocName = args[2]
if not subdocName then if not subdocName then
subdocName = 'index' subdocName = '_index'
end end
f = io.open(moddocPath .. subdocName .. '.txt', 'rb') f = io.open(moddocPath .. subdocName .. '.md', 'rb')
if not f then if not f then
print('No documentation found for ' .. mod .. '.') f = io.open(moddocPath .. subdocName:match '%w+' .. '/' .. subdocName .. '.md', 'rb')
return
end end
funcdocs = f:read '*a' if not f then
local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= 'index.txt' end) moddocPath = moddocPath .. subdocName .. '/'
subdocName = args[3] or '_index'
f = io.open(moddocPath .. subdocName .. '.md', 'rb')
end
if not f then
sinks.out:writeln('No documentation found for ' .. mod .. '.')
return 1
end
end
funcdocs = f:read '*a':gsub('-([%d]+)', '%1')
local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= '_index.md' and f ~= 'index.md' end)
local subdocs = table.map(moddocs, function(fname) local subdocs = table.map(moddocs, function(fname)
return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.txt', ''))) return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.md', '')))
end) end)
if subdocName == 'index' then if #moddocs ~= 0 then
funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ') funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ')
end end
local valsStr = funcdocs:match '%-%-%-\n([^%-%-%-]+)\n'
local vals = {}
if valsStr then
local _, endpos = funcdocs:find('---\n' .. valsStr .. '\n---\n\n', 1, true)
funcdocs = funcdocs:sub(endpos + 1, #funcdocs)
-- parse vals
local lines = string.split(valsStr, '\n')
for _, line in ipairs(lines) do
local key = line:match '(%w+): '
local val = line:match '^%w+: (.-)$'
if key then
vals[key] = val
end
end
end
if mod == 'api' then
funcdocs = string.format(apidocHeader, vals.title, vals.description or 'no description.') .. funcdocs
end
doc = funcdocs:sub(1, #funcdocs - 1)
f:close()
end end
if not funcdocs then
funcdocs = f:read '*a'
end
local desc = ''
local ok = pcall(require, mod)
local backtickOccurence = 0 local backtickOccurence = 0
local formattedFuncs = lunacolors.format(funcdocs:sub(1, #funcdocs - 1):gsub('`', function() sinks.out:writeln(lunacolors.format(doc:gsub('`', function()
backtickOccurence = backtickOccurence + 1 backtickOccurence = backtickOccurence + 1
if backtickOccurence % 2 == 0 then if backtickOccurence % 2 == 0 then
return '{reset}' return '{reset}'
else else
return '{underline}{green}' return '{underline}{green}'
end end
end)) end):gsub('\n#+.-\n', function(t)
local signature = t:gsub('<.->(.-)</.->', '{underline}%1'):gsub('\\', '<')
if ok then return '{bold}{yellow}' .. signature .. '{reset}'
local props = {} end)))
local propstr = ''
local modDesc = ''
local modmt = getmetatable(require(mod))
if modmt then
modDesc = modmt.__doc
if modmt.__docProp then
-- not all modules have docs for properties
props = table.map(modmt.__docProp, function(v, k)
return lunacolors.underline(lunacolors.blue(k)) .. ' > ' .. v
end)
end
if #props > 0 then
propstr = '\n# Properties\n' .. table.concat(props, '\n') .. '\n'
end
desc = string.format(modDocFormat, modDesc, propstr)
end
end
print(desc .. formattedFuncs)
f:close()
return
end
local modules = table.map(fs.readdir(moddocPath), function(f)
return lunacolors.underline(lunacolors.blue(string.gsub(f, '.txt', '')))
end)
io.write [[
Welcome to Hilbish's doc tool! Here you can find documentation for builtin
functions and other things.
Usage: doc <section> [subdoc]
A section is a module or a literal section and a subdoc is a subsection for it.
Available sections: ]]
io.flush()
print(table.concat(modules, ', '))
end) end)

View File

@ -0,0 +1,5 @@
local commander = require 'commander'
commander.register('exec', function(args)
hilbish.exec(args[1])
end)

View File

@ -1,15 +1,15 @@
local commander = require 'commander' local commander = require 'commander'
commander.register('fg', function() commander.register('fg', function(_, sinks)
local job = hilbish.jobs.last() local job = hilbish.jobs.last()
if not job then if not job then
print 'fg: no last job' sinks.out:writeln 'fg: no last job'
return 1 return 1
end end
local err = job.foreground() -- waits for job; blocks local err = job:foreground() -- waits for job; blocks
if err then if err then
print('fg: ' .. err) sinks.out:writeln('fg: ' .. err)
return 2 return 2
end end
end) end)

View File

@ -24,7 +24,7 @@ function hilbish.completion.handler(line, pos)
return {compGroup}, pfx return {compGroup}, pfx
else else
local ok, compGroups, pfx = pcall(hilbish.completion.call, local ok, compGroups, pfx = pcall(hilbish.completion.call,
'command.' .. #fields[1], query, ctx, fields) 'command.' .. fields[1], query, ctx, fields)
if ok then if ok then
return compGroups, pfx return compGroups, pfx
end end

View File

@ -1,5 +1,6 @@
-- Prelude initializes everything else for our shell -- Prelude initializes everything else for our shell
local _ = require 'succulent' -- Function additions local _ = require 'succulent' -- Function additions
local bait = require 'bait'
local fs = require 'fs' local fs = require 'fs'
package.path = package.path .. ';' .. hilbish.dataDir .. '/?/init.lua' package.path = package.path .. ';' .. hilbish.dataDir .. '/?/init.lua'
@ -28,7 +29,9 @@ do
return got_virt return got_virt
end end
if type(key) == 'string' then
virt_G[key] = os.getenv(key) virt_G[key] = os.getenv(key)
end
return virt_G[key] return virt_G[key]
end, end,
@ -54,7 +57,6 @@ do
if ok then if ok then
for _, module in ipairs(modules) do for _, module in ipairs(modules) do
local entry = package.searchpath(module, startSearchPath) local entry = package.searchpath(module, startSearchPath)
print(entry)
if entry then if entry then
dofile(entry) dofile(entry)
end end
@ -63,3 +65,15 @@ do
package.path = package.path .. ';' .. startSearchPath package.path = package.path .. ';' .. startSearchPath
end end
bait.catch('error', function(event, handler, err)
print(string.format('Encountered an error in %s handler\n%s', event, err:sub(8)))
end)
bait.catch('command.not-found', function(cmd)
print(string.format('hilbish: %s not found', cmd))
end)
bait.catch('command.not-executable', function(cmd)
print(string.format('hilbish: %s: not executable', cmd))
end)

View File

@ -1,5 +1,6 @@
local bait = require 'bait' local bait = require 'bait'
bait.catch('command.exit', function(_, cmd, priv) bait.catch('command.exit', function(_, cmd, priv)
if not cmd then return end
if not priv and hilbish.opts.history then hilbish.history.add(cmd) end if not priv and hilbish.opts.history then hilbish.history.add(cmd) end
end) end)

View File

@ -2,8 +2,8 @@ local bait = require 'bait'
local lunacolors = require 'lunacolors' local lunacolors = require 'lunacolors'
hilbish.motd = [[ hilbish.motd = [[
Hilbish 2.0 is a {red}major{reset} update! If your config doesn't work 1000 commits on the Hilbish repository brings us to {cyan}Version 2.1!{reset}
anymore, that will definitely be why! A MOTD, very message, much day. Docs, docs, docs... At least builtins work with pipes now.
]] ]]
bait.catch('hilbish.init', function() bait.catch('hilbish.init', function()

View File

@ -1,3 +1,4 @@
--- hilbish.runner
local currentRunner = 'hybrid' local currentRunner = 'hybrid'
local runners = {} local runners = {}
@ -74,6 +75,12 @@ function hilbish.runner.setCurrent(name)
hilbish.runner.setMode(r.run) hilbish.runner.setMode(r.run)
end end
--- Returns the current runner by name.
--- @returns string
function hilbish.runner.getCurrent()
return currentRunner
end
hilbish.runner.add('hybrid', function(input) hilbish.runner.add('hybrid', function(input)
local cmdStr = hilbish.aliases.resolve(input) local cmdStr = hilbish.aliases.resolve(input)

27
os.go 100644
View File

@ -0,0 +1,27 @@
package main
import (
"hilbish/util"
rt "github.com/arnodel/golua/runtime"
"github.com/blackfireio/osinfo"
)
// #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.
// #field family Family name of the current OS
// #field name Pretty name of the current OS
// #field version Version of the current OS
func hshosLoader(rtm *rt.Runtime) *rt.Table {
info, _ := osinfo.GetOSInfo()
mod := rt.NewTable()
util.SetField(rtm, mod, "family", rt.StringValue(info.Family))
util.SetField(rtm, mod, "name", rt.StringValue(info.Name))
util.SetField(rtm, mod, "version", rt.StringValue(info.Version))
return mod
}

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"github.com/rivo/uniseg"
) )
// initGrid - Grid display details. Called each time we want to be sure to have // initGrid - Grid display details. Called each time we want to be sure to have
@ -13,8 +14,8 @@ func (g *CompletionGroup) initGrid(rl *Instance) {
// Compute size of each completion item box // Compute size of each completion item box
tcMaxLength := 1 tcMaxLength := 1
for i := range g.Suggestions { for i := range g.Suggestions {
if len(g.Suggestions[i]) > tcMaxLength { if uniseg.GraphemeClusterCount(g.Suggestions[i]) > tcMaxLength {
tcMaxLength = len([]rune(g.Suggestions[i])) tcMaxLength = uniseg.GraphemeClusterCount(g.Suggestions[i])
} }
} }
@ -103,7 +104,7 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) {
rl.tcUsedY++ rl.tcUsedY++
} }
cellWidth := strconv.Itoa((GetTermWidth() / g.tcMaxX) - 2) cellWidth := strconv.Itoa((GetTermWidth() / g.tcMaxX) - 4)
x := 0 x := 0
y := 1 y := 1
@ -124,7 +125,15 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) {
comp += seqInvert comp += seqInvert
} }
comp += fmt.Sprintf("%-"+cellWidth+"s %s", fmtEscape(g.Suggestions[i]), seqReset) sugg := g.Suggestions[i]
if len(sugg) > GetTermWidth() {
sugg = sugg[:GetTermWidth() - 4] + "..."
}
formatStr := "%-"+cellWidth+"s%s "
if g.tcMaxX == 1 {
formatStr = "%s%s"
}
comp += fmt.Sprintf(formatStr, fmtEscape(sugg), seqReset)
} }
// Always add a newline to the group if the end if not punctuated with one // Always add a newline to the group if the end if not punctuated with one

View File

@ -123,23 +123,20 @@ func (rl *Instance) walkHistory(i int) {
// When we are exiting the current line buffer to move around // When we are exiting the current line buffer to move around
// the history, we make buffer the current line // the history, we make buffer the current line
if rl.histPos == 0 && (rl.histPos+i) == 1 { if rl.histOffset == 0 && rl.histOffset + i == 1 {
rl.lineBuf = string(rl.line) rl.lineBuf = string(rl.line)
} }
switch rl.histPos + i { rl.histOffset += i
case 0, history.Len() + 1: if rl.histOffset == 0 {
rl.histPos = 0
rl.line = []rune(rl.lineBuf) rl.line = []rune(rl.lineBuf)
rl.pos = len(rl.lineBuf) rl.pos = len(rl.lineBuf)
return } else if rl.histOffset <= -1 {
case -1: rl.histOffset = 0
rl.histPos = 0 } else {
rl.lineBuf = string(rl.line)
default:
dedup = true dedup = true
old = string(rl.line) old = string(rl.line)
new, err = history.GetLine(history.Len() - rl.histPos - 1) new, err = history.GetLine(history.Len() - rl.histOffset)
if err != nil { if err != nil {
rl.resetHelpers() rl.resetHelpers()
print("\r\n" + err.Error() + "\r\n") print("\r\n" + err.Error() + "\r\n")
@ -148,7 +145,6 @@ func (rl *Instance) walkHistory(i int) {
} }
rl.clearLine() rl.clearLine()
rl.histPos += i
rl.line = []rune(new) rl.line = []rune(new)
rl.pos = len(rl.line) rl.pos = len(rl.line)
if rl.pos > 0 { if rl.pos > 0 {

View File

@ -134,6 +134,7 @@ type Instance struct {
// history operating params // history operating params
lineBuf string lineBuf string
histPos int histPos int
histOffset int
histNavIdx int // Used for quick history navigation. histNavIdx int // Used for quick history navigation.
// //

View File

@ -49,7 +49,7 @@ func (rl *Instance) Readline() (string, error) {
// History Init // History Init
// We need this set to the last command, so that we can access it quickly // We need this set to the last command, so that we can access it quickly
rl.histPos = 0 rl.histOffset = 0
rl.viUndoHistory = []undoItem{{line: "", pos: 0}} rl.viUndoHistory = []undoItem{{line: "", pos: 0}}
// Multisplit // Multisplit
@ -238,7 +238,9 @@ func (rl *Instance) Readline() (string, error) {
// Normal completion search does only refresh the search pattern and the comps // Normal completion search does only refresh the search pattern and the comps
if rl.modeTabFind || rl.modeAutoFind { if rl.modeTabFind || rl.modeAutoFind {
rl.resetVirtualComp(false)
rl.backspaceTabFind() rl.backspaceTabFind()
rl.renderHelpers()
rl.viUndoSkipAppend = true rl.viUndoSkipAppend = true
} else { } else {
// Always cancel any virtual completion // Always cancel any virtual completion
@ -331,6 +333,8 @@ func (rl *Instance) Readline() (string, error) {
rl.modeTabFind = true rl.modeTabFind = true
rl.updateTabFind([]rune{}) rl.updateTabFind([]rune{})
rl.updateVirtualComp()
rl.renderHelpers()
rl.viUndoSkipAppend = true rl.viUndoSkipAppend = true
// Tab Completion & Completion Search --------------------------------------------------------------- // Tab Completion & Completion Search ---------------------------------------------------------------
@ -484,7 +488,10 @@ func (rl *Instance) Readline() (string, error) {
if string(r[:i]) != seqShiftTab && if string(r[:i]) != seqShiftTab &&
string(r[:i]) != seqForwards && string(r[:i]) != seqBackwards && string(r[:i]) != seqForwards && string(r[:i]) != seqBackwards &&
string(r[:i]) != seqUp && string(r[:i]) != seqDown { string(r[:i]) != seqUp && string(r[:i]) != seqDown {
rl.resetVirtualComp(false) // basically only applies except on 1st ctrl r open
// so if we have not explicitly selected something
// (tabCompletionSelect is false) drop virtual completion
rl.resetVirtualComp(!rl.tabCompletionSelect)
} }
} }
@ -517,7 +524,9 @@ func (rl *Instance) Readline() (string, error) {
if rl.modeAutoFind || rl.modeTabFind { if rl.modeAutoFind || rl.modeTabFind {
rl.resetVirtualComp(false) rl.resetVirtualComp(false)
rl.updateTabFind(r[:i]) rl.updateTabFind(r[:i])
rl.renderHelpers()
rl.viUndoSkipAppend = true rl.viUndoSkipAppend = true
continue
} else { } else {
rl.resetVirtualComp(false) rl.resetVirtualComp(false)
rl.editorInput(r[:i]) rl.editorInput(r[:i])
@ -537,6 +546,10 @@ func (rl *Instance) Readline() (string, error) {
// entry readline is currently configured for and then update the line entries // entry readline is currently configured for and then update the line entries
// accordingly. // accordingly.
func (rl *Instance) editorInput(r []rune) { func (rl *Instance) editorInput(r []rune) {
if len(r) == 0 {
return
}
switch rl.modeViMode { switch rl.modeViMode {
case VimKeys: case VimKeys:
rl.vi(r[0]) rl.vi(r[0])
@ -604,6 +617,7 @@ func (rl *Instance) escapeSeq(r []rune) {
case string(charEscape): case string(charEscape):
switch { switch {
case rl.modeAutoFind: case rl.modeAutoFind:
rl.resetVirtualComp(true)
rl.resetTabFind() rl.resetTabFind()
rl.clearHelpers() rl.clearHelpers()
rl.resetTabCompletion() rl.resetTabCompletion()
@ -611,6 +625,7 @@ func (rl *Instance) escapeSeq(r []rune) {
rl.renderHelpers() rl.renderHelpers()
case rl.modeTabFind: case rl.modeTabFind:
rl.resetVirtualComp(true)
rl.resetTabFind() rl.resetTabFind()
rl.resetTabCompletion() rl.resetTabCompletion()

View File

@ -2,6 +2,7 @@ package readline
import ( import (
"strings" "strings"
"github.com/rivo/uniseg"
) )
// insertCandidateVirtual - When a completion candidate is selected, we insert it virtually in the input line: // insertCandidateVirtual - When a completion candidate is selected, we insert it virtually in the input line:
@ -249,10 +250,10 @@ func (rl *Instance) viJumpEVirtual(tokeniser func([]rune, int) ([]string, int, i
return return
case pos >= len(word)-1: case pos >= len(word)-1:
word = rTrimWhiteSpace(split[index+1]) word = rTrimWhiteSpace(split[index+1])
adjust = len(split[index]) - pos adjust = uniseg.GraphemeClusterCount(split[index]) - pos
adjust += len(word) - 1 adjust += uniseg.GraphemeClusterCount(word) - 1
default: default:
adjust = len(word) - pos - 1 adjust = uniseg.GraphemeClusterCount(word) - pos - 1
} }
return return
} }

View File

@ -1,6 +1,10 @@
package readline package readline
import "golang.org/x/text/width" import (
"strings"
"golang.org/x/text/width"
)
// updateHelpers is a key part of the whole refresh process: // updateHelpers is a key part of the whole refresh process:
// it should coordinate reprinting the input line, any Infos and completions // it should coordinate reprinting the input line, any Infos and completions
@ -52,19 +56,19 @@ func (rl *Instance) updateReferences() {
rl.posY = 0 rl.posY = 0
rl.fullY = 0 rl.fullY = 0
var fullLine, cPosLine int var curLine []rune
if len(rl.currentComp) > 0 { if len(rl.currentComp) > 0 {
fullLine = getWidth(rl.lineComp) curLine = rl.lineComp
cPosLine = getWidth(rl.lineComp[:rl.pos])
} else { } else {
fullLine = getWidth(rl.line) curLine = rl.line
cPosLine = getWidth(rl.line[:rl.pos])
} }
fullLine := getWidth(curLine)
cPosLine := getWidth(curLine[:rl.pos])
// We need the X offset of the whole line // We need the X offset of the whole line
toEndLine := rl.promptLen + fullLine toEndLine := rl.promptLen + fullLine
fullOffset := toEndLine / GetTermWidth() fullOffset := toEndLine / GetTermWidth()
rl.fullY = fullOffset rl.fullY = fullOffset + strings.Count(string(curLine), "\n")
fullRest := toEndLine % GetTermWidth() fullRest := toEndLine % GetTermWidth()
rl.fullX = fullRest rl.fullX = fullRest

View File

@ -245,7 +245,7 @@ func (rl *Instance) vi(r rune) {
} }
// Keep the previous cursor position // Keep the previous cursor position
prev := rl.pos //prev := rl.pos
new, err := rl.StartEditorWithBuffer(multiline, "") new, err := rl.StartEditorWithBuffer(multiline, "")
if err != nil || len(new) == 0 || string(new) == string(multiline) { if err != nil || len(new) == 0 || string(new) == string(multiline) {
@ -257,11 +257,11 @@ func (rl *Instance) vi(r rune) {
// Clean the shell and put the new buffer, with adjusted pos if needed. // Clean the shell and put the new buffer, with adjusted pos if needed.
rl.clearLine() rl.clearLine()
rl.line = new rl.line = new
if prev > len(rl.line) { rl.pos = len(rl.line)
rl.pos = len(rl.line) - 1 /*if prev > len(rl.line) {
} else { } else {
rl.pos = prev rl.pos = prev
} }*/
case 'w': case 'w':
// If we were not yanking // If we were not yanking

25
rl.go
View File

@ -225,7 +225,11 @@ func (lr *lineReader) Resize() {
return return
} }
// lua module // #interface history
// command history
// The history interface deals with command history.
// This includes the ability to override functions to change the main
// method of saving history.
func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table { func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table {
lrLua := map[string]util.LuaExport{ lrLua := map[string]util.LuaExport{
"add": {lr.luaAddHistory, 1, false}, "add": {lr.luaAddHistory, 1, false},
@ -241,6 +245,10 @@ func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table {
return mod return mod
} }
// #interface history
// add(cmd)
// Adds a command to the history.
// --- @param cmd string
func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -254,10 +262,18 @@ func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
return c.Next(), nil return c.Next(), nil
} }
// #interface history
// size() -> number
// Returns the amount of commands in the history.
// --- @returns number
func (lr *lineReader) luaSize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 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 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
func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -272,6 +288,10 @@ func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
} }
// #interface history
// all() -> table
// Retrieves all history.
// --- @returns table
func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
tbl := rt.NewTable() tbl := rt.NewTable()
size := lr.fileHist.Len() size := lr.fileHist.Len()
@ -284,6 +304,9 @@ func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
return c.PushingNext1(t.Runtime, rt.TableValue(tbl)), nil return c.PushingNext1(t.Runtime, rt.TableValue(tbl)), nil
} }
// #interface history
// clear()
// Deletes all commands from the history.
func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
lr.fileHist.clear() lr.fileHist.clear()
return c.Next(), nil return c.Next(), nil

2
rpkg.conf 100644
View File

@ -0,0 +1,2 @@
[rpkg]
user_macros = "${git_props:root}/rpkg.macros"

25
rpkg.macros 100644
View File

@ -0,0 +1,25 @@
function git_short_hash {
short_hash="$(cached git_short_hash)"
if [ -z "$short_hash" ]; then
short_hash="$(git rev-parse --short HEAD)"
fi
output "$short_hash"
}
function git_tag_version {
tag="$(cached git_tag_version)"
if [ -z "$tag" ]; then
tag="$(git describe --tags --abbrev=0)"
fi
# Remove the potential prefix of `v`
if [[ $tag =~ ^v[0-9].* ]]; then
tag="${tag:1}"
fi
tag="${tag/"-"/"."}"
output "$tag"
}

View File

@ -6,6 +6,13 @@ import (
rt "github.com/arnodel/golua/runtime" rt "github.com/arnodel/golua/runtime"
) )
// #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.
func runnerModeLoader(rtm *rt.Runtime) *rt.Table { func runnerModeLoader(rtm *rt.Runtime) *rt.Table {
exports := map[string]util.LuaExport{ exports := map[string]util.LuaExport{
"sh": {shRunner, 1, false}, "sh": {shRunner, 1, false},
@ -19,6 +26,20 @@ func runnerModeLoader(rtm *rt.Runtime) *rt.Table {
return mod return mod
} }
// #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.
// 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
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
func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -28,13 +49,13 @@ func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return nil, err return nil, err
} }
input, exitCode, cont, err := execSh(cmd) _, exitCode, cont, err := execSh(aliases.Resolve(cmd))
var luaErr rt.Value = rt.NilValue var luaErr rt.Value = rt.NilValue
if err != nil { if err != nil {
luaErr = rt.StringValue(err.Error()) luaErr = rt.StringValue(err.Error())
} }
runnerRet := rt.NewTable() runnerRet := rt.NewTable()
runnerRet.Set(rt.StringValue("input"), rt.StringValue(input)) runnerRet.Set(rt.StringValue("input"), rt.StringValue(cmd))
runnerRet.Set(rt.StringValue("exitCode"), rt.IntValue(int64(exitCode))) runnerRet.Set(rt.StringValue("exitCode"), rt.IntValue(int64(exitCode)))
runnerRet.Set(rt.StringValue("continue"), rt.BoolValue(cont)) runnerRet.Set(rt.StringValue("continue"), rt.BoolValue(cont))
runnerRet.Set(rt.StringValue("err"), luaErr) runnerRet.Set(rt.StringValue("err"), luaErr)
@ -42,6 +63,11 @@ func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext(t.Runtime, rt.TableValue(runnerRet)), nil return c.PushingNext(t.Runtime, rt.TableValue(runnerRet)), nil
} }
// #interface runner
// 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
func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err

128
sink.go 100644
View File

@ -0,0 +1,128 @@
package main
import (
"fmt"
"io"
"hilbish/util"
rt "github.com/arnodel/golua/runtime"
)
var sinkMetaKey = rt.StringValue("hshsink")
// #type
// A sink is a structure that has input and/or output to/from
// a desination.
type sink struct{
writer io.Writer
reader io.Reader
ud *rt.UserData
}
func setupSinkType(rtm *rt.Runtime) {
sinkMeta := rt.NewTable()
sinkMethods := rt.NewTable()
sinkFuncs := map[string]util.LuaExport{
"write": {luaSinkWrite, 2, false},
"writeln": {luaSinkWriteln, 2, false},
}
util.SetExports(l, sinkMethods, sinkFuncs)
sinkIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
arg := c.Arg(1)
val := sinkMethods.Get(arg)
return c.PushingNext1(t.Runtime, val), nil
}
sinkMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(sinkIndex, "__index", 2, false)))
l.SetRegistry(sinkMetaKey, rt.TableValue(sinkMeta))
}
// #member
// write(str)
// Writes data to a sink.
func luaSinkWrite(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil {
return nil, err
}
s, err := sinkArg(c, 0)
if err != nil {
return nil, err
}
data, err := c.StringArg(1)
if err != nil {
return nil, err
}
s.writer.Write([]byte(data))
return c.Next(), nil
}
// #member
// writeln(str)
// Writes data to a sink with a newline at the end.
func luaSinkWriteln(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil {
return nil, err
}
s, err := sinkArg(c, 0)
if err != nil {
return nil, err
}
data, err := c.StringArg(1)
if err != nil {
return nil, err
}
s.writer.Write([]byte(data + "\n"))
return c.Next(), nil
}
func newSinkInput(r io.Reader) *sink {
s := &sink{
reader: r,
}
s.ud = sinkUserData(s)
return s
}
func newSinkOutput(w io.Writer) *sink {
s := &sink{
writer: w,
}
s.ud = sinkUserData(s)
return s
}
func sinkArg(c *rt.GoCont, arg int) (*sink, error) {
s, ok := valueToSink(c.Arg(arg))
if !ok {
return nil, fmt.Errorf("#%d must be a sink", arg + 1)
}
return s, nil
}
func valueToSink(val rt.Value) (*sink, bool) {
u, ok := val.TryUserData()
if !ok {
return nil, false
}
s, ok := u.Value().(*sink)
return s, ok
}
func sinkUserData(s *sink) *rt.UserData {
sinkMeta := l.Registry(sinkMetaKey)
return rt.NewUserData(s, sinkMeta.AsTable())
}

View File

@ -15,13 +15,19 @@ const (
timerTimeout timerTimeout
) )
// #type
// #interface timers
// #property type What type of timer it is
// #property running If the timer is running
// #property duration The duration in milliseconds that the timer will run
// The Job type describes a Hilbish timer.
type timer struct{ type timer struct{
id int id int
typ timerType typ timerType
running bool running bool
dur time.Duration dur time.Duration
fun *rt.Closure fun *rt.Closure
th *timerHandler th *timersModule
ticker *time.Ticker ticker *time.Ticker
ud *rt.UserData ud *rt.UserData
channel chan struct{} channel chan struct{}
@ -73,6 +79,10 @@ func (t *timer) stop() error {
return nil return nil
} }
// #interface timers
// #member
// start()
// Starts a timer.
func timerStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func timerStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -91,6 +101,10 @@ func timerStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// #interface timers
// #member
// stop()
// Stops a timer.
func timerStop(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func timerStop(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err

View File

@ -10,10 +10,10 @@ import (
rt "github.com/arnodel/golua/runtime" rt "github.com/arnodel/golua/runtime"
) )
var timers *timerHandler var timers *timersModule
var timerMetaKey = rt.StringValue("hshtimer") var timerMetaKey = rt.StringValue("hshtimer")
type timerHandler struct { type timersModule struct {
mu *sync.RWMutex mu *sync.RWMutex
wg *sync.WaitGroup wg *sync.WaitGroup
timers map[int]*timer timers map[int]*timer
@ -21,8 +21,8 @@ type timerHandler struct {
running int running int
} }
func newTimerHandler() *timerHandler { func newTimersModule() *timersModule {
return &timerHandler{ return &timersModule{
timers: make(map[int]*timer), timers: make(map[int]*timer),
latestID: 0, latestID: 0,
mu: &sync.RWMutex{}, mu: &sync.RWMutex{},
@ -30,11 +30,11 @@ func newTimerHandler() *timerHandler {
} }
} }
func (th *timerHandler) wait() { func (th *timersModule) wait() {
th.wg.Wait() th.wg.Wait()
} }
func (th *timerHandler) create(typ timerType, dur time.Duration, fun *rt.Closure) *timer { func (th *timersModule) create(typ timerType, dur time.Duration, fun *rt.Closure) *timer {
th.mu.Lock() th.mu.Lock()
defer th.mu.Unlock() defer th.mu.Unlock()
@ -54,14 +54,21 @@ func (th *timerHandler) create(typ timerType, dur time.Duration, fun *rt.Closure
return t return t
} }
func (th *timerHandler) get(id int) *timer { func (th *timersModule) get(id int) *timer {
th.mu.RLock() th.mu.RLock()
defer th.mu.RUnlock() defer th.mu.RUnlock()
return th.timers[id] return th.timers[id]
} }
func (th *timerHandler) luaCreate(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #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
func (th *timersModule) luaCreate(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(3); err != nil { if err := c.CheckNArgs(3); err != nil {
return nil, err return nil, err
} }
@ -83,7 +90,12 @@ func (th *timerHandler) luaCreate(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.UserDataValue(tmr.ud)), nil return c.PushingNext1(t.Runtime, rt.UserDataValue(tmr.ud)), nil
} }
func (th *timerHandler) luaGet(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #interface timers
// get(id) -> @Timer
// Retrieves a timer via its ID.
// --- @param id number
// --- @returns Timer
func (th *timersModule) luaGet(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
} }
@ -100,7 +112,34 @@ func (th *timerHandler) luaGet(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
func (th *timerHandler) loader(rtm *rt.Runtime) *rt.Table { // #interface timers
// #field INTERVAL Constant for an interval timer type
// #field TIMEOUT Constant for a timeout timer type
// timeout and interval API
/*
If you ever want to run a piece of code on a timed interval, or want to wait
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.
An example of usage:
```
local t = hilbish.timers.create(hilbish.timers.TIMEOUT, 5000, function()
print 'hello!'
end)
t:start()
print(t.running) // true
```
*/
func (th *timersModule) loader(rtm *rt.Runtime) *rt.Table {
timerMethods := rt.NewTable() timerMethods := rt.NewTable()
timerFuncs := map[string]util.LuaExport{ timerFuncs := map[string]util.LuaExport{
"start": {timerStart, 1, false}, "start": {timerStart, 1, false},
@ -141,6 +180,9 @@ func (th *timerHandler) loader(rtm *rt.Runtime) *rt.Table {
luaTh := rt.NewTable() luaTh := rt.NewTable()
util.SetExports(rtm, luaTh, thExports) util.SetExports(rtm, luaTh, thExports)
util.SetField(rtm, luaTh, "INTERVAL", rt.IntValue(0))
util.SetField(rtm, luaTh, "TIMEOUT", rt.IntValue(1))
return luaTh return luaTh
} }

23
userdir.go 100644
View File

@ -0,0 +1,23 @@
package main
import (
"hilbish/util"
rt "github.com/arnodel/golua/runtime"
)
// #interface userDir
// user-related directories
// 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.
// #field config The user's config directory
// #field data The user's directory for program data
func userDirLoader(rtm *rt.Runtime) *rt.Table {
mod := rt.NewTable()
util.SetField(rtm, mod, "config", rt.StringValue(confDir))
util.SetField(rtm, mod, "data", rt.StringValue(userDataDir))
return mod
}

View File

@ -10,53 +10,18 @@ import (
rt "github.com/arnodel/golua/runtime" rt "github.com/arnodel/golua/runtime"
) )
// Document adds a documentation string to a module.
// It is accessible via the __doc metatable.
func Document(module *rt.Table, doc string) {
mt := module.Metatable()
if mt == nil {
mt = rt.NewTable()
module.SetMetatable(mt)
}
mt.Set(rt.StringValue("__doc"), rt.StringValue(doc))
}
// SetField sets a field in a table, adding docs for it. // SetField sets a field in a table, adding docs for it.
// It is accessible via the __docProp metatable. It is a table of the names of the fields. // It is accessible via the __docProp metatable. It is a table of the names of the fields.
func SetField(rtm *rt.Runtime, module *rt.Table, field string, value rt.Value, doc string) { func SetField(rtm *rt.Runtime, module *rt.Table, field string, value rt.Value) {
// TODO: ^ rtm isnt needed, i should remove it // TODO: ^ rtm isnt needed, i should remove it
SetFieldDoc(module, field, doc)
module.Set(rt.StringValue(field), value) module.Set(rt.StringValue(field), value)
} }
// SetFieldDoc sets the __docProp metatable for a field on the
// module.
func SetFieldDoc(module *rt.Table, field, doc string) {
mt := module.Metatable()
if mt == nil {
mt = rt.NewTable()
module.SetMetatable(mt)
}
docProp := mt.Get(rt.StringValue("__docProp"))
if docProp == rt.NilValue {
docPropTbl := rt.NewTable()
mt.Set(rt.StringValue("__docProp"), rt.TableValue(docPropTbl))
docProp = mt.Get(rt.StringValue("__docProp"))
}
docProp.AsTable().Set(rt.StringValue(field), rt.StringValue(doc))
}
// SetFieldProtected sets a field in a protected table. A protected table // SetFieldProtected sets a field in a protected table. A protected table
// is one which has a metatable proxy to ensure no overrides happen to it. // is one which has a metatable proxy to ensure no overrides happen to it.
// It sets the field in the table and sets the __docProp metatable on the // It sets the field in the table and sets the __docProp metatable on the
// user facing table. // user facing table.
func SetFieldProtected(module, realModule *rt.Table, field string, value rt.Value, doc string) { func SetFieldProtected(module, realModule *rt.Table, field string, value rt.Value) {
SetFieldDoc(module, field, doc)
realModule.Set(rt.StringValue(field), value) realModule.Set(rt.StringValue(field), value)
} }

View File

@ -11,8 +11,8 @@ var (
// Version info // Version info
var ( var (
ver = "v2.0.0" ver = "v2.2.0"
releaseName = "Hibiscus" releaseName = "Poppy"
gitCommit string gitCommit string
gitBranch string gitBranch string
) )

View File

@ -14,7 +14,7 @@ var (
.. hilbish.userDir.config .. '/hilbish/?/init.lua;' .. hilbish.userDir.config .. '/hilbish/?/init.lua;'
.. hilbish.userDir.config .. '/hilbish/?/?.lua;' .. hilbish.userDir.config .. '/hilbish/?/?.lua;'
.. hilbish.userDir.config .. '/hilbish/?.lua'` .. hilbish.userDir.config .. '/hilbish/?.lua'`
dataDir = "/usr/share/hilbish" dataDir = "/usr/local/share/hilbish"
preloadPath = dataDir + "/nature/init.lua" preloadPath = dataDir + "/nature/init.lua"
sampleConfPath = dataDir + "/.hilbishrc.lua" // Path to default/sample config sampleConfPath = dataDir + "/.hilbishrc.lua" // Path to default/sample config
defaultConfDir = "" defaultConfDir = ""

Some files were not shown because too many files have changed in this diff Show More