diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..a06ed29
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,6 @@
+root = true
+
+[*]
+charset = utf-8
+indent_size = 4
+indent_style = tab
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 371d284..4aab838 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -25,7 +25,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v2
with:
- go-version: '1.17.7'
+ go-version: '1.18.8'
- name: Download Task
run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
- name: Build
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index e378376..6515d25 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -2,13 +2,14 @@ name: Generate docs
on:
push:
- branches: [master]
+ branches:
+ - master
jobs:
gen:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- uses: actions/setup-go@v2
- name: Run docgen
run: go run cmd/docgen/docgen.go
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index a3a2840..416c97e 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -33,10 +33,14 @@ jobs:
- uses: actions/checkout@v3
with:
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
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
+ ldflags: '-s -w'
binary_name: hilbish
extra_files: LICENSE README.md CHANGELOG.md .hilbishrc.lua nature libs docs emmyLuaDocs
diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml
new file mode 100644
index 0000000..547673b
--- /dev/null
+++ b/.github/workflows/website.yml
@@ -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
diff --git a/.gitignore b/.gitignore
index 338ef97..1abf82c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
*.exe
hilbish
+!docs/api/hilbish
docgen
+!cmd/docgen
.vim
petals/
+.hugo_build.lock
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 49904ad..ec8db20 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,44 @@
# 🎀 Changelog
## 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/#/
@@ -39,7 +76,7 @@ without arguments will disown the last job.
fields on a job object.
- Documentation for jobs is now available via `doc jobs`.
- `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 uses.
- `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
random errors introduced with the new Lua runtime (see [#197])
- `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
### Changed
- **Breaking Change:** Upgraded to Lua 5.4.
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.
- Overrides on the `hilbish` table are no longer permitted.
- **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.
- **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.
+- **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`
methods to make it act how they want.
- `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.
- Put full input in history if prompted for continued input
- 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 insert unhandled control keys.
- 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.
- Input getting cut off on enter in certain cases.
- 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))
-- Files with same name as parent folder in completions getting cut off [#136](https://github.com/Rosettea/Hilbish/issues/136))
+- 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 [#130](https://github.com/Rosettea/Hilbish/issues/130))
- `hilbish.which` now works with commanders and aliases.
- Background jobs no longer take stdin so they do not interfere with shell
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
where it can also resolve to the beginning of command names.
(reference [this commit](https://github.com/Rosettea/Hilbish/commit/2790982ad123115c6ddbc5764677fdca27668cea))
@@ -145,6 +192,29 @@ menu is open.
- Escape codes now work.
- Escape percentage symbols in completion entries, so you will no longer see
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
### Added
@@ -569,6 +639,11 @@ This input for example will prompt for more input to complete:
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.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
diff --git a/LICENSE b/LICENSE
index da3c8c1..3d8f013 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2022 Rosettea
+Copyright (c) 2021-2023 Rosettea
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 86879d1..3a3dbbb 100644
--- a/README.md
+++ b/README.md
@@ -26,49 +26,30 @@ and aims to be infinitely configurable. If something isn't, open an issue!
# Table of Contents
- [Screenshots](#Screenshots)
-- [Installation](#Installation)
- - [Prebuilt Bins](#Prebuilt-binaries)
- - [AUR](#AUR)
- - [Nixpkgs](#Nixpkgs)
- - [Manual Build](#Manual-Build)
-- [Getting Started](#Getting-Started)
+- [Getting Hilbish](#Getting-Hilbish)
- [Contributing](#Contributing)
# Screenshots
-
+
-# Installation
-## Prebuilt binaries
-Go [here](https://nightly.link/Rosettea/Hilbish/workflows/build/master) for
-builds on the master branch.
+# Getting Hilbish
+**NOTE:** Hilbish is not guaranteed to work properly on Windows, starting
+from the 2.0 version. It will still be able to compile, but functionality
+may be lacking.
-## AUR
-[![AUR maintainer](https://img.shields.io/aur/maintainer/hilbish?logo=arch-linux&style=flat-square)](https://aur.archlinux.org/packages/hilbish)
-Arch Linux users can install Hilbish from the AUR with the following command:
-```sh
-yay -S hilbish
-```
+You can check the [install page](https://rosettea.github.io/Hilbish/install/)
+on the website for distributed binaries from GitHub or other package repositories.
+Otherwise, continue reading for steps on compiling.
-[![AUR maintainer](https://img.shields.io/aur/maintainer/hilbish?logo=arch-linux&style=flat-square)](https://aur.archlinux.org/packages/hilbish-git)
-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
+## Prerequisites
- [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
are submodules.
```sh
diff --git a/Taskfile.yaml b/Taskfile.yaml
index 067f2ba..5c5caae 100644
--- a/Taskfile.yaml
+++ b/Taskfile.yaml
@@ -3,23 +3,24 @@
version: '3'
vars:
- PREFIX: '{{default "/usr" .PREFIX}}'
+ PREFIX: '{{default "/usr/local" .PREFIX}}'
bindir__: '{{.PREFIX}}/bin'
BINDIR: '{{default .bindir__ .BINDIR}}'
libdir__: '{{.PREFIX}}/share/hilbish'
LIBDIR: '{{default .libdir__ .LIBDIR}}'
- GOFLAGS: '-ldflags "-s -w"'
+ goflags__: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}}"'
+ GOFLAGS: '{{default .goflags__ .GOFLAGS}}'
tasks:
default:
cmds:
- - go build {{.GOFLAGS}}
+ - CGO_ENABLED=0 go build {{.GOFLAGS}}
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:
cmds:
- - go build {{.GOFLAGS}}
+ - CGO_ENABLED=0 go build {{.GOFLAGS}}
install:
cmds:
@@ -33,4 +34,4 @@ tasks:
- rm -vrf
"{{.DESTDIR}}{{.BINDIR}}/hilbish"
"{{.DESTDIR}}{{.LIBDIR}}"
- - sed -i '/hilbish/d' /etc/shells
+ - grep -v 'hilbish' /etc/shells > /tmp/shells.hilbish_uninstall && mv /tmp/shells.hilbish_uninstall /etc/shells
diff --git a/aliases.go b/aliases.go
index 3007cc3..bfacc43 100644
--- a/aliases.go
+++ b/aliases.go
@@ -9,40 +9,40 @@ import (
rt "github.com/arnodel/golua/runtime"
)
-var aliases *aliasHandler
+var aliases *aliasModule
-type aliasHandler struct {
+type aliasModule struct {
aliases map[string]string
mu *sync.RWMutex
}
// initialize aliases map
-func newAliases() *aliasHandler {
- return &aliasHandler{
+func newAliases() *aliasModule {
+ return &aliasModule{
aliases: make(map[string]string),
mu: &sync.RWMutex{},
}
}
-func (a *aliasHandler) Add(alias, cmd string) {
+func (a *aliasModule) Add(alias, cmd string) {
a.mu.Lock()
defer a.mu.Unlock()
a.aliases[alias] = cmd
}
-func (a *aliasHandler) All() map[string]string {
+func (a *aliasModule) All() map[string]string {
return a.aliases
}
-func (a *aliasHandler) Delete(alias string) {
+func (a *aliasModule) Delete(alias string) {
a.mu.Lock()
defer a.mu.Unlock()
delete(a.aliases, alias)
}
-func (a *aliasHandler) Resolve(cmdstr string) string {
+func (a *aliasModule) Resolve(cmdstr string) string {
a.mu.RLock()
defer a.mu.RUnlock()
@@ -66,7 +66,10 @@ func (a *aliasHandler) Resolve(cmdstr string) string {
// 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
hshaliasesLua := map[string]util.LuaExport{
"add": util.LuaExport{hlalias, 2, false},
@@ -81,7 +84,18 @@ func (a *aliasHandler) Loader(rtm *rt.Runtime) *rt.Table {
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
+// Get a table of all aliases, with string keys as the alias and the value as the command.
+// --- @returns table
+func (a *aliasModule) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
aliasesList := rt.NewTable()
for k, v := range a.All() {
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
}
-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 {
return nil, err
}
@@ -103,7 +121,12 @@ func (a *aliasHandler) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
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 {
return nil, err
}
diff --git a/api.go b/api.go
index cfa3d06..3d3c887 100644
--- a/api.go
+++ b/api.go
@@ -1,6 +1,14 @@
-// Here is the core api for the hilbi shell itself
-// Basically, stuff about the shell itself and other functions
-// go here.
+// the core Hilbish API
+// The Hilbish module includes the core API, containing
+// 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
import (
@@ -19,7 +27,6 @@ import (
rt "github.com/arnodel/golua/runtime"
"github.com/arnodel/golua/lib/packagelib"
"github.com/maxlandon/readline"
- "github.com/blackfireio/osinfo"
"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
}
- util.SetFieldProtected(fakeMod, mod, "ver", rt.StringValue(getVersion()), "Hilbish version")
- util.SetFieldProtected(fakeMod, mod, "user", rt.StringValue(username), "Username of user")
- util.SetFieldProtected(fakeMod, mod, "host", rt.StringValue(host), "Host name of the machine")
- util.SetFieldProtected(fakeMod, mod, "home", rt.StringValue(curuser.HomeDir), "Home directory of the user")
- util.SetFieldProtected(fakeMod, mod, "dataDir", rt.StringValue(dataDir), "Directory for Hilbish's data files")
- util.SetFieldProtected(fakeMod, mod, "interactive", rt.BoolValue(interactive), "If this is an interactive shell")
- util.SetFieldProtected(fakeMod, mod, "login", rt.BoolValue(login), "Whether this is a login shell")
- util.SetFieldProtected(fakeMod, mod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)")
- util.SetFieldProtected(fakeMod, mod, "exitCode", rt.IntValue(0), "Exit code of last exected command")
- util.Document(fakeMod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.")
+ util.SetFieldProtected(fakeMod, mod, "ver", rt.StringValue(getVersion()))
+ util.SetFieldProtected(fakeMod, mod, "user", rt.StringValue(username))
+ util.SetFieldProtected(fakeMod, mod, "host", rt.StringValue(host))
+ util.SetFieldProtected(fakeMod, mod, "home", rt.StringValue(curuser.HomeDir))
+ util.SetFieldProtected(fakeMod, mod, "dataDir", rt.StringValue(dataDir))
+ util.SetFieldProtected(fakeMod, mod, "interactive", rt.BoolValue(interactive))
+ util.SetFieldProtected(fakeMod, mod, "login", rt.BoolValue(login))
+ util.SetFieldProtected(fakeMod, mod, "vimMode", rt.NilValue)
+ util.SetFieldProtected(fakeMod, mod, "exitCode", rt.IntValue(0))
// hilbish.userDir table
- hshuser := rt.NewTable()
-
- 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.")
+ hshuser := userDirLoader(rtm)
mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser))
// hilbish.os table
- hshos := rt.NewTable()
- 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")
+ hshos := hshosLoader(rtm)
mod.Set(rt.StringValue("os"), rt.TableValue(hshos))
// hilbish.aliases table
aliases = newAliases()
aliasesModule := aliases.Loader(rtm)
- util.Document(aliasesModule, "Alias inferface for Hilbish.")
mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule))
// hilbish.history table
historyModule := lr.Loader(rtm)
mod.Set(rt.StringValue("history"), rt.TableValue(historyModule))
- util.Document(historyModule, "History interface for Hilbish.")
// hilbish.completion table
hshcomp := completionLoader(rtm)
- util.Document(hshcomp, "Completions interface for Hilbish.")
mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp))
// hilbish.runner table
runnerModule := runnerModeLoader(rtm)
- util.Document(runnerModule, "Runner/exec interface for Hilbish.")
mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule))
// hilbish.jobs table
jobs = newJobHandler()
jobModule := jobs.loader(rtm)
- util.Document(jobModule, "(Background) job interface.")
mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule))
// hilbish.timers table
- timers = newTimerHandler()
- timerModule := timers.loader(rtm)
- util.Document(timerModule, "Timer interface, for control of all intervals and timeouts.")
- mod.Set(rt.StringValue("timers"), rt.TableValue(timerModule))
+ timers = newTimersModule()
+ timersModule := timers.loader(rtm)
+ mod.Set(rt.StringValue("timers"), rt.TableValue(timersModule))
editorModule := editorLoader(rtm)
- util.Document(editorModule, "")
mod.Set(rt.StringValue("editor"), rt.TableValue(editorModule))
versionModule := rt.NewTable()
- util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch), "Git branch Hilbish was compiled from")
- util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion()), "Full version info, including release name")
- util.SetField(rtm, versionModule, "commit", rt.StringValue(gitCommit), "Git commit Hilbish was compiled from")
- util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName), "Release name")
- util.Document(versionModule, "Version info interface.")
+ util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch))
+ util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion()))
+ util.SetField(rtm, versionModule, "commit", rt.StringValue(gitCommit))
+ util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName))
mod.Set(rt.StringValue("version"), rt.TableValue(versionModule))
pluginModule := moduleLoader(rtm)
@@ -192,19 +180,21 @@ func getenv(key, fallback string) 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)
}
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.
// If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
// 3rd values instead of being outputted to the terminal.
// --- @param cmd string
+// --- @param returnOut boolean
+// --- @returns number, string, string
func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
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
}
-// cwd()
+// cwd() -> string
// Returns the current directory of the shell
+// --- @returns string
func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
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.
// This is a separate instance from the one Hilbish actually uses.
// Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
-// --- @param prompt string
+// --- @param prompt? string
+// --- @returns string|nil
func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
- if err := c.Check1Arg(); err != nil {
- return nil, err
+ luaprompt := c.Arg(0)
+ 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)
- if err != nil {
- return nil, err
+ prompt, ok := luaprompt.TryString()
+ if !ok {
+ // 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()
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`
There are a few verbs that can be used in the prompt text.
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
`%h` - Hostname of device
--- @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) {
err := c.Check1Arg()
@@ -451,12 +449,12 @@ func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil
}
-// timeout(cb, time)
-// Runs the `cb` function after `time` in milliseconds
-// Returns a `timer` object (see `doc timers`).
+// timeout(cb, time) -> @Timer
+// Runs the `cb` function after `time` in milliseconds.
+// This creates a timer that starts immediately.
// --- @param cb function
// --- @param time number
-// --- @return table
+// --- @returns Timer
func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil {
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
}
-// interval(cb, time)
+// interval(cb, time) -> @Timer
// 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 time number
-// --- @return table
+// --- @return Timer
func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil {
return nil, err
@@ -543,9 +541,11 @@ func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil
}
-// which(name)
-// Checks if `name` is a valid command
-// --- @param binName string
+// which(name) -> string
+// Checks if `name` is a valid command.
+// Will return the path of the binary, or a basename if it's a commander.
+// --- @param name string
+// --- @returns string
func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
@@ -555,7 +555,10 @@ func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
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
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,
// override this function with your custom handler.
// --- @param line string
-// --- @param pos int
+// --- @param pos number
func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil
}
diff --git a/cmd/docgen/docgen.go b/cmd/docgen/docgen.go
index 39a2a76..aae6202 100644
--- a/cmd/docgen/docgen.go
+++ b/cmd/docgen/docgen.go
@@ -7,28 +7,266 @@ import (
"go/doc"
"go/parser"
"go/token"
+ "regexp"
"strings"
"os"
+ "sync"
)
-type EmmyPiece struct {
- FuncName string
- Docs []string
+var header = `---
+title: %s %s
+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
+ 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
FuncSig 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() {
fset := token.NewFileSet()
os.Mkdir("docs", 0777)
+ os.Mkdir("docs/api", 0777)
os.Mkdir("emmyLuaDocs", 0777)
-
dirs := []string{"./"}
filepath.Walk("golibs/", func (path string, info os.FileInfo, err error) error {
@@ -51,120 +289,266 @@ func main() {
}
}
- prefix := map[string]string{
- "hilbish": "hl",
- "fs": "f",
- "commander": "c",
- "bait": "b",
- "terminal": "term",
- }
- docs := make(map[string][]DocPiece)
- emmyDocs := make(map[string][]EmmyPiece)
-
+ interfaceModules := make(map[string]*module)
for l, f := range pkgs {
p := doc.New(f, "./", doc.AllDecls)
+ pieces := []docPiece{}
+ typePieces := []docPiece{}
+ mod := l
+ if mod == "main" {
+ mod = "hilbish"
+ }
+ var hasInterfaces bool
for _, t := range p.Funcs {
- mod := l
- if strings.HasPrefix(t.Name, "hl") { mod = "hilbish" }
- if !strings.HasPrefix(t.Name, prefix[mod]) || t.Name == "Loader" { continue }
- 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)
- }
+ piece := setupDoc(mod, t)
+ if piece == nil {
+ continue
}
-
- dps := DocPiece{
- Doc: funcdoc,
- FuncSig: funcsig,
- FuncName: strings.TrimPrefix(t.Name, prefix[mod]),
+
+ pieces = append(pieces, *piece)
+ if piece.IsInterface {
+ hasInterfaces = true
}
-
- docs[mod] = append(docs[mod], dps)
- emmyDocs[mod] = append(emmyDocs[mod], em)
}
for _, t := range p.Types {
- for _, m := range t.Methods {
- if !strings.HasPrefix(m.Name, prefix[l]) || m.Name == "Loader" { continue }
- parts := strings.Split(strings.TrimSpace(m.Doc), "\n")
- funcsig := parts[0]
- doc := parts[1:]
- 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)
- }
+ typePiece := setupDocType(mod, t)
+ if typePiece != nil {
+ typePieces = append(typePieces, *typePiece)
+ if typePiece.IsInterface {
+ hasInterfaces = true
}
- dps := DocPiece{
- Doc: funcdoc,
- FuncSig: funcsig,
- FuncName: strings.TrimPrefix(m.Name, prefix[l]),
+ }
+
+ for _, m := range t.Methods {
+ piece := setupDoc(mod, m)
+ if piece == nil {
+ continue
}
- docs[l] = append(docs[l], dps)
- emmyDocs[l] = append(emmyDocs[l], em)
+ 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 {
- if mod == "main" { continue }
- f, _ := os.Create("docs/" + mod + ".txt")
- for _, dps := range v {
- f.WriteString(dps.FuncSig + " > ")
- for _, doc := range dps.Doc {
- if !strings.HasPrefix(doc, "---") {
- f.WriteString(doc + "\n")
+ docPath := "docs/api/" + mod + ".md"
+ if v.HasInterfaces {
+ os.Mkdir("docs/api/" + mod, 0777)
+ 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(`%s`, linkedTyp, typName)
+ })
+ f.WriteString(fmt.Sprintf("## Introduction\n%s\n\n", modDescription))
+ if len(modu.Fields) != 0 {
+ f.WriteString("## Interface fields\n")
+ for _, dps := range modu.Fields {
+ f.WriteString(fmt.Sprintf("- `%s`: ", dps.FuncName))
+ f.WriteString(strings.Join(dps.Doc, " "))
+ f.WriteString("\n")
+ }
+ f.WriteString("\n")
+ }
+ if len(modu.Properties) != 0 {
+ f.WriteString("## Object properties\n")
+ for _, dps := range modu.Properties {
+ f.WriteString(fmt.Sprintf("- `%s`: ", dps.FuncName))
+ f.WriteString(strings.Join(dps.Doc, " "))
+ f.WriteString("\n")
+ }
+ f.WriteString("\n")
+ }
+
+ if len(modu.Docs) != 0 {
+ f.WriteString("## Functions\n")
+ 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(`%s`, 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")
}
}
- f.WriteString("\n")
- }
- }
-
- for mod, v := range emmyDocs {
- if mod == "main" { continue }
- f, _ := os.Create("emmyLuaDocs/" + mod + ".lua")
- f.WriteString("--- @meta\n\nlocal " + mod + " = {}\n\n")
- for _, em := range v {
- var funcdocs []string
- for _, dps := range docs[mod] {
- if dps.FuncName == em.FuncName {
- funcdocs = dps.Doc
+
+ if len(modu.Types) != 0 {
+ f.WriteString("## Types\n")
+ for _, dps := range modu.Types {
+ f.WriteString(fmt.Sprintf("## %s\n", dps.FuncName))
+ for _, doc := range dps.Doc {
+ if !strings.HasPrefix(doc, "---") {
+ f.WriteString(doc + "\n")
+ }
+ }
+ if len(dps.Properties) != 0 {
+ f.WriteString("### Properties\n")
+ for _, dps := range dps.Properties {
+ f.WriteString(fmt.Sprintf("- `%s`: ", dps.FuncName))
+ f.WriteString(strings.Join(dps.Doc, " "))
+ f.WriteString("\n")
+ }
+ }
+ f.WriteString("\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(`%s`, 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")
+ }
}
}
- f.WriteString("--- " + strings.Join(funcdocs, "\n--- ") + "\n")
- if len(em.Docs) != 0 {
- f.WriteString(strings.Join(em.Docs, "\n") + "\n")
+ }(mod, docPath, v)
+
+ go func(md, modname string, modu module) {
+ defer wg.Done()
+
+ if modu.ParentModule != "" {
+ return
}
- f.WriteString("function " + mod + "." + em.FuncName + "(" + strings.Join(em.Params, ", ") + ") end\n\n")
- }
- f.WriteString("return " + mod + "\n")
+
+ 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()
}
diff --git a/complete.go b/complete.go
index 76d65f7..51b426f 100644
--- a/complete.go
+++ b/complete.go
@@ -11,15 +11,49 @@ import (
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{}
sb := &strings.Builder{}
quoted := false
- for _, r := range str {
+ for i, r := range str {
if r == '"' {
quoted = !quoted
sb.WriteRune(r)
+ } else if r == ' ' && str[i - 1] == '\\' {
+ sb.WriteRune(r)
} else if !quoted && r == ' ' {
split = append(split, sb.String())
sb.Reset()
@@ -39,12 +73,22 @@ func splitQuote(str 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) {
+ q := splitForFile(ctx)
+ query = ""
+ if len(q) != 0 {
+ query = q[len(q) - 1]
+ }
+
var completions []string
prefixes := []string{"./", "../", "/", "~/"}
@@ -54,7 +98,7 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
if len(fileCompletions) != 0 {
for _, f := range fileCompletions {
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
}
completions = append(completions, f)
@@ -66,7 +110,6 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
// filter out executables, but in path
for _, dir := range filepath.SplitList(os.Getenv("PATH")) {
- // print dir to stderr for debugging
// search for an executable which matches our query string
if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil {
// get basename from matches
@@ -102,6 +145,7 @@ func matchPath(query string) ([]string, string) {
var entries []string
var baseName string
+ query = escapeInvertReplaer.Replace(query)
path, _ := filepath.Abs(util.ExpandHome(filepath.Dir(query)))
if string(query) == "" {
// filepath base below would give us "."
@@ -112,7 +156,16 @@ func matchPath(query string) ([]string, string) {
}
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) {
entry := file.Name()
if file.IsDir() {
@@ -124,32 +177,20 @@ func matchPath(query string) ([]string, string) {
entries = append(entries, entry)
}
}
+ if !strings.HasPrefix(oldQuery, "\"") {
+ baseName = escapeFilename(baseName)
+ }
return entries, baseName
}
func escapeFilename(fname string) string {
- args := []string{
- "\"", "\\\"",
- "'", "\\'",
- "`", "\\`",
- " ", "\\ ",
- "(", "\\(",
- ")", "\\)",
- "[", "\\[",
- "]", "\\]",
- "$", "\\$",
- "&", "\\&",
- "*", "\\*",
- ">", "\\>",
- "<", "\\<",
- "|", "\\|",
- }
-
- r := strings.NewReplacer(args...)
- return r.Replace(fname)
+ return escapeReplaer.Replace(fname)
}
+// #interface completions
+// tab completions
+// The completions interface deals with tab completions.
func completionLoader(rtm *rt.Runtime) *rt.Table {
exports := map[string]util.LuaExport{
"files": {luaFileComplete, 3, false},
@@ -164,11 +205,26 @@ func completionLoader(rtm *rt.Runtime) *rt.Table {
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) {
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) {
if err := c.CheckNArgs(4); err != nil {
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
}
+// #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) {
query, ctx, fds, err := getCompleteParams(t, c)
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
}
+// #interface completions
+// bins(query, ctx, fields) -> entries (table), prefix (string)
+// Returns binary/executale completion candidates based on the provided query.
+// --- @param query string
+// --- @param ctx string
+// --- @param fields table
func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
query, ctx, fds, err := getCompleteParams(t, c)
if err != nil {
diff --git a/docs/api/_index.md b/docs/api/_index.md
new file mode 100644
index 0000000..8c9f722
--- /dev/null
+++ b/docs/api/_index.md
@@ -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.
diff --git a/docs/api/bait.md b/docs/api/bait.md
new file mode 100644
index 0000000..a70eb17
--- /dev/null
+++ b/docs/api/bait.md
@@ -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`
+
diff --git a/docs/api/commander.md b/docs/api/commander.md
new file mode 100644
index 0000000..341eeda
--- /dev/null
+++ b/docs/api/commander.md
@@ -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 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.
+
+## Functions
+### deregister(name)
+Deregisters any command registered with `name`
+
+### register(name, cb)
+Register a command with `name` that runs `cb` when ran
+
diff --git a/docs/api/fs.md b/docs/api/fs.md
new file mode 100644
index 0000000..ee6949f
--- /dev/null
+++ b/docs/api/fs.md
@@ -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
+
diff --git a/docs/api/hilbish/_index.md b/docs/api/hilbish/_index.md
new file mode 100644
index 0000000..81ca993
--- /dev/null
+++ b/docs/api/hilbish/_index.md
@@ -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.`,
+replacing with the name of the command (for example `command.git`).
+`cb` must be a function that returns a table of "completion groups."
+Check `doc completions` for more information.
+
+### 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) -> Timer
+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) -> Timer
+Runs the `cb` function after `time` in milliseconds.
+This creates a timer that starts immediately.
+
+### which(name) -> string
+Checks if `name` is a valid command.
+Will return the path of the binary, or a basename if it's a commander.
+
+## 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.
+
diff --git a/docs/api/hilbish/hilbish.aliases.md b/docs/api/hilbish/hilbish.aliases.md
new file mode 100644
index 0000000..bae5bfc
--- /dev/null
+++ b/docs/api/hilbish/hilbish.aliases.md
@@ -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\
+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.
+
diff --git a/docs/api/hilbish/hilbish.completions.md b/docs/api/hilbish/hilbish.completions.md
new file mode 100644
index 0000000..6f8740f
--- /dev/null
+++ b/docs/api/hilbish/hilbish.completions.md
@@ -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.
+
diff --git a/docs/api/hilbish/hilbish.editor.md b/docs/api/hilbish/hilbish.editor.md
new file mode 100644
index 0000000..30a3842
--- /dev/null
+++ b/docs/api/hilbish/hilbish.editor.md
@@ -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.
+
diff --git a/docs/api/hilbish/hilbish.history.md b/docs/api/hilbish/hilbish.history.md
new file mode 100644
index 0000000..9fa9b01
--- /dev/null
+++ b/docs/api/hilbish/hilbish.history.md
@@ -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.
+
diff --git a/docs/api/hilbish/hilbish.jobs.md b/docs/api/hilbish/hilbish.jobs.md
new file mode 100644
index 0000000..e41be2c
--- /dev/null
+++ b/docs/api/hilbish/hilbish.jobs.md
@@ -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\<Job>
+Returns a table of all job objects.
+
+### disown(id)
+Disowns a job. This deletes it from the job table.
+
+### get(id) -> Job
+Get a job object via its ID.
+
+### last() -> Job
+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.
+
diff --git a/docs/api/hilbish/hilbish.os.md b/docs/api/hilbish/hilbish.os.md
new file mode 100644
index 0000000..aa2198e
--- /dev/null
+++ b/docs/api/hilbish/hilbish.os.md
@@ -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
+
diff --git a/docs/api/hilbish/hilbish.runner.md b/docs/api/hilbish/hilbish.runner.md
new file mode 100644
index 0000000..68ffdc6
--- /dev/null
+++ b/docs/api/hilbish/hilbish.runner.md
@@ -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`.
+
diff --git a/docs/api/hilbish/hilbish.timers.md b/docs/api/hilbish/hilbish.timers.md
new file mode 100644
index 0000000..e899d1d
--- /dev/null
+++ b/docs/api/hilbish/hilbish.timers.md
@@ -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) -> 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`
+
+### get(id) -> Timer
+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.
+
diff --git a/docs/api/hilbish/hilbish.userDir.md b/docs/api/hilbish/hilbish.userDir.md
new file mode 100644
index 0000000..0b95057
--- /dev/null
+++ b/docs/api/hilbish/hilbish.userDir.md
@@ -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
+
diff --git a/docs/api/terminal.md b/docs/api/terminal.md
new file mode 100644
index 0000000..99d4b49
--- /dev/null
+++ b/docs/api/terminal.md
@@ -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
+
diff --git a/docs/bait.txt b/docs/bait.txt
deleted file mode 100644
index fdc712f..0000000
--- a/docs/bait.txt
+++ /dev/null
@@ -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`
-
diff --git a/docs/commander.txt b/docs/commander.txt
deleted file mode 100644
index 8b4b329..0000000
--- a/docs/commander.txt
+++ /dev/null
@@ -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
-
diff --git a/docs/completions.md b/docs/completions.md
new file mode 100644
index 0000000..c2de27a
--- /dev/null
+++ b/docs/completions.md
@@ -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.`
diff --git a/docs/completions.txt b/docs/completions.txt
deleted file mode 100644
index 1354dc0..0000000
--- a/docs/completions.txt
+++ /dev/null
@@ -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.`
diff --git a/docs/fs.txt b/docs/fs.txt
deleted file mode 100644
index 8372afd..0000000
--- a/docs/fs.txt
+++ /dev/null
@@ -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`
-
diff --git a/docs/hilbish.txt b/docs/hilbish.txt
deleted file mode 100644
index d9763a0..0000000
--- a/docs/hilbish.txt
+++ /dev/null
@@ -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.`,
-replacing with the name of the command (for example `command.git`).
-`cb` must be a function that returns a table of "completion groups."
-Check `doc completions` for more information.
-
-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
-
diff --git a/docs/hooks/index.txt b/docs/hooks/_index.md
similarity index 62%
rename from docs/hooks/index.txt
rename to docs/hooks/_index.md
index f771543..6616b05 100644
--- a/docs/hooks/index.txt
+++ b/docs/hooks/_index.md
@@ -6,3 +6,8 @@ Here is the format for a doc for a hook:
`` just means the arguments of the hook. If a hook doc has the format
of `arg...`, it means the hook can take/recieve any number of `arg`.
+
++ error -> eventName, handler, err > Emitted when there is an error in
+an event handler. The `eventName` is the name of the event the handler
+is for, the `handler` is the callback function, and `err` is the error
+message.
diff --git a/docs/hooks/command.txt b/docs/hooks/command.md
similarity index 67%
rename from docs/hooks/command.txt
rename to docs/hooks/command.md
index f97f7e3..cd1ae3c 100644
--- a/docs/hooks/command.txt
+++ b/docs/hooks/command.md
@@ -3,5 +3,5 @@
+ `command.not-found` -> cmdStr > Thrown when a command is not found.
-+ `command.no-perm` -> cmdStr > Thrown when Hilbish attempts to execute a file but
-has no permission.
++ `command.not-executable` -> cmdStr > Thrown when Hilbish attempts to run a file
+that is not executable.
diff --git a/docs/hooks/hilbish.txt b/docs/hooks/hilbish.md
similarity index 85%
rename from docs/hooks/hilbish.txt
rename to docs/hooks/hilbish.md
index d6d5542..3d6d2ea 100644
--- a/docs/hooks/hilbish.txt
+++ b/docs/hooks/hilbish.md
@@ -5,3 +5,5 @@
+ `hilbish.vimAction` -> actionName, args > Sent when the user does a "vim action," being something
like yanking or pasting text. See `doc vim-mode actions` for more info.
+
++ `hilbish.cancel` > Sent when the user cancels their input with Ctrl-C.
diff --git a/docs/hooks/job.txt b/docs/hooks/job.md
similarity index 100%
rename from docs/hooks/job.txt
rename to docs/hooks/job.md
diff --git a/docs/hooks/signal.txt b/docs/hooks/signal.md
similarity index 100%
rename from docs/hooks/signal.txt
rename to docs/hooks/signal.md
diff --git a/docs/jobs.txt b/docs/jobs.md
similarity index 100%
rename from docs/jobs.txt
rename to docs/jobs.md
diff --git a/docs/lunacolors.txt b/docs/lunacolors.md
similarity index 100%
rename from docs/lunacolors.txt
rename to docs/lunacolors.md
diff --git a/docs/nature/index.txt b/docs/nature/_index.md
similarity index 100%
rename from docs/nature/index.txt
rename to docs/nature/_index.md
diff --git a/docs/runner-mode.txt b/docs/runner-mode.md
similarity index 75%
rename from docs/runner-mode.txt
rename to docs/runner-mode.md
index 5765f18..0b5ce24 100644
--- a/docs/runner-mode.txt
+++ b/docs/runner-mode.md
@@ -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.
+ setMode(mode) > The same as `hilbish.runnerMode`
-+ sh(input) -> input, code, err > Runs `input` in Hilbish's sh interpreter
-+ lua(input) -> input, code, err > Evals `input` as Lua code
++ sh(input) -> table > Runs `input` in Hilbish's sh interpreter
++ lua(input) -> table > Evals `input` as Lua code
+
+The table value that runners return can have at least 4 values:
++ input (string): The full input text.
++ exitCode (number): Exit code (usually from a command)
++ continue (boolean): Whether to prompt the user for more input
+(in the case of incomplete syntax)
++ err (string): A string that represents an error from the runner.
+This should only be set when, for example, there is a syntax error.
+It can be set to a few special values for Hilbish to throw the right
+hooks and have a better looking message.
+
++ `: not-found` will throw a `command.not-found` hook
+based on what `` is.
++ `: not-executable` will throw a `command.not-executable` hook.
The others here are defined in Lua and have EmmyLua documentation.
These functions should be preferred over the previous ones.
diff --git a/docs/terminal.txt b/docs/terminal.txt
deleted file mode 100644
index 7683bbb..0000000
--- a/docs/terminal.txt
+++ /dev/null
@@ -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
-
diff --git a/docs/timers.md b/docs/timers.md
new file mode 100644
index 0000000..1b9c602
--- /dev/null
+++ b/docs/timers.md
@@ -0,0 +1 @@
+This has been moved to the `hilbish.timers` API doc (accessible by `doc api hilbish.timers`)
diff --git a/docs/timers.txt b/docs/timers.txt
deleted file mode 100644
index 0f89718..0000000
--- a/docs/timers.txt
+++ /dev/null
@@ -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
diff --git a/docs/vim-mode/index.txt b/docs/vim-mode/_index.md
similarity index 100%
rename from docs/vim-mode/index.txt
rename to docs/vim-mode/_index.md
diff --git a/docs/vim-mode/actions.txt b/docs/vim-mode/actions.md
similarity index 100%
rename from docs/vim-mode/actions.txt
rename to docs/vim-mode/actions.md
diff --git a/editor.go b/editor.go
index 868f458..3038f07 100644
--- a/editor.go
+++ b/editor.go
@@ -6,6 +6,10 @@ import (
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 {
exports := map[string]util.LuaExport{
"insert": {editorInsert, 1, false},
@@ -20,6 +24,9 @@ func editorLoader(rtm *rt.Runtime) *rt.Table {
return mod
}
+// #interface editor
+// insert(text)
+// Inserts text into the line.
func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
@@ -35,6 +42,11 @@ func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
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) {
if err := c.Check1Arg(); err != nil {
return nil, err
@@ -55,6 +67,10 @@ func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
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) {
if err := c.Check1Arg(); err != nil {
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
}
+// #interface editor
+// getLine() -> string
+// Returns the current input line.
func editorGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
buf := lr.rl.GetLine()
diff --git a/emmyLuaDocs/bait.lua b/emmyLuaDocs/bait.lua
index a5ecebd..35a37ed 100644
--- a/emmyLuaDocs/bait.lua
+++ b/emmyLuaDocs/bait.lua
@@ -12,14 +12,21 @@ function bait.catch(name, cb) end
--- @param cb function
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 bait.hooks(name) end
+
+--- Removes the `catcher` for the event with `name`.
--- For this to work, `catcher` has to be the same function used to catch
--- an event, like one saved to a variable.
-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`
--- @param name string
--- @vararg any
-function bait.throw(name, ...) end
+function bait.throw(name, ...args) end
return bait
diff --git a/emmyLuaDocs/fs.lua b/emmyLuaDocs/fs.lua
index 14e7be4..e974ab9 100644
--- a/emmyLuaDocs/fs.lua
+++ b/emmyLuaDocs/fs.lua
@@ -4,11 +4,13 @@ local fs = {}
--- Gives an absolute version of `path`.
--- @param path string
+--- @returns string
function fs.abs(path) end
--- Gives the basename of `path`. For the rules,
--- see Go's filepath.Base
-function fs.basename() end
+--- @returns string
+function fs.basename(path) end
--- Changes directory to `dir`
--- @param dir string
@@ -16,28 +18,40 @@ function fs.cd(dir) end
--- Returns the directory part of `path`. For the rules, see Go's
--- 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.
--- 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
--- 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.
--- @param name string
--- @param recursive boolean
function fs.mkdir(name, recursive) end
---- Returns a table of files in `dir`
+--- Returns a table of files in `dir`.
--- @param dir string
--- @return table
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
+--- @returns table
function fs.stat(path) end
return fs
diff --git a/emmyLuaDocs/hilbish.lua b/emmyLuaDocs/hilbish.lua
index ca34425..8b20583 100644
--- a/emmyLuaDocs/hilbish.lua
+++ b/emmyLuaDocs/hilbish.lua
@@ -2,6 +2,49 @@
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`
--- @param cmd string
--- @param orig string
@@ -21,6 +64,7 @@ function hilbish.appendPath(dir) end
function hilbish.complete(scope, cb) end
--- Returns the current directory of the shell
+--- @returns string
function hilbish.cwd() end
--- 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,
--- override this function with your custom handler.
--- @param line string
---- @param pos int
+--- @param pos number
function hilbish.hinter(line, pos) end
--- 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
--- 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 time number
---- @return table
+--- @return Timer
function hilbish.interval(cb, time) end
--- Changes the continued line prompt to `str`
@@ -73,20 +117,23 @@ function hilbish.prependPath(dir) end
--- `%u` - Name of current user
--- `%h` - Hostname of device
--- @param str string
---- @param typ string Type of prompt, being left or right. Left by default.
+--- @param typ? string Type of prompt, being left or right. Left by default.
function hilbish.prompt(str, typ) end
--- Read input from the user, using Hilbish's line editor/input reader.
--- This is a separate instance from the one Hilbish actually uses.
--- Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
---- @param prompt string
+--- @param prompt? string
+--- @returns string|nil
function hilbish.read(prompt) end
--- Runs `cmd` in Hilbish's sh interpreter.
--- If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
--- 3rd values instead of being outputted to the terminal.
--- @param cmd string
-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
--- 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
function hilbish.runnerMode(mode) end
---- Runs the `cb` function after `time` in milliseconds
---- Returns a `timer` object (see `doc timers`).
+--- Runs the `cb` function after `time` in milliseconds.
+--- This creates a timer that starts immediately.
--- @param cb function
--- @param time number
---- @return table
+--- @returns Timer
function hilbish.timeout(cb, time) end
---- Checks if `name` is a valid command
---- @param binName string
-function hilbish.which(binName) end
+--- Checks if `name` is a valid command.
+--- Will return the path of the binary, or a basename if it's a commander.
+--- @param name string
+--- @returns string
+function hilbish.which(name) end
+
+--- Puts a job in the background. This acts the same as initially running a job.
+function hilbish.jobs:background() end
+
+--- Returns binary/executale completion candidates based on the provided query.
+--- @param query string
+--- @param ctx string
+--- @param fields table
+function hilbish.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
+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
+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
diff --git a/exec.go b/exec.go
index 6e24719..726a986 100644
--- a/exec.go
+++ b/exec.go
@@ -96,23 +96,23 @@ func runInput(input string, priv bool) {
if currentRunner.Type() == rt.StringType {
switch currentRunner.AsString() {
case "hybrid":
- _, _, err = handleLua(cmdString)
+ _, _, err = handleLua(input)
if err == nil {
cmdFinish(0, input, priv)
return
}
- input, exitCode, cont, err = handleSh(cmdString)
+ input, exitCode, cont, err = handleSh(input)
case "hybridRev":
_, _, _, err = handleSh(input)
if err == nil {
cmdFinish(0, input, priv)
return
}
- input, exitCode, err = handleLua(cmdString)
+ input, exitCode, err = handleLua(input)
case "lua":
- input, exitCode, err = handleLua(cmdString)
+ input, exitCode, err = handleLua(input)
case "sh":
- input, exitCode, cont, err = handleSh(cmdString)
+ input, exitCode, cont, err = handleSh(input)
}
} else {
// can only be a string or function so
@@ -141,9 +141,9 @@ func runInput(input string, priv bool) {
if err != nil {
if exErr, ok := isExecError(err); ok {
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)
}
@@ -195,7 +195,8 @@ func runLuaRunner(runr rt.Value, userInput string) (input string, exitCode uint8
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
chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv()))
if err != nil && noexecute {
@@ -322,8 +323,18 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str))
}
+ hc := interp.HandlerCtx(ctx)
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 {
fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error())
return interp.NewExitStatus(1)
@@ -363,7 +374,6 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
killTimeout := 2 * time.Second
// from here is basically copy-paste of the default exec handler from
// sh/interp but with our job handling
- hc := interp.HandlerCtx(ctx)
path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0])
if err != nil {
fmt.Fprintln(hc.Stderr, err)
@@ -551,7 +561,7 @@ func splitInput(input string) ([]string, string) {
}
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
// results in it being unknown in lua .... ????
// so we allow the hook handler to take lua runtime Values
diff --git a/gallery/tab.png b/gallery/tab.png
new file mode 100644
index 0000000..409d796
Binary files /dev/null and b/gallery/tab.png differ
diff --git a/go.mod b/go.mod
index b86b410..1573917 100644
--- a/go.mod
+++ b/go.mod
@@ -3,11 +3,8 @@ module hilbish
go 1.17
require (
- github.com/Rosettea/Malvales v0.0.0-20220707042337-37a9ee7758f9
github.com/arnodel/golua v0.0.0-20220221163911-dfcf252b6f86
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/pborman/getopt v1.1.0
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/arnodel/strftime v0.1.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/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/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
@@ -42,4 +28,4 @@ replace github.com/maxlandon/readline => ./readline
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
diff --git a/go.sum b/go.sum
index bf2960f..53e83d6 100644
--- a/go.sum
+++ b/go.sum
@@ -1,9 +1,5 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-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/golua v0.0.0-20221213193027-cbf6d4e4d345 h1:QNYjYDogUSiNUkffbhFSrSCtpZhofeiVYGFN2FI4wSs=
+github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345/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/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs=
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/blackfireio/osinfo v1.0.3 h1:Yk2t2GTPjBcESv6nDSWZKO87bGMQgO+Hi9OoXPpxX8c=
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.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc=
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/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/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/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/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.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
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/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/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/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk=
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.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
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.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/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-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4=
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-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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/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-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/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=
diff --git a/golibs/bait/bait.go b/golibs/bait/bait.go
index 89e0c4a..3f3c34e 100644
--- a/golibs/bait/bait.go
+++ b/golibs/bait/bait.go
@@ -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
import (
+ "errors"
+
"hilbish/util"
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...)
if err != nil {
- // panicking here won't actually cause hilbish to panic and instead will
- // print the error and remove the hook. reference the recoverer function in lua.go
+ if event != "error" {
+ 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)
}
} else {
@@ -187,19 +199,11 @@ func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
"catchOnce": util.LuaExport{b.bcatchOnce, 2, false},
"throw": util.LuaExport{b.bthrow, 1, true},
"release": util.LuaExport{b.brelease, 2, false},
+ "hooks": util.LuaExport{b.bhooks, 1, false},
}
mod := rt.NewTable()
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
}
@@ -276,9 +280,11 @@ func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
}
// 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
// an event, like one saved to a variable.
+// --- @param name string
+// --- @param catcher function
func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
name, catcher, err := util.HandleStrCallback(t, c)
if err != nil {
@@ -289,3 +295,35 @@ func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil
}
+
+// hooks(name) -> table
+// Returns a table with hooks (callback functions) on the event with `name`.
+// --- @param name string
+// --- @returns table
+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
+}
diff --git a/golibs/commander/commander.go b/golibs/commander/commander.go
index 24f1c03..c639cf9 100644
--- a/golibs/commander/commander.go
+++ b/golibs/commander/commander.go
@@ -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
import (
@@ -32,7 +62,6 @@ func (c *Commander) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
}
mod := rt.NewTable()
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
}
diff --git a/golibs/fs/fs.go b/golibs/fs/fs.go
index 5b12e73..1c1a5ca 100644
--- a/golibs/fs/fs.go
+++ b/golibs/fs/fs.go
@@ -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
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("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
}
@@ -93,9 +93,15 @@ func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), err
}
-// stat(path)
-// Returns info about `path`
+// 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
// --- @param path string
+// --- @returns table
func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
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
}
-// readdir(dir)
-// Returns a table of files in `dir`
+// readdir(dir) -> {}
+// Returns a table of files in `dir`.
// --- @param dir string
// --- @return table
func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
@@ -145,9 +151,10 @@ func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil
}
-// abs(path)
+// abs(path) -> string
// Gives an absolute version of `path`.
// --- @param path string
+// --- @returns string
func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
path, err := c.StringArg(0)
if err != nil {
@@ -163,9 +170,10 @@ func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.StringValue(abspath)), nil
}
-// basename(path)
+// basename(path) -> string
// Gives the basename of `path`. For the rules,
// see Go's filepath.Base
+// --- @returns string
func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
@@ -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
}
-// dir(path)
+// dir(path) -> string
// Returns the directory part of `path`. For the rules, see Go's
// filepath.Dir
+// --- @param path string
+// --- @returns string
func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
@@ -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
}
-// glob(pattern)
+// glob(pattern) -> matches (table)
// Glob all files and directories that match the pattern.
// For the rules, see Go's filepath.Glob
+// --- @param pattern string
+// --- @returns table
func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
@@ -219,9 +231,11 @@ func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext(t.Runtime, rt.TableValue(luaMatches)), nil
}
-// join(paths...)
+// join(...) -> string
// Takes paths and joins them together with the OS's
// directory separator (forward or backward slash).
+// --- @vararg string
+// --- @returns string
func fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
strs := make([]string, len(c.Etc()))
for i, v := range c.Etc() {
diff --git a/golibs/terminal/terminal.go b/golibs/terminal/terminal.go
index df1755c..2040fac 100644
--- a/golibs/terminal/terminal.go
+++ b/golibs/terminal/terminal.go
@@ -1,3 +1,5 @@
+// low level terminal library
+// The terminal library is a simple and lower level library for certain terminal interactions.
package terminal
import (
@@ -26,7 +28,6 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
mod := rt.NewTable()
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
}
diff --git a/hilbish-git.spec b/hilbish-git.spec
new file mode 100644
index 0000000..77d2e13
--- /dev/null
+++ b/hilbish-git.spec
@@ -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
diff --git a/history.go b/history.go
index a8eb089..51ccf27 100644
--- a/history.go
+++ b/history.go
@@ -73,13 +73,13 @@ func newFileHistory(path string) *fileHistory {
}
}
- itms := []string{""}
lines := strings.Split(string(data), "\n")
+ itms := make([]string, len(lines) - 1)
for i, l := range lines {
if i == len(lines) - 1 {
continue
}
- itms = append(itms, l)
+ itms[i] = l
}
f, err := os.OpenFile(path, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0755)
if err != nil {
diff --git a/job.go b/job.go
index 709cc1f..1beba9c 100644
--- a/job.go
+++ b/job.go
@@ -18,6 +18,16 @@ import (
var jobs *jobHandler
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 {
cmd string
running bool
@@ -110,6 +120,10 @@ func (j *job) getProc() *os.Process {
return nil
}
+// #interface jobs
+// #member
+// start()
+// Starts running the job.
func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
@@ -130,6 +144,10 @@ func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil
}
+// #interface jobs
+// #member
+// stop()
+// Stops the job from running.
func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
@@ -148,6 +166,11 @@ func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
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) {
if err := c.Check1Arg(); err != nil {
return nil, err
@@ -180,6 +203,10 @@ func luaForegroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
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) {
if err := c.Check1Arg(); err != nil {
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 {
jobMethods := rt.NewTable()
jFuncs := map[string]util.LuaExport{
@@ -353,6 +387,11 @@ func jobUserData(j *job) *rt.UserData {
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) {
j.mu.RLock()
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
}
+// #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) {
if err := c.CheckNArgs(3); err != nil {
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
}
+// #interface jobs
+// all() -> table<@Job>
+// Returns a table of all job objects.
+// --- @returns table
func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
j.mu.RLock()
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
}
+// #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) {
if err := c.Check1Arg(); err != nil {
return nil, err
@@ -431,6 +484,10 @@ func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
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) {
j.mu.RLock()
defer j.mu.RUnlock()
diff --git a/libs/lunacolors b/libs/lunacolors
index 8467b87..34a57c9 160000
--- a/libs/lunacolors
+++ b/libs/lunacolors
@@ -1 +1 @@
-Subproject commit 8467b87dd8d49c68b4100b2d129d5f071544b8cf
+Subproject commit 34a57c964590f89aa065188a588c7b38aff99c28
diff --git a/lua.go b/lua.go
index 419970c..0a7c115 100644
--- a/lua.go
+++ b/lua.go
@@ -23,6 +23,7 @@ func luaInit() {
MessageHandler: debuglib.Traceback,
})
lib.LoadAll(l)
+ setupSinkType(l)
lib.LoadLibs(l, hilbishLoader)
// yes this is stupid, i know
@@ -49,7 +50,7 @@ func luaInit() {
hooks = bait.New(l)
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)
})
diff --git a/main.go b/main.go
index ee0f584..4fa321c 100644
--- a/main.go
+++ b/main.go
@@ -115,9 +115,11 @@ func main() {
os.Setenv("SHELL", os.Args[0])
}
- go handleSignals()
lr = newLineReader("", false)
luaInit()
+
+ go handleSignals()
+
// If user's config doesn't exixt,
if _, err := os.Stat(defaultConfPath); os.IsNotExist(err) && *configflag == defaultConfPath {
// Read default from current directory
@@ -181,11 +183,14 @@ input:
break
}
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
fmt.Fprintln(os.Stderr, err)
}
- fmt.Println("^C")
+ // TODO: Halt if any other error occurs
continue
}
var priv bool
diff --git a/nature/commands/bg.lua b/nature/commands/bg.lua
index f0aa462..fbb3543 100644
--- a/nature/commands/bg.lua
+++ b/nature/commands/bg.lua
@@ -1,15 +1,15 @@
local commander = require 'commander'
-commander.register('bg', function()
+commander.register('bg', function(_, sinks)
local job = hilbish.jobs.last()
if not job then
- print 'bg: no last job'
+ sinks.out:writeln 'bg: no last job'
return 1
end
- local err = job.background()
+ local err = job:background()
if err then
- print('bg: ' .. err)
+ sinks.out:writeln('bg: ' .. err)
return 2
end
end)
diff --git a/nature/commands/cat.lua b/nature/commands/cat.lua
new file mode 100644
index 0000000..06df507
--- /dev/null
+++ b/nature/commands/cat.lua
@@ -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)
diff --git a/nature/commands/cd.lua b/nature/commands/cd.lua
index b4d1041..7cfe4a2 100644
--- a/nature/commands/cd.lua
+++ b/nature/commands/cd.lua
@@ -4,32 +4,25 @@ local fs = require 'fs'
local dirs = require 'nature.dirs'
dirs.old = hilbish.cwd()
-commander.register('cd', function (args)
+commander.register('cd', function (args, sinks)
if #args > 1 then
- print("cd: too many arguments")
+ sinks.out:writeln("cd: too many arguments")
return 1
- elseif #args > 0 then
- local path = args[1]:gsub('$%$','\0'):gsub('${([%w_]+)}', os.getenv)
- :gsub('$([%w_]+)', os.getenv):gsub('%z','$'):gsub('^%s*(.-)%s*$', '%1')
-
- if path == '-' then
- path = dirs.old
- print(path)
- end
- dirs.setOld(hilbish.cwd())
- dirs.push(path)
-
- local ok, err = pcall(function() fs.cd(path) end)
- if not ok then
- print(err)
- return 1
- end
- bait.throw('cd', path)
-
- return
end
- fs.cd(hilbish.home)
- bait.throw('cd', hilbish.home)
- dirs.push(hilbish.home)
+ local path = args[1] and args[1] or hilbish.home
+ if path == '-' then
+ path = dirs.old
+ sinks.out:writeln(path)
+ end
+
+ dirs.setOld(hilbish.cwd())
+ dirs.push(path)
+
+ local ok, err = pcall(function() fs.cd(path) end)
+ if not ok then
+ sinks.out:writeln(err)
+ return 1
+ end
+ bait.throw('cd', path)
end)
diff --git a/nature/commands/cdr.lua b/nature/commands/cdr.lua
index 0438e6f..288ae22 100644
--- a/nature/commands/cdr.lua
+++ b/nature/commands/cdr.lua
@@ -3,9 +3,9 @@ local fs = require 'fs'
local lunacolors = require 'lunacolors'
local dirs = require 'nature.dirs'
-commander.register('cdr', function(args)
+commander.register('cdr', function(args, sinks)
if not args[1] then
- print(lunacolors.format [[
+ sinks.out:writeln(lunacolors.format [[
cdr: change directory to one which has been recently visied
usage: cdr
@@ -17,21 +17,21 @@ to get a list of recent directories, use {green}{underline}cdr list{reset}]])
if args[1] == 'list' then
local recentDirs = dirs.recentDirs
if #recentDirs == 0 then
- print 'No directories have been visited.'
+ sinks.out:writeln 'No directories have been visited.'
return 1
end
- print(table.concat(recentDirs, '\n'))
+ sinks.out:writeln(table.concat(recentDirs, '\n'))
return
end
local index = tonumber(args[1])
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
end
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
end
diff --git a/nature/commands/clear.lua b/nature/commands/clear.lua
new file mode 100644
index 0000000..68aa197
--- /dev/null
+++ b/nature/commands/clear.lua
@@ -0,0 +1,7 @@
+local ansikit = require 'ansikit'
+local commander = require 'commander'
+
+commander.register('clear', function()
+ ansikit.clear(true)
+ ansikit.cursorTo(0, 0)
+end)
diff --git a/nature/commands/disown.lua b/nature/commands/disown.lua
index f8f144f..6645a0f 100644
--- a/nature/commands/disown.lua
+++ b/nature/commands/disown.lua
@@ -1,8 +1,8 @@
local commander = require 'commander'
-commander.register('disown', function(args)
+commander.register('disown', function(args, sinks)
if #hilbish.jobs.all() == 0 then
- print 'disown: no current job'
+ sinks.out:writeln 'disown: no current job'
return 1
end
@@ -10,7 +10,7 @@ commander.register('disown', function(args)
if #args < 0 then
id = tonumber(args[1])
if not id then
- print 'disown: invalid id for job'
+ sinks.out:writeln 'disown: invalid id for job'
return 1
end
else
@@ -19,7 +19,7 @@ commander.register('disown', function(args)
local ok = pcall(hilbish.jobs.disown, id)
if not ok then
- print 'disown: job does not exist'
+ sinks.out:writeln 'disown: job does not exist'
return 2
end
end)
diff --git a/nature/commands/doc.lua b/nature/commands/doc.lua
index a290cd8..d37e677 100644
--- a/nature/commands/doc.lua
+++ b/nature/commands/doc.lua
@@ -2,94 +2,99 @@ local commander = require 'commander'
local fs = require 'fs'
local lunacolors = require 'lunacolors'
-commander.register('doc', function(args)
+commander.register('doc', function(args, sinks)
local moddocPath = hilbish.dataDir .. '/docs/'
- local modDocFormat = [[
-%s
-%s
-# Functions
+ local stat = pcall(fs.stat, '.git/refs/heads/extended-job-api')
+ if stat then
+ -- hilbish git
+ 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 [subdoc]
+Available sections: ]] .. table.concat(modules, ', ')
if #args > 0 then
local mod = args[1]
- local f = io.open(moddocPath .. mod .. '.txt', 'rb')
+ local f = io.open(moddocPath .. mod .. '.md', 'rb')
local funcdocs = nil
+ local subdocName = args[2]
if not f then
-- assume subdir
- -- dataDir/docs//.txt
+ -- dataDir/docs//.md
moddocPath = moddocPath .. mod .. '/'
- local subdocName = args[2]
if not subdocName then
- subdocName = 'index'
+ subdocName = '_index'
end
- f = io.open(moddocPath .. subdocName .. '.txt', 'rb')
+ f = io.open(moddocPath .. subdocName .. '.md', 'rb')
if not f then
- print('No documentation found for ' .. mod .. '.')
- return
+ f = io.open(moddocPath .. subdocName:match '%w+' .. '/' .. subdocName .. '.md', 'rb')
end
- funcdocs = f:read '*a'
- local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= 'index.txt' end)
- local subdocs = table.map(moddocs, function(fname)
- return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.txt', '')))
- end)
- if subdocName == 'index' then
- funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ')
+ if not f then
+ 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)
+ return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.md', '')))
+ end)
+ if #moddocs ~= 0 then
+ funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ')
end
- if not funcdocs then
- funcdocs = f:read '*a'
- end
- local desc = ''
- local ok = pcall(require, mod)
- local backtickOccurence = 0
- local formattedFuncs = lunacolors.format(funcdocs:sub(1, #funcdocs - 1):gsub('`', function()
- backtickOccurence = backtickOccurence + 1
- if backtickOccurence % 2 == 0 then
- return '{reset}'
- else
- return '{underline}{green}'
- 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)
- if ok then
- local props = {}
- 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)
+ -- 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
- 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)
+ 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()
-
- 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 [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, ', '))
+ local backtickOccurence = 0
+ sinks.out:writeln(lunacolors.format(doc:gsub('`', function()
+ backtickOccurence = backtickOccurence + 1
+ if backtickOccurence % 2 == 0 then
+ return '{reset}'
+ else
+ return '{underline}{green}'
+ end
+ end):gsub('\n#+.-\n', function(t)
+ local signature = t:gsub('<.->(.-)', '{underline}%1'):gsub('\\', '<')
+ return '{bold}{yellow}' .. signature .. '{reset}'
+ end)))
end)
diff --git a/nature/commands/exec.lua b/nature/commands/exec.lua
new file mode 100644
index 0000000..d279e31
--- /dev/null
+++ b/nature/commands/exec.lua
@@ -0,0 +1,5 @@
+local commander = require 'commander'
+
+commander.register('exec', function(args)
+ hilbish.exec(args[1])
+end)
diff --git a/nature/commands/fg.lua b/nature/commands/fg.lua
index a3f1451..c5b6738 100644
--- a/nature/commands/fg.lua
+++ b/nature/commands/fg.lua
@@ -1,15 +1,15 @@
local commander = require 'commander'
-commander.register('fg', function()
+commander.register('fg', function(_, sinks)
local job = hilbish.jobs.last()
if not job then
- print 'fg: no last job'
+ sinks.out:writeln 'fg: no last job'
return 1
end
- local err = job.foreground() -- waits for job; blocks
+ local err = job:foreground() -- waits for job; blocks
if err then
- print('fg: ' .. err)
+ sinks.out:writeln('fg: ' .. err)
return 2
end
end)
diff --git a/nature/completions.lua b/nature/completions.lua
index d20cc59..f8127a1 100644
--- a/nature/completions.lua
+++ b/nature/completions.lua
@@ -24,7 +24,7 @@ function hilbish.completion.handler(line, pos)
return {compGroup}, pfx
else
local ok, compGroups, pfx = pcall(hilbish.completion.call,
- 'command.' .. #fields[1], query, ctx, fields)
+ 'command.' .. fields[1], query, ctx, fields)
if ok then
return compGroups, pfx
end
diff --git a/nature/init.lua b/nature/init.lua
index df31d8d..d1f919c 100644
--- a/nature/init.lua
+++ b/nature/init.lua
@@ -1,5 +1,6 @@
-- Prelude initializes everything else for our shell
local _ = require 'succulent' -- Function additions
+local bait = require 'bait'
local fs = require 'fs'
package.path = package.path .. ';' .. hilbish.dataDir .. '/?/init.lua'
@@ -28,7 +29,9 @@ do
return got_virt
end
- virt_G[key] = os.getenv(key)
+ if type(key) == 'string' then
+ virt_G[key] = os.getenv(key)
+ end
return virt_G[key]
end,
@@ -54,7 +57,6 @@ do
if ok then
for _, module in ipairs(modules) do
local entry = package.searchpath(module, startSearchPath)
- print(entry)
if entry then
dofile(entry)
end
@@ -63,3 +65,15 @@ do
package.path = package.path .. ';' .. startSearchPath
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)
diff --git a/nature/opts/history.lua b/nature/opts/history.lua
index f7ab1d7..016b22d 100644
--- a/nature/opts/history.lua
+++ b/nature/opts/history.lua
@@ -1,5 +1,6 @@
local bait = require 'bait'
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
end)
diff --git a/nature/opts/motd.lua b/nature/opts/motd.lua
index 5f30a6c..79954d6 100644
--- a/nature/opts/motd.lua
+++ b/nature/opts/motd.lua
@@ -2,8 +2,8 @@ local bait = require 'bait'
local lunacolors = require 'lunacolors'
hilbish.motd = [[
-Hilbish 2.0 is a {red}major{reset} update! If your config doesn't work
-anymore, that will definitely be why! A MOTD, very message, much day.
+1000 commits on the Hilbish repository brings us to {cyan}Version 2.1!{reset}
+Docs, docs, docs... At least builtins work with pipes now.
]]
bait.catch('hilbish.init', function()
diff --git a/nature/runner.lua b/nature/runner.lua
index e155f63..235ab77 100644
--- a/nature/runner.lua
+++ b/nature/runner.lua
@@ -1,3 +1,4 @@
+--- hilbish.runner
local currentRunner = 'hybrid'
local runners = {}
@@ -74,6 +75,12 @@ function hilbish.runner.setCurrent(name)
hilbish.runner.setMode(r.run)
end
+--- Returns the current runner by name.
+--- @returns string
+function hilbish.runner.getCurrent()
+ return currentRunner
+end
+
hilbish.runner.add('hybrid', function(input)
local cmdStr = hilbish.aliases.resolve(input)
diff --git a/os.go b/os.go
new file mode 100644
index 0000000..da9eadd
--- /dev/null
+++ b/os.go
@@ -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
+}
diff --git a/readline/comp-grid.go b/readline/comp-grid.go
index 48a2039..c198bdb 100644
--- a/readline/comp-grid.go
+++ b/readline/comp-grid.go
@@ -4,7 +4,8 @@ import (
"fmt"
"strconv"
"strings"
-)
+ "github.com/rivo/uniseg"
+)
// initGrid - Grid display details. Called each time we want to be sure to have
// a working completion group either immediately, or later on. Generally defered.
@@ -13,8 +14,8 @@ func (g *CompletionGroup) initGrid(rl *Instance) {
// Compute size of each completion item box
tcMaxLength := 1
for i := range g.Suggestions {
- if len(g.Suggestions[i]) > tcMaxLength {
- tcMaxLength = len([]rune(g.Suggestions[i]))
+ if uniseg.GraphemeClusterCount(g.Suggestions[i]) > tcMaxLength {
+ tcMaxLength = uniseg.GraphemeClusterCount(g.Suggestions[i])
}
}
@@ -103,7 +104,7 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) {
rl.tcUsedY++
}
- cellWidth := strconv.Itoa((GetTermWidth() / g.tcMaxX) - 2)
+ cellWidth := strconv.Itoa((GetTermWidth() / g.tcMaxX) - 4)
x := 0
y := 1
@@ -124,7 +125,15 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) {
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
diff --git a/readline/history.go b/readline/history.go
index 41200c6..f772813 100644
--- a/readline/history.go
+++ b/readline/history.go
@@ -123,23 +123,20 @@ func (rl *Instance) walkHistory(i int) {
// When we are exiting the current line buffer to move around
// 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)
}
- switch rl.histPos + i {
- case 0, history.Len() + 1:
- rl.histPos = 0
+ rl.histOffset += i
+ if rl.histOffset == 0 {
rl.line = []rune(rl.lineBuf)
rl.pos = len(rl.lineBuf)
- return
- case -1:
- rl.histPos = 0
- rl.lineBuf = string(rl.line)
- default:
+ } else if rl.histOffset <= -1 {
+ rl.histOffset = 0
+ } else {
dedup = true
old = string(rl.line)
- new, err = history.GetLine(history.Len() - rl.histPos - 1)
+ new, err = history.GetLine(history.Len() - rl.histOffset)
if err != nil {
rl.resetHelpers()
print("\r\n" + err.Error() + "\r\n")
@@ -148,7 +145,6 @@ func (rl *Instance) walkHistory(i int) {
}
rl.clearLine()
- rl.histPos += i
rl.line = []rune(new)
rl.pos = len(rl.line)
if rl.pos > 0 {
diff --git a/readline/instance.go b/readline/instance.go
index fcd8379..039f040 100644
--- a/readline/instance.go
+++ b/readline/instance.go
@@ -134,6 +134,7 @@ type Instance struct {
// history operating params
lineBuf string
histPos int
+ histOffset int
histNavIdx int // Used for quick history navigation.
//
diff --git a/readline/readline.go b/readline/readline.go
index 731e297..f1d6c96 100644
--- a/readline/readline.go
+++ b/readline/readline.go
@@ -49,7 +49,7 @@ func (rl *Instance) Readline() (string, error) {
// History Init
// 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}}
// Multisplit
@@ -238,7 +238,9 @@ func (rl *Instance) Readline() (string, error) {
// Normal completion search does only refresh the search pattern and the comps
if rl.modeTabFind || rl.modeAutoFind {
+ rl.resetVirtualComp(false)
rl.backspaceTabFind()
+ rl.renderHelpers()
rl.viUndoSkipAppend = true
} else {
// Always cancel any virtual completion
@@ -331,6 +333,8 @@ func (rl *Instance) Readline() (string, error) {
rl.modeTabFind = true
rl.updateTabFind([]rune{})
+ rl.updateVirtualComp()
+ rl.renderHelpers()
rl.viUndoSkipAppend = true
// Tab Completion & Completion Search ---------------------------------------------------------------
@@ -484,7 +488,10 @@ func (rl *Instance) Readline() (string, error) {
if string(r[:i]) != seqShiftTab &&
string(r[:i]) != seqForwards && string(r[:i]) != seqBackwards &&
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 {
rl.resetVirtualComp(false)
rl.updateTabFind(r[:i])
+ rl.renderHelpers()
rl.viUndoSkipAppend = true
+ continue
} else {
rl.resetVirtualComp(false)
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
// accordingly.
func (rl *Instance) editorInput(r []rune) {
+ if len(r) == 0 {
+ return
+ }
+
switch rl.modeViMode {
case VimKeys:
rl.vi(r[0])
@@ -604,6 +617,7 @@ func (rl *Instance) escapeSeq(r []rune) {
case string(charEscape):
switch {
case rl.modeAutoFind:
+ rl.resetVirtualComp(true)
rl.resetTabFind()
rl.clearHelpers()
rl.resetTabCompletion()
@@ -611,6 +625,7 @@ func (rl *Instance) escapeSeq(r []rune) {
rl.renderHelpers()
case rl.modeTabFind:
+ rl.resetVirtualComp(true)
rl.resetTabFind()
rl.resetTabCompletion()
diff --git a/readline/tab-virtual.go b/readline/tab-virtual.go
index fa7318e..d1e1d76 100644
--- a/readline/tab-virtual.go
+++ b/readline/tab-virtual.go
@@ -2,6 +2,7 @@ package readline
import (
"strings"
+ "github.com/rivo/uniseg"
)
// 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
case pos >= len(word)-1:
word = rTrimWhiteSpace(split[index+1])
- adjust = len(split[index]) - pos
- adjust += len(word) - 1
+ adjust = uniseg.GraphemeClusterCount(split[index]) - pos
+ adjust += uniseg.GraphemeClusterCount(word) - 1
default:
- adjust = len(word) - pos - 1
+ adjust = uniseg.GraphemeClusterCount(word) - pos - 1
}
return
}
diff --git a/readline/update.go b/readline/update.go
index 0c2de38..8f85c6d 100644
--- a/readline/update.go
+++ b/readline/update.go
@@ -1,6 +1,10 @@
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:
// it should coordinate reprinting the input line, any Infos and completions
@@ -52,19 +56,19 @@ func (rl *Instance) updateReferences() {
rl.posY = 0
rl.fullY = 0
- var fullLine, cPosLine int
+ var curLine []rune
if len(rl.currentComp) > 0 {
- fullLine = getWidth(rl.lineComp)
- cPosLine = getWidth(rl.lineComp[:rl.pos])
+ curLine = rl.lineComp
} else {
- fullLine = getWidth(rl.line)
- cPosLine = getWidth(rl.line[:rl.pos])
+ curLine = rl.line
}
+ fullLine := getWidth(curLine)
+ cPosLine := getWidth(curLine[:rl.pos])
// We need the X offset of the whole line
toEndLine := rl.promptLen + fullLine
fullOffset := toEndLine / GetTermWidth()
- rl.fullY = fullOffset
+ rl.fullY = fullOffset + strings.Count(string(curLine), "\n")
fullRest := toEndLine % GetTermWidth()
rl.fullX = fullRest
diff --git a/readline/vim.go b/readline/vim.go
index 886927b..d496705 100644
--- a/readline/vim.go
+++ b/readline/vim.go
@@ -245,7 +245,7 @@ func (rl *Instance) vi(r rune) {
}
// Keep the previous cursor position
- prev := rl.pos
+ //prev := rl.pos
new, err := rl.StartEditorWithBuffer(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.
rl.clearLine()
rl.line = new
- if prev > len(rl.line) {
- rl.pos = len(rl.line) - 1
+ rl.pos = len(rl.line)
+ /*if prev > len(rl.line) {
} else {
rl.pos = prev
- }
+ }*/
case 'w':
// If we were not yanking
diff --git a/rl.go b/rl.go
index f6cb6cd..96b8451 100644
--- a/rl.go
+++ b/rl.go
@@ -225,7 +225,11 @@ func (lr *lineReader) Resize() {
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 {
lrLua := map[string]util.LuaExport{
"add": {lr.luaAddHistory, 1, false},
@@ -241,6 +245,10 @@ func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table {
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) {
if err := c.Check1Arg(); err != nil {
return nil, err
@@ -254,10 +262,18 @@ func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
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) {
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) {
if err := c.Check1Arg(); err != nil {
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
}
+// #interface history
+// all() -> table
+// Retrieves all history.
+// --- @returns table
func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
tbl := rt.NewTable()
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
}
+// #interface history
+// clear()
+// Deletes all commands from the history.
func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
lr.fileHist.clear()
return c.Next(), nil
diff --git a/rpkg.conf b/rpkg.conf
new file mode 100644
index 0000000..957dc05
--- /dev/null
+++ b/rpkg.conf
@@ -0,0 +1,2 @@
+[rpkg]
+user_macros = "${git_props:root}/rpkg.macros"
diff --git a/rpkg.macros b/rpkg.macros
new file mode 100644
index 0000000..dbcf187
--- /dev/null
+++ b/rpkg.macros
@@ -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"
+}
diff --git a/runnermode.go b/runnermode.go
index b8995cd..8e9e7b9 100644
--- a/runnermode.go
+++ b/runnermode.go
@@ -6,6 +6,13 @@ import (
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 {
exports := map[string]util.LuaExport{
"sh": {shRunner, 1, false},
@@ -19,6 +26,20 @@ func runnerModeLoader(rtm *rt.Runtime) *rt.Table {
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) {
if err := c.Check1Arg(); err != nil {
return nil, err
@@ -28,13 +49,13 @@ func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return nil, err
}
- input, exitCode, cont, err := execSh(cmd)
+ _, exitCode, cont, err := execSh(aliases.Resolve(cmd))
var luaErr rt.Value = rt.NilValue
if err != nil {
luaErr = rt.StringValue(err.Error())
}
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("continue"), rt.BoolValue(cont))
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
}
+// #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) {
if err := c.Check1Arg(); err != nil {
return nil, err
diff --git a/sink.go b/sink.go
new file mode 100644
index 0000000..9a98856
--- /dev/null
+++ b/sink.go
@@ -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())
+}
diff --git a/timer.go b/timer.go
index 74d13c4..5d536f5 100644
--- a/timer.go
+++ b/timer.go
@@ -15,13 +15,19 @@ const (
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{
id int
typ timerType
running bool
dur time.Duration
fun *rt.Closure
- th *timerHandler
+ th *timersModule
ticker *time.Ticker
ud *rt.UserData
channel chan struct{}
@@ -73,6 +79,10 @@ func (t *timer) stop() error {
return nil
}
+// #interface timers
+// #member
+// start()
+// Starts a timer.
func timerStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
@@ -91,6 +101,10 @@ func timerStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil
}
+// #interface timers
+// #member
+// stop()
+// Stops a timer.
func timerStop(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
diff --git a/timerhandler.go b/timerhandler.go
index 64caff8..0cb4197 100644
--- a/timerhandler.go
+++ b/timerhandler.go
@@ -10,10 +10,10 @@ import (
rt "github.com/arnodel/golua/runtime"
)
-var timers *timerHandler
+var timers *timersModule
var timerMetaKey = rt.StringValue("hshtimer")
-type timerHandler struct {
+type timersModule struct {
mu *sync.RWMutex
wg *sync.WaitGroup
timers map[int]*timer
@@ -21,8 +21,8 @@ type timerHandler struct {
running int
}
-func newTimerHandler() *timerHandler {
- return &timerHandler{
+func newTimersModule() *timersModule {
+ return &timersModule{
timers: make(map[int]*timer),
latestID: 0,
mu: &sync.RWMutex{},
@@ -30,11 +30,11 @@ func newTimerHandler() *timerHandler {
}
}
-func (th *timerHandler) wait() {
+func (th *timersModule) 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()
defer th.mu.Unlock()
@@ -54,14 +54,21 @@ func (th *timerHandler) create(typ timerType, dur time.Duration, fun *rt.Closure
return t
}
-func (th *timerHandler) get(id int) *timer {
+func (th *timersModule) get(id int) *timer {
th.mu.RLock()
defer th.mu.RUnlock()
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 {
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
}
-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 {
return nil, err
}
@@ -100,7 +112,34 @@ func (th *timerHandler) luaGet(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
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()
timerFuncs := map[string]util.LuaExport{
"start": {timerStart, 1, false},
@@ -141,6 +180,9 @@ func (th *timerHandler) loader(rtm *rt.Runtime) *rt.Table {
luaTh := rt.NewTable()
util.SetExports(rtm, luaTh, thExports)
+ util.SetField(rtm, luaTh, "INTERVAL", rt.IntValue(0))
+ util.SetField(rtm, luaTh, "TIMEOUT", rt.IntValue(1))
+
return luaTh
}
diff --git a/userdir.go b/userdir.go
new file mode 100644
index 0000000..a6c4852
--- /dev/null
+++ b/userdir.go
@@ -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
+}
diff --git a/util/util.go b/util/util.go
index d27cfe1..45e33dc 100644
--- a/util/util.go
+++ b/util/util.go
@@ -10,53 +10,18 @@ import (
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.
// 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
- SetFieldDoc(module, field, doc)
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
// 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
// user facing table.
-func SetFieldProtected(module, realModule *rt.Table, field string, value rt.Value, doc string) {
- SetFieldDoc(module, field, doc)
+func SetFieldProtected(module, realModule *rt.Table, field string, value rt.Value) {
realModule.Set(rt.StringValue(field), value)
}
diff --git a/vars.go b/vars.go
index 810c1ee..7cbc768 100644
--- a/vars.go
+++ b/vars.go
@@ -11,8 +11,8 @@ var (
// Version info
var (
- ver = "v2.0.0"
- releaseName = "Hibiscus"
+ ver = "v2.2.0"
+ releaseName = "Poppy"
gitCommit string
gitBranch string
)
diff --git a/vars_linux.go b/vars_linux.go
index 815ba6a..e1160ba 100644
--- a/vars_linux.go
+++ b/vars_linux.go
@@ -14,7 +14,7 @@ var (
.. hilbish.userDir.config .. '/hilbish/?/init.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"
sampleConfPath = dataDir + "/.hilbishrc.lua" // Path to default/sample config
defaultConfDir = ""
diff --git a/website/archetypes/default.md b/website/archetypes/default.md
new file mode 100644
index 0000000..00e77bd
--- /dev/null
+++ b/website/archetypes/default.md
@@ -0,0 +1,6 @@
+---
+title: "{{ replace .Name "-" " " | title }}"
+date: {{ .Date }}
+draft: true
+---
+
diff --git a/website/config.toml b/website/config.toml
new file mode 100644
index 0000000..31f42d5
--- /dev/null
+++ b/website/config.toml
@@ -0,0 +1,35 @@
+baseURL = 'https://rosettea.github.io/Hilbish/'
+languageCode = 'en-us'
+title = 'Hilbish'
+theme = 'hsh'
+enableGitInfo = true
+
+[menu]
+[[menu.nav]]
+ identifier = 'home'
+ name = 'Home'
+ pageref = '/'
+ weight = 1
+[[menu.nav]]
+ identifier = 'install'
+ name = 'Install'
+ pageref = '/install'
+ weight = 2
+[[menu.nav]]
+ identifier = 'docs'
+ name = 'Docs'
+ pageref = '/docs'
+ weight = 3
+[[menu.nav]]
+ identifier = 'blog'
+ name = 'Blog'
+ pageref = '/blog'
+ weight = 4
+
+[markup.goldmark.renderer]
+unsafe = true
+
+[author]
+ [author.sammyette]
+ name = 'sammyette'
+ picture = 'https://avatars1.githubusercontent.com/u/38820196?s=460&u=b9f4efb2375bae6cb30656d790c6e0a2939327c0&v=4'
diff --git a/website/content/_index.md b/website/content/_index.md
new file mode 100644
index 0000000..2b1087b
--- /dev/null
+++ b/website/content/_index.md
@@ -0,0 +1,134 @@
+---
+description: 'Something Unique. Hilbish is the new interactive shell for Lua fans. Extensible, scriptable, configurable: All in Lua.'
+---
+
+[//]: <>
+
+
+
+
Something Unique.
+
+ Hilbish is the new interactive shell for Lua fans.
+ Extensible, scriptable, configurable: All in Lua.
+
+ Hilbish is configured and scripted in the Lua programming language.
+ This removes all the old, ugly things about Shell script and introduces
+ everything good about Lua, including other languages (Moonscript & Fennel).
+
+ Hilbish offers a bunch of features to make your interactive
+ shell experience rich. Things like syntax highlighting and hinting
+ available via the Lua API.
+
+
* Command hints shown in photo are not default.
+
+
+
+
+
+
+
+
+
+
+
+
Why not just Lua?
+
+ Hilbish is your interactive shell as well as a just a Lua interpreter
+ and enhanced REPL.
+
+
+
Batteries included Lua runtime that's also your user shell!
+
Hilbish is easily cross platform. It has OS agnostic interfaces for easy cross platform Lua code.
+
+
+
+
+
Try It Today!
+
+ Hilbish is known to run on the 3 major platforms (Windows, MacOS, Linux)
+ but likely builds on other Unixes!
+
+ Windows doesn't work as well as it should, so if you're a Windows user,
+ say something!
+
+
diff --git a/website/content/blog/v2.0-release.md b/website/content/blog/v2.0-release.md
new file mode 100644
index 0000000..23b8f6f
--- /dev/null
+++ b/website/content/blog/v2.0-release.md
@@ -0,0 +1,114 @@
+---
+title: "Hilbish v2.0 Release"
+date: 2022-12-29T01:55:21+00:00
+---
+
+Hilbish v2.0 has been released!
+Well actually, it was released a week ago, but I only wrote this
+Hilbish blog *after* that.
+
+This is a **big** release, coming 9 months after the previous v1.2.0 and
+featuring over 40+ bug fixes and tons of new features and enhancements, so
+let's see what is in this release.
+
+# Documentation
+When querying about the problems people have with Hilbish, one of the
+issues was its poor documentation. Hilbish had plain text, autogenerated
+documentation which only covered the module functions (bait, hilbish,
+commander, etc.) and did not include the interfaces (`hilbish.timers`,
+`hilbish.jobs` and all that).
+
+I have tried to improve this by working on documenting all the
+interfaces (except for some functions of `hilbish.runner`, that's hard to do)
+and made the documentation markdown for use on this website. This means
+that users can look at documentation here or with the `doc` command.
+
+Hopefully this addresses documentation complaints, and if not, please open an issue.
+
+# Main Bug Fixes
+As this is a piece of software with no unit testing that is maintained by me alone,
+there is gonna be either some bug or something that I overlooked when
+making a change. I make a lot of mistakes. There's also the other fact that
+sometimes there's just bugs for any other reasosn. Good thing I fixed
+more than 40 of those bugs in this release!
+
+## Readline Bug Fixes
+The pure Go readline library is good in some ways and bad in others.
+A good portion of the bug fixes are for the readline library, and also
+related to text input with east asian characters and the like (Korean, Japanese,
+etc.)
+
+A few of the fixes (and additions) include:
+
+- Fixing various crashes, including when there is a "stray" newline at the end of text
+- Grid completion menu causing spam and duplicate text when there are items longer than
+the terminal and/or contain Japanese or other characters.
+- Cursor positioning with CJK characters
+- Adding new keybinds and fixing others
+
+## Other fixes
+There are a lot more fixes, even more than the ones listed here, but these are the main ones:
+ - Don't put alias expanded command in history (I've fixed this 5 times now....)
+ - Handle stdin being nonblocking
+ - Completion related fixes, like showing the full name, completing files with spaces
+
+# Breaking changes
+This release is a major version bump not only because there are tons of fixes, but because
+there are breaking changes. This means that there are some changes done which would
+cause errors with an old user config (breaking).
+
+## Lua 5.4
+The most important is the use of a new Lua VM library. Previously, Hilbish
+used gopher-lua, which implements Lua 5.1. This has been changed to
+[golua](https://github.com/arnodel/golua/), which implements Lua 5.4.
+
+Moving from 5.1 to 5.4 does have breaking changes even if it doesn't seem like it,
+and since these are different Lua implementations, there may be some differences there too.
+
+## Userdata
+Previously, objects such as jobs or timers were represented by tables.
+This has been changed to userdata to make more sense.
+
+## Other changes
+Runner functions are now required to return a table.
+It can (at the moment) have 4 variables:
+ - `input` (user input)
+ - `exitCode` (exit code)
+ - `error` (error message)
+ - `continue` (whether to prompt for more input)
+User input has been added to the return to account for runners wanting to
+prompt for continued input, and to add it properly to history. `continue`
+got added so that it would be easier for runners to get continued input
+without having to actually handle it at all.
+
+The MacOS config paths now match Linux, since it makes more sense for
+a program like Hilbish.
+
+The Hilbish greeting is now an *opt*, and is printed by default.
+
+# Feature Additions
+Besides fixes and changes, this release also includes a good portion of
+new features! Users can now add handlers for syntax highlighting and
+inline hinting.
+
+Some new hooks have been added, like `hilbish.cancel` and `hilbish.init`.
+You can look at all the hooks via the `doc hooks` command
+
+Job management functions have also been added. You can now put jobs in the
+foreground/background and disown them via the expected commands and also
+via the Lua API.
+
+The `hilbish.timers` API interface was also added in this release!
+
+# Closing Off
+Hilbish has gone from something small and simple for myself to a slightly
+advanced shell with a decent amount of features, and a few users. It
+still hasn't reached levels of other alt shells in regards to literally
+everything, but the goal is to get there!
+
+If you want to check the FULL changelog, you can [do so here.](https://github.com/Rosettea/Hilbish/releases/tag/v2.0.0)
+This v2.0 release marks an advancement in Hilbish (and also how long
+one of my projects hasn't died) and I hope it can advance even further.
+
+Thanks for reading, and I'll be back for the v2.1 release notes, or maybe
+something else in between.
diff --git a/website/content/blog/v2.1-release.md b/website/content/blog/v2.1-release.md
new file mode 100644
index 0000000..b2e4a17
--- /dev/null
+++ b/website/content/blog/v2.1-release.md
@@ -0,0 +1,64 @@
+---
+title: "v2.1 Release"
+date: 2023-02-07T18:25:38-04:00
+draft: false
+---
+
+> The release with full changelogs and prebuilt binaries can be
+seen at the [v2.1.0](https://github.com/Rosettea/Hilbish/releases/tag/v2.1.0)
+tag.
+
+Oh look! A new release of Hilbish! This time is the v2.1 release,
+with a small amount of features and mainly documentation changes and
+bug fixes.
+
+# Documentation
+There have been a few documentation enhancements for this release.
+This includes:
+- Adding the return types for all functions that need them
+- Documenting Hilbish types like job objects and timers properly.
+They now have a separate heading and listing of properties and methods.
+- Fixing outdated documentation
+
+# Features
+## Sinks
+A major addition is the new "sink" type for commanders to write
+their output to. This was the solution to pipes and other shell
+operators not working with builtins. If you wrote a commander
+and made it `print`, use `sinks.out:write` instead.
+
+This is also documented at the [commander docs](./docs/api/commander).
+
+## `doc` command
+Since API documentation has been moved to an API folder and also includes
+interfaces, a change has been made to get the module name from the
+passed from the requested page. This means that
+`doc api hilbish hilbish.jobs` is now shortened to `doc api hilbish.jobs`
+
+# Bug Fixes
+Small release, small amount of bug fixes. Even though, this is the main
+part of this release.
+
+## Completions and Symlinks
+Previously Hilbish completions did not work with symlinks properly.
+This can be tested in the previous 2.0 release by attempting to
+path complete to `/bin`. Since this is (or can be?) a symlink to
+`/usr/bin`, it was not marked as a directory and therefore did not
+automatically add the ending slash. This has been fixed.
+
+## Segfaults
+I found that when I updated my terminal of choice ([Tym]) for the new
+daemon feature, Hilbish would sometimes segfault on startup. This is due
+to it getting a resize event on startup while `bait` was not initialized
+yet.
+
+## API Fixes
+- The `hilbish.which` function works with aliases.
+- `hilbish.completion.files` and `hilbish.completion.bins` will no longer
+cause a panic with all empty arguments passed.
+
+# Next Release
+Stay tuned for the v2.2 release, which will have a bigger set of features
+and maybe some more bug fixes!
+
+[Tym]: https://github.com/endaaman/tym
diff --git a/website/content/blog/welcome.md b/website/content/blog/welcome.md
new file mode 100644
index 0000000..16a878d
--- /dev/null
+++ b/website/content/blog/welcome.md
@@ -0,0 +1,6 @@
+---
+title: "Welcome to the Hilbish blog"
+---
+
+Hello! Welcome to the Hilbish blog. This will mainly contain release
+announcements and some other things relating to Hilbish (development).
diff --git a/website/content/docs/_index.md b/website/content/docs/_index.md
new file mode 100644
index 0000000..32dbf84
--- /dev/null
+++ b/website/content/docs/_index.md
@@ -0,0 +1,13 @@
+---
+title: Introduction
+layout: doc
+weight: -1
+menu: docs
+---
+
+Hilbish is a hyper-extensible shell mainly intended for interactive use.
+To enhance the interactive experience, Hilbish comes with a wide range
+of features and sane defaults, including a nice looking prompt,
+advanced completion menus and history search.
+
+Here documents some of the features of Hilbish and the Lua API.
diff --git a/website/content/docs/api b/website/content/docs/api
new file mode 120000
index 0000000..1c5c360
--- /dev/null
+++ b/website/content/docs/api
@@ -0,0 +1 @@
+../../../docs/api/
\ No newline at end of file
diff --git a/website/content/docs/faq.md b/website/content/docs/faq.md
new file mode 100644
index 0000000..997fbaa
--- /dev/null
+++ b/website/content/docs/faq.md
@@ -0,0 +1,25 @@
+---
+title: Frequently Asked Questions
+layout: doc
+weight: -20
+menu: docs
+---
+
+# Is Hilbish POSIX compliant?
+No, it is not. POSIX compliance is a non-goal. Perhaps in the future,
+someone would be able to write a native plugin to support shell scripting
+(which would be against it's main goal, but ....)
+
+# Windows Support?
+It compiles for Windows (CI ensures it does), but otherwise it is not
+directly supported. If you'd like to improve this situation,
+checkout [the discussion](https://github.com/Rosettea/Hilbish/discussions/165).
+
+# Where is the API documentation?
+The builtin `doc` command supplies all documentation of Hilbish provided
+APIs. You can also check the sidebar.
+
+# Why?
+Hilbish emerged from the desire of a Lua configured shell.
+It was the initial reason that it was created, but now it's more:
+to be hyper extensible, simpler and more user friendly.
diff --git a/website/content/docs/features/_index.md b/website/content/docs/features/_index.md
new file mode 100644
index 0000000..0e14346
--- /dev/null
+++ b/website/content/docs/features/_index.md
@@ -0,0 +1,11 @@
+---
+title: Features
+layout: doc
+weight: -40
+menu: docs
+---
+
+Hilbish has a wide range of features to enhance the user's experience and
+is always adding new ones. If there is something missing here or something
+you would like to see, please [start a discussion](https://github.com/Rosettea/Hilbish/discussions)
+or comment on any existing ones which match your request.
diff --git a/website/content/docs/features/runner-mode.md b/website/content/docs/features/runner-mode.md
new file mode 100644
index 0000000..87ecc8b
--- /dev/null
+++ b/website/content/docs/features/runner-mode.md
@@ -0,0 +1,17 @@
+---
+title: Runner Mode
+description: Customize the interactive script/command runner.
+layout: doc
+menu:
+ docs:
+ parent: "Features"
+---
+
+Hilbish allows you to change how interactive text can be interpreted.
+This is mainly due to the fact that the default method Hilbish uses
+is that it runs Lua first and then falls back to shell script.
+
+In some cases, someone might want to switch to just shell script to avoid
+it while interactive but still have a Lua config, or go full Lua to use
+Hilbish as a REPL. This also allows users to add alternative languages,
+instead of either like Fennel.
diff --git a/website/content/docs/getting-started.md b/website/content/docs/getting-started.md
new file mode 100644
index 0000000..f0fe56d
--- /dev/null
+++ b/website/content/docs/getting-started.md
@@ -0,0 +1,59 @@
+---
+title: Getting Started
+layout: doc
+weight: -10
+menu: docs
+---
+
+To start Hilbish, open a terminal. If Hilbish has been installed and is not the
+default shell, you can simply run `hilbish` to start it. This will launch
+a normal interactive session.
+To exit, you can either run the `exit` command or hit Ctrl+D.
+
+# Setting as Default
+## Login shell
+There are a few ways to make Hilbish your default shell. A simple way is
+to make it your user/login shell.
+
+{{< warning `It is not recommended to set Hilbish as your login shell. That is expected to be a
+POSIX compliant shell, which Hilbish is not. At most, there will just be a
+few variables missing in your environment` >}}
+
+To do that, simply run `chsh -s /usr/bin/hilbish`.
+Some distros (namely Fedora) might have `lchsh` instead, which is used like `lchsh `.
+When prompted, you can put the path for Hilbish.
+
+## Default with terminal
+The simpler way is to set the default shell for your terminal. The way of
+doing this depends on how your terminal settings are configured.
+
+## Run after login shell
+Some shells (like zsh) have an rc file, like `.zlogin`, which is ran when the shell session
+is a login shell. In that file, you can run Hilbish. Example:
+
+```
+exec hilbish -S -l
+```
+
+This will replace the shell with Hilbish, set $SHELL to Hilbish and launch it as a login shell.
+
+# Configuration
+Once installation and setup has been done, you can then configure Hilbish.
+It is configured and scripted via Lua, so the config file is a Lua file.
+You can use any pure Lua library to do whatever you want.
+
+Hilbish's sample configuration is usually located in `hilbish.dataDir .. '/.hilbishrc.lua'`.
+You can print that path via Lua to see what it is: `print(hilbish.dataDir .. '/.hilbishrc.lua')`.
+As an example, it will usually will result in `/usr/share/hilbish/.hilbishrc.lua` on Linux.
+
+To edit your user configuration, you can copy that file to `hilbish.userDir.config .. '/hilbish/init.lua'`,
+which follows XDG on Linux and MacOS, and is located in %APPDATA% on Windows.
+
+As the directory is usually `~/.config` on Linux, you can run this command to copy it:
+`cp /usr/share/hilbish/.hilbishrc.lua ~/.config/hilbish/init.lua`
+
+Now you can get to editing it. Since it's just a Lua file, having basic
+knowledge of Lua would help. All of Lua's standard libraries and functions
+from Lua 5.4 are available. Hilbish has some custom and modules that are
+available. To see them, you can run the `doc` command. This also works as
+general documentation for other things.
diff --git a/website/content/install.md b/website/content/install.md
new file mode 100644
index 0000000..1ae103f
--- /dev/null
+++ b/website/content/install.md
@@ -0,0 +1,55 @@
+---
+title: Install
+description: Steps on how to install Hilbish on all the OSes and distros supported.
+layout: page
+---
+
+## Official Binaries
+The best way to get Hilbish is to get a build directly from GitHub.
+At any time, there are 2 versions of Hilbish recommended for download:
+the latest stable release, and development builds from the master branch.
+
+You can download both at any time, but note that the development builds may
+have breaking changes.
+
+For the latest **stable release**, check here: https://github.com/Rosettea/Hilbish/releases/latest
+For a **development build**: https://nightly.link/Rosettea/Hilbish/workflows/build/master
+
+## Package Repositories
+### Fedora (COPR)
+An official COPR is offered to install Hilbish easily on Fedora.
+Enable the repo:
+```
+sudo dnf copr enable sammyette/Hilbish
+```
+
+And install Hilbish:
+```
+sudo dnf install hilbish
+```
+
+Or for the latest development build from master:
+```
+sudo dnf install hilbish-git
+```
+
+### Arch Linux (AUR)
+Hilbish is on the AUR. Setup an AUR helper, and install.
+Example with yay:
+
+```
+yay -S hilbish
+```
+
+Or, from master branch:
+```
+yay -S hilbish-git
+```
+
+### Alpine Linux
+Hilbish is currentlty in the testing/edge repository for Alpine.
+Follow the steps [here](https://wiki.alpinelinux.org/wiki/Enable_Community_Repository)
+(Using testing repositories) and install:
+```
+apk add hilbish
+```
diff --git a/website/static/hilbish-flower.png b/website/static/hilbish-flower.png
new file mode 100644
index 0000000..b4fb0f7
Binary files /dev/null and b/website/static/hilbish-flower.png differ
diff --git a/website/themes/hsh/LICENSE b/website/themes/hsh/LICENSE
new file mode 100644
index 0000000..da3c8c1
--- /dev/null
+++ b/website/themes/hsh/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Rosettea
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/website/themes/hsh/archetypes/default.md b/website/themes/hsh/archetypes/default.md
new file mode 100644
index 0000000..ac36e06
--- /dev/null
+++ b/website/themes/hsh/archetypes/default.md
@@ -0,0 +1,2 @@
++++
++++
diff --git a/website/themes/hsh/layouts/404.html b/website/themes/hsh/layouts/404.html
new file mode 100644
index 0000000..06b3561
--- /dev/null
+++ b/website/themes/hsh/layouts/404.html
@@ -0,0 +1,7 @@
+{{ define "main"}}
+
+