mirror of https://github.com/Hilbis/Hilbish
Compare commits
204 Commits
883558c051
...
287334db74
Author | SHA1 | Date |
---|---|---|
sammyette | 287334db74 | |
sammyette | ab4cb0fb54 | |
sammyette | 62dc2e8b02 | |
sammyette | bfa3b55542 | |
sammyette | f7e66bb957 | |
sammyette | d7ab887234 | |
youkwhd | b24fc4a422 | |
sammyette | 0e4b95d9b9 | |
sammyette | 38d036d96f | |
sammyette | 4c61c551aa | |
sammyette | ff6e08902f | |
sammyette | bbd5637e9b | |
TorchedSammy | d46c079afb | |
sammyette | 42ab856e45 | |
TorchedSammy | 478e3020b1 | |
sammyette | a20123fc24 | |
sammyette | fb9d30520a | |
sammyette | 521298733e | |
sammyette | f8a391e24f | |
James Dugan | aa376f9b14 | |
sammyette | 16b39fe157 | |
sammyette | 40c3cecabb | |
sammyette | ce99209f0d | |
sammyette | d1dbb84c16 | |
sammyette | 5b84718021 | |
sammyette | 3d727e6411 | |
sammyette | 7d487bfc98 | |
sammyette | 11fc1edd94 | |
sammyette | b416264138 | |
sammyette | 11323a70aa | |
sammyette | db32fd7a87 | |
sammyette | 017702ea0d | |
sammyette | 30830ed6ed | |
sammyette | 7932fa677f | |
sammyette | c1b78a8ea7 | |
sammyette | 3128ef7934 | |
sammyette | c3394f7046 | |
sammyette | b1f957e98f | |
sammyette | f788c9374c | |
sammyette | b7602b0bbe | |
sammyette | 7b84faabf9 | |
sammyette | 8fdae6c1d7 | |
sammyette | 190ed5b2dc | |
sammyette | 9d5f5abef4 | |
sammyette | a0513c0a05 | |
sammyette | 1d64a57e24 | |
TorchedSammy | 1a4585aa7a | |
sammyette | 60fd4c3b4c | |
sammyette | 29e14c1aee | |
Super | ee1ed077b9 | |
sammyette | 78eb657897 | |
sammyette | a5b6fc8eda | |
sammyette | 4421869b85 | |
sammyette | 6d07d8db53 | |
sammyette | 483e5f6dbe | |
sammyette | 83492e4e69 | |
sammyette | 4fcd310048 | |
sammyette | db8e87e5dd | |
sammyette | ba04fb2af7 | |
sammyette | caff604d95 | |
sammyette | 2b9059f726 | |
sammyette | 00c967d7c1 | |
TorchedSammy | ab26b82a9f | |
sammyette | f540fc2c04 | |
sammyette | c6c81ddece | |
sammyette | 3eae0f07be | |
sammyette | cbc5e81c9d | |
TorchedSammy | 6729ecddea | |
sammyette | 485306fbe2 | |
sammyette | 480d4de750 | |
sammyette | d7dde44a0c | |
sammyette | 237b306eb8 | |
sammyette | bd01b478d5 | |
sammyette | a37f8e3b30 | |
sammyette | 9c6d6c4f31 | |
sammyette | 2dbd201e1e | |
sammyette | 3dd834fcdf | |
sammyette | 659d1e0c4e | |
TorchedSammy | 0379e302ac | |
sammyette | d577b3c958 | |
sammyette | 3d0fffd49a | |
sammyette | 013fa30658 | |
crumb | 4e37e8ca47 | |
sammyette | 26c8f28034 | |
sammyette | 6be65c04a6 | |
sammyette | 7bfd7b12c4 | |
sammyette | d48a66e393 | |
sammyette | d873f04f70 | |
sammyette | 7961a441ad | |
TorchedSammy | c55cd345ba | |
sammyette | 9e1ec1641c | |
sammyette | 2911257eb9 | |
sammyette | f2ee600c28 | |
sammyette | 61914f8dc7 | |
sammyette | 6dbb39d404 | |
sammyette | 890f10e176 | |
sammyette | 6b27d14f45 | |
sammyette | efc69ab769 | |
sammyette | cdd07a4981 | |
sammyette | e2cf417144 | |
sammyette | f97a04179d | |
sammyette | 2f6ab5fd92 | |
sammyette | 088e326bd1 | |
TorchedSammy | c87fbe2b99 | |
sammyette | 5bb6ba4aee | |
sammyette | 8d20ad9eed | |
sammyette | 71d23a4727 | |
sammyette | 6530d48b00 | |
sammyette | d99e056a76 | |
sammyette | 109b5785af | |
sammyette | a6bcfdfca9 | |
sammyette | 4e023703b6 | |
sammyette | 5814462f5d | |
sammyette | 354f257c03 | |
TorchedSammy | a2f0ff3e09 | |
sammyette | 07a7a75b46 | |
sammyette | 887260cd8a | |
sammyette | ef5363cb1f | |
sammyette | 246fc4d7ee | |
sammyette | 91ec0cf4a4 | |
sammyette | 2a80795331 | |
sammyette | 72d6b0a4a0 | |
sammyette | 4eea793e69 | |
sammyette | b3410e85bc | |
sammyette | c12cb82fb5 | |
TorchedSammy | 68a37d8c79 | |
sammyette | f107d1df71 | |
sammyette | 61f6337f5f | |
sammyette | 0f33f72de7 | |
sammyette | c2db9e65fa | |
sammyette | 97188e73a5 | |
sammyette | 3cedff273a | |
TorchedSammy | a1ce4389e8 | |
sammyette | 35e2a54172 | |
TorchedSammy | 7234111d6d | |
sammyette | a105b8e38d | |
sammyette | 5dc3b7d337 | |
sammyette | d5dffabcb2 | |
sammyette | 5ea23d58d1 | |
sammyette | 1a2d29aee7 | |
sammyette | b46bd0059e | |
sammyette | 78c95de784 | |
sammyette | e4d87afab3 | |
sammyette | 70594f4db2 | |
sammyette | f661db158b | |
sammyette | 787d6c082f | |
sammyette | ad94be925c | |
sammyette | fc82d4f5eb | |
sammyette | e5eefb1d2d | |
sammyette | 6525fa774e | |
sammyette | 514cbe25c7 | |
sammyette | 84dd939bbf | |
sammyette | f94b8ae59b | |
sammyette | 7b3dc951c9 | |
sammyette | 0a49e1a4ef | |
TorchedSammy | 6ca36847f1 | |
TorchedSammy | 5ca728ba06 | |
sammyette | 813354b662 | |
TorchedSammy | 8d40179a73 | |
TorchedSammy | f7e725b5b9 | |
sammyette | 4ee160fb66 | |
TorchedSammy | 1024f93446 | |
TorchedSammy | 9c8d7692bc | |
TorchedSammy | 9131c72501 | |
TorchedSammy | 26ff6c9a46 | |
TorchedSammy | 604dedb36d | |
TorchedSammy | 395f3c0742 | |
TorchedSammy | 0c44531a7f | |
TorchedSammy | 4e850bb322 | |
TorchedSammy | 09d04a7850 | |
TorchedSammy | 4df37b4341 | |
TorchedSammy | 5e2b3367de | |
Daniel de Sá | b6aecb12f6 | |
TorchedSammy | 3f9b230381 | |
TorchedSammy | b395b70ecd | |
TorchedSammy | 06102ebdae | |
TorchedSammy | bd4e0df7b3 | |
TorchedSammy | ebec585690 | |
TorchedSammy | ff4609e432 | |
TorchedSammy | ef3e7d92bc | |
TorchedSammy | d6338fc021 | |
TorchedSammy | 3eaeb6a5da | |
TorchedSammy | 8b547f2af0 | |
TorchedSammy | 1febe66f84 | |
TorchedSammy | 6ffcc498ac | |
TorchedSammy | fe47c6c7a1 | |
sammy | 0d32a10ca3 | |
TorchedSammy | cc6e5d01dd | |
TorchedSammy | 068a5b5149 | |
TorchedSammy | 117a4580b4 | |
TorchedSammy | 0db7f96fd7 | |
TorchedSammy | 300248de54 | |
TorchedSammy | 3ee2b03330 | |
TorchedSammy | 3bec2c91a8 | |
TorchedSammy | b4ca5bfda3 | |
TorchedSammy | 308e257872 | |
TorchedSammy | 7db2a2c826 | |
TorchedSammy | 22f6ea8a3e | |
TorchedSammy | 91596fa81c | |
TorchedSammy | e5c8e5eaff | |
TorchedSammy | 8647dc57a1 | |
TorchedSammy | 8f41005da7 | |
TorchedSammy | 7108523a4c | |
TorchedSammy | ee34ccdbc3 |
|
@ -0,0 +1,6 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = tab
|
|
@ -1,8 +1,12 @@
|
||||||
name: Build
|
name: Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
- push
|
push:
|
||||||
- pull_request
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -19,18 +23,18 @@ jobs:
|
||||||
goos: windows
|
goos: windows
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.17.7'
|
go-version: '1.22.2'
|
||||||
- name: Download Task
|
- name: Download Task
|
||||||
run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
|
run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
|
||||||
- name: Build
|
- name: Build
|
||||||
run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} ./bin/task
|
run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} ./bin/task
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v4
|
||||||
if: matrix.goos == 'windows'
|
if: matrix.goos == 'windows'
|
||||||
with:
|
with:
|
||||||
name: hilbish-${{ matrix.goos }}-${{ matrix.goarch }}
|
name: hilbish-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||||
|
@ -44,7 +48,7 @@ jobs:
|
||||||
libs
|
libs
|
||||||
docs
|
docs
|
||||||
emmyLuaDocs
|
emmyLuaDocs
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v4
|
||||||
if: matrix.goos != 'windows'
|
if: matrix.goos != 'windows'
|
||||||
with:
|
with:
|
||||||
name: hilbish-${{ matrix.goos }}-${{ matrix.goarch }}
|
name: hilbish-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||||
|
|
|
@ -26,7 +26,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
|
|
|
@ -2,14 +2,15 @@ name: Generate docs
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [master]
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
gen:
|
gen:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v2
|
- uses: actions/setup-go@v5
|
||||||
- name: Run docgen
|
- name: Run docgen
|
||||||
run: go run cmd/docgen/docgen.go
|
run: go run cmd/docgen/docgen.go
|
||||||
- name: Commit new docs
|
- name: Commit new docs
|
||||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
||||||
create-release:
|
create-release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- uses: taiki-e/create-gh-release-action@v1
|
- uses: taiki-e/create-gh-release-action@v1
|
||||||
with:
|
with:
|
||||||
title: Hilbish $tag
|
title: Hilbish $tag
|
||||||
|
@ -30,13 +30,17 @@ jobs:
|
||||||
- goarch: arm64
|
- goarch: arm64
|
||||||
goos: windows
|
goos: windows
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: wangyoucao577/go-release-action@v1.25
|
fetch-depth: 0
|
||||||
|
- name: Download Task
|
||||||
|
run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
|
||||||
|
- uses: wangyoucao577/go-release-action@v1
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
goos: ${{ matrix.goos }}
|
goos: ${{ matrix.goos }}
|
||||||
goarch: ${{ matrix.goarch }}
|
goarch: ${{ matrix.goarch }}
|
||||||
|
ldflags: '-s -w'
|
||||||
binary_name: hilbish
|
binary_name: hilbish
|
||||||
extra_files: LICENSE README.md CHANGELOG.md .hilbishrc.lua nature libs docs emmyLuaDocs
|
extra_files: LICENSE README.md CHANGELOG.md .hilbishrc.lua nature libs docs emmyLuaDocs
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
name: Build website
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup Hugo
|
||||||
|
uses: peaceiris/actions-hugo@v3
|
||||||
|
with:
|
||||||
|
hugo-version: '0.111.3'
|
||||||
|
extended: true
|
||||||
|
|
||||||
|
- name: Set branch name
|
||||||
|
id: branch
|
||||||
|
run: echo "BRANCH_NAME=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- name: Fix base URL
|
||||||
|
if: env.BRANCH_NAME != 'master' && github.repository_owner == 'Rosettea'
|
||||||
|
run: sed -i "s%baseURL = 'https://rosettea.github.io/Hilbish/'%baseURL = 'https://rosettea.github.io/Hilbish/versions/${{ env.BRANCH_NAME }}'%" website/config.toml
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: 'cd website && hugo --minify'
|
||||||
|
|
||||||
|
- name: Deploy
|
||||||
|
if: env.BRANCH_NAME == 'master' && github.repository_owner == 'Rosettea'
|
||||||
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
publish_dir: ./website/public
|
||||||
|
keep_files: true
|
||||||
|
- name: Deploy
|
||||||
|
if: env.BRANCH_NAME != 'master' && github.repository_owner == 'Rosettea'
|
||||||
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
publish_dir: ./website/public
|
||||||
|
destination_dir: versions/${{ env.BRANCH_NAME }}
|
||||||
|
keep_files: true
|
|
@ -1,6 +1,9 @@
|
||||||
*.exe
|
*.exe
|
||||||
hilbish
|
hilbish
|
||||||
|
!docs/api/hilbish
|
||||||
docgen
|
docgen
|
||||||
|
!cmd/docgen
|
||||||
|
|
||||||
.vim
|
.vim
|
||||||
petals/
|
petals/
|
||||||
|
.hugo_build.lock
|
||||||
|
|
|
@ -1,18 +1,39 @@
|
||||||
-- Default Hilbish config
|
-- Default Hilbish config
|
||||||
|
local hilbish = require 'hilbish'
|
||||||
local lunacolors = require 'lunacolors'
|
local lunacolors = require 'lunacolors'
|
||||||
local bait = require 'bait'
|
local bait = require 'bait'
|
||||||
local ansikit = require 'ansikit'
|
local ansikit = require 'ansikit'
|
||||||
|
|
||||||
|
local unreadCount = 0
|
||||||
|
local running = false
|
||||||
local function doPrompt(fail)
|
local function doPrompt(fail)
|
||||||
hilbish.prompt(lunacolors.format(
|
hilbish.prompt(lunacolors.format(
|
||||||
'{blue}%u {cyan}%d ' .. (fail and '{red}' or '{green}') .. '∆ '
|
'{blue}%u {cyan}%d ' .. (fail and '{red}' or '{green}') .. '∆ '
|
||||||
))
|
))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function doNotifyPrompt()
|
||||||
|
if running or unreadCount == hilbish.messages.unreadCount() then return end
|
||||||
|
|
||||||
|
local notifPrompt = string.format('• %s unread notification%s', hilbish.messages.unreadCount(), hilbish.messages.unreadCount() > 1 and 's' or '')
|
||||||
|
unreadCount = hilbish.messages.unreadCount()
|
||||||
|
hilbish.prompt(lunacolors.blue(notifPrompt), 'right')
|
||||||
|
|
||||||
|
hilbish.timeout(function()
|
||||||
|
hilbish.prompt('', 'right')
|
||||||
|
end, 3000)
|
||||||
|
end
|
||||||
|
|
||||||
doPrompt()
|
doPrompt()
|
||||||
|
|
||||||
|
bait.catch('command.preexec', function()
|
||||||
|
running = true
|
||||||
|
end)
|
||||||
|
|
||||||
bait.catch('command.exit', function(code)
|
bait.catch('command.exit', function(code)
|
||||||
|
running = false
|
||||||
doPrompt(code ~= 0)
|
doPrompt(code ~= 0)
|
||||||
|
doNotifyPrompt()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
bait.catch('hilbish.vimMode', function(mode)
|
bait.catch('hilbish.vimMode', function(mode)
|
||||||
|
@ -22,3 +43,7 @@ bait.catch('hilbish.vimMode', function(mode)
|
||||||
ansikit.cursorStyle(ansikit.lineCursor)
|
ansikit.cursorStyle(ansikit.lineCursor)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
bait.catch('hilbish.notification', function(notif)
|
||||||
|
doNotifyPrompt()
|
||||||
|
end)
|
||||||
|
|
200
CHANGELOG.md
200
CHANGELOG.md
|
@ -1,7 +1,153 @@
|
||||||
# 🎀 Changelog
|
# 🎀 Changelog
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
**NOTE:** Hilbish now uses [Task] insead of Make for builds.
|
### Added
|
||||||
|
- `fs.pipe` function to get a pair of connected files (a pipe).
|
||||||
|
- Added an alternative 2nd parameter to `hilbish.run`, which is `streams`.
|
||||||
|
`streams` is a table of input and output streams to run the command with.
|
||||||
|
It uses these 3 keys:
|
||||||
|
- `input` as standard input for the command
|
||||||
|
- `out` as standard output
|
||||||
|
- `err` as standard error
|
||||||
|
|
||||||
|
Here is a minimal example of the new usage which allows users to now pipe commands
|
||||||
|
directly via Lua functions:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local fs = require 'fs'
|
||||||
|
local pr, pw = fs.pipe()
|
||||||
|
hilbish.run('ls -l', {
|
||||||
|
stdout = pw,
|
||||||
|
stderr = pw,
|
||||||
|
})
|
||||||
|
|
||||||
|
pw:close()
|
||||||
|
|
||||||
|
hilbish.run('wc -l', {
|
||||||
|
stdin = pr
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix ansi attributes causing issues with text when cut off in greenhouse
|
||||||
|
- `exec` command should return if no arg presented
|
||||||
|
|
||||||
|
## [2.2.3] - 2024-04-27
|
||||||
|
### Fixed
|
||||||
|
- Highligher and hinter work now, since it was regressed from the previous minor release.
|
||||||
|
- `cat` command no longer prints extra newline at end of each file
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `cat` command now reads files in chunks, allowing for reading large files
|
||||||
|
|
||||||
|
## [2.2.2] - 2024-04-16
|
||||||
|
### Fixed
|
||||||
|
- Line refresh fixes (less flicker)
|
||||||
|
- Do more checks for a TTY
|
||||||
|
- Panic if ENOTTY is thrown from readline
|
||||||
|
- use `x/term` function to check if a terminal
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Page Up/Down keybinds for Greenhouse will now scroll up and down the size of the region (a page)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Remove usage of `hilbish.goro` in Greenhouse.
|
||||||
|
- Values in `hilbish` table are no longer protected. This means
|
||||||
|
they can be overridden. (#287)
|
||||||
|
|
||||||
|
## [2.2.1] - 2023-12-26
|
||||||
|
### Fixed
|
||||||
|
- Removed a left over debug print
|
||||||
|
- Recover panic in `hilbish.goro`
|
||||||
|
|
||||||
|
## [2.2.0] - 2023-12-25
|
||||||
|
### Added
|
||||||
|
- [Native Modules](https://rosettea.github.io/Hilbish/docs/api/hilbish/hilbish.module/)
|
||||||
|
- Made a few additions to the sink type:
|
||||||
|
- `read()` method for retrieving input (so now the `in` sink of commanders is useful)
|
||||||
|
- `flush()` and `autoFlush()` related to flushing outputs
|
||||||
|
- `pipe` property to check if a sink with input is a pipe (like stdin)
|
||||||
|
- Add fuzzy search to history search (enable via `hilbish.opts.fuzzy = true`)
|
||||||
|
- Show indexes on cdr list and use ~ for home directory.
|
||||||
|
- Fix doc command not displaying correct subdocs when using shorthand api doc access (`doc api hilbish.jobs` as an example)
|
||||||
|
- `hilbish.messages` interface (details in [#219])
|
||||||
|
- `hilbish.notification` signal when a message/notification is sent
|
||||||
|
- `notifyJobFinish` opt to send a notification when background jobs are
|
||||||
|
- `hilbish.goVersion` for the version of Go used to compile Hilbish.
|
||||||
|
completed.
|
||||||
|
- Allow numbered arg substitutions in aliases.
|
||||||
|
- Example: `hilbish.alias('hello', 'echo %1 says hello')` allows the user to run `hello hilbish`
|
||||||
|
which will output `hilbish says hello`.
|
||||||
|
- Greenhouse
|
||||||
|
- Greenhouse is a pager library and program. Basic usage is `greenhouse <file>`
|
||||||
|
- Using this also brings enhancements to the `doc` command like easy
|
||||||
|
navigation of neighboring doc files.
|
||||||
|
Ctrl-N can be used for the table of contents, which views adjacent documentation.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Documentation for EVERYTHING has been improved, with more
|
||||||
|
information added, code example, parameter details, etc.
|
||||||
|
You can see the improvements!
|
||||||
|
- Documentation has gotten an uplift in the `doc` command.
|
||||||
|
This includes:
|
||||||
|
- Proper highlighting of code
|
||||||
|
- Paging (via Greenhouse)
|
||||||
|
- Highlighting more markdown things
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix panic when runner doesn't return a table
|
||||||
|
- Fix edge case of crash on empty alias resolve
|
||||||
|
- File completion on Windows
|
||||||
|
- Job management commands work now
|
||||||
|
- Fix infinite loop when navigating history without any history. [#252](https://github.com/Rosettea/Hilbish/issues/252)
|
||||||
|
- Return the prefix when calling `hilbish.completions.call`. [#219](https://github.com/Rosettea/Hilbish/issues/219)
|
||||||
|
- Replaced `sed` in-place editing with `grep` and `mv` for compatibility with BSD utils
|
||||||
|
|
||||||
|
## [2.1.2] - 2022-04-10
|
||||||
|
### Removed
|
||||||
|
- Bad april fools code ;(
|
||||||
|
|
||||||
|
## [2.1.1] - 2022-04-01
|
||||||
|
### Added
|
||||||
|
- Validation checks for command input
|
||||||
|
- Improved runtime performance
|
||||||
|
- Validate Lua code
|
||||||
|
|
||||||
|
## [2.1.0] - 2022-02-10
|
||||||
|
### Added
|
||||||
|
- Documented custom userdata types (Job and Timer Objects)
|
||||||
|
- Coming with this fix is also adding the return types for some functions that were missing it
|
||||||
|
- Added a dedicated input and dedicated outputs for commanders (sinks - info at `doc api commander`).
|
||||||
|
- Local docs is used if one of Hilbish's branches is found
|
||||||
|
- Return 1 exit code on doc not found
|
||||||
|
- `hilbish.runner.getCurrent()` to get the current runner
|
||||||
|
- Initialize Hilbish Lua API before handling signals
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- `index` or `_index` subdocs should not show up anymore
|
||||||
|
- `hilbish.which` not working correctly with aliases
|
||||||
|
- Commanders not being able to pipe with commands or any related operator.
|
||||||
|
- Resolve symlinks in completions
|
||||||
|
- Updated `runner-mode` docs
|
||||||
|
- Fix `hilbish.completion` functions panicking when empty input is provided
|
||||||
|
|
||||||
|
## [2.0.1] - 2022-12-28
|
||||||
|
### Fixed
|
||||||
|
- Corrected documentation for hooks, removing outdated `command.no-perm`
|
||||||
|
- Fixed an issue where `cd` with no args would not update the old pwd
|
||||||
|
- Tiny documentation enhancements for the `hilbish.timer` interface
|
||||||
|
|
||||||
|
## [2.0.0] - 2022-12-20
|
||||||
|
**NOTES FOR USERS/PACKAGERS UPDATING:**
|
||||||
|
- Hilbish now uses [Task] insead of Make for builds.
|
||||||
|
- The doc format has been changed from plain text to markdown.
|
||||||
|
**YOU MUST reinstall Hilbish to remove the duplicate, old docs.**
|
||||||
|
- Hilbish will by default install to **`/usr/local`** instead of just `/usr/`
|
||||||
|
when building via Task. This is mainly to avoid conflict of distro packages
|
||||||
|
and local installs, and is the correct place when building from git either way.
|
||||||
|
To keep Hilbish in `/usr`, you must have `PREFIX="/usr/"` when running `task build` or `task install`
|
||||||
|
- Windows is no longer supported. It will build and run, but **will** have problems.
|
||||||
|
If you want to help fix the situation, start a discussion or open an issue and contribute.
|
||||||
|
|
||||||
[Task]: https://taskfile.dev/#/
|
[Task]: https://taskfile.dev/#/
|
||||||
|
|
||||||
|
@ -39,7 +185,7 @@ without arguments will disown the last job.
|
||||||
fields on a job object.
|
fields on a job object.
|
||||||
- Documentation for jobs is now available via `doc jobs`.
|
- Documentation for jobs is now available via `doc jobs`.
|
||||||
- `hilbish.alias.resolve(cmdstr)` to resolve a command alias.
|
- `hilbish.alias.resolve(cmdstr)` to resolve a command alias.
|
||||||
- `hilbish.opts` for shell options. Currently, the only opt is `autocd`.
|
- `hilbish.opts` for shell options.
|
||||||
- `hilbish.editor` interface for interacting with the line editor that
|
- `hilbish.editor` interface for interacting with the line editor that
|
||||||
Hilbish uses.
|
Hilbish uses.
|
||||||
- `hilbish.vim` interface to dynamically get/set vim registers.
|
- `hilbish.vim` interface to dynamically get/set vim registers.
|
||||||
|
@ -73,12 +219,20 @@ disables commands being added to history.
|
||||||
- A new and "safer" event emitter has been added. This causes a performance deficit, but avoids a lot of
|
- A new and "safer" event emitter has been added. This causes a performance deficit, but avoids a lot of
|
||||||
random errors introduced with the new Lua runtime (see [#197])
|
random errors introduced with the new Lua runtime (see [#197])
|
||||||
- `bait.release(name, catcher)` removes `handler` for the named `event`
|
- `bait.release(name, catcher)` removes `handler` for the named `event`
|
||||||
|
- `exec`, `clear` and `cat` builtin commands
|
||||||
|
- `hilbish.cancel` hook thrown when user cancels input with Ctrl-C
|
||||||
|
- 1st item on history is now inserted when history search menu is opened ([#148])
|
||||||
|
- Documentation has been improved vastly!
|
||||||
|
|
||||||
|
[#148]: https://github.com/Rosettea/Hilbish/issues/148
|
||||||
[#197]: https://github.com/Rosettea/Hilbish/issues/197
|
[#197]: https://github.com/Rosettea/Hilbish/issues/197
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- **Breaking Change:** Upgraded to Lua 5.4.
|
- **Breaking Change:** Upgraded to Lua 5.4.
|
||||||
This is probably one of (if not the) biggest things in this release.
|
This is probably one of (if not the) biggest things in this release.
|
||||||
|
To recap quickly on what matters (mostly):
|
||||||
|
- `os.execute` returns 3 values instead of 1 (but you should be using `hilbish.run`)
|
||||||
|
- I/O operations must be flushed (`io.flush()`)
|
||||||
- **Breaking Change:** MacOS config paths now match Linux.
|
- **Breaking Change:** MacOS config paths now match Linux.
|
||||||
- Overrides on the `hilbish` table are no longer permitted.
|
- Overrides on the `hilbish` table are no longer permitted.
|
||||||
- **Breaking Change:** Runner functions are now required to return a table.
|
- **Breaking Change:** Runner functions are now required to return a table.
|
||||||
|
@ -97,6 +251,7 @@ of a dot. (ie. `job.stop()` -> `job:stop()`)
|
||||||
- All `fs` module functions which take paths now implicitly expand ~ to home.
|
- All `fs` module functions which take paths now implicitly expand ~ to home.
|
||||||
- **Breaking Change:** `hilbish.greeting` has been moved to an opt (`hilbish.opts.greeting`) and is
|
- **Breaking Change:** `hilbish.greeting` has been moved to an opt (`hilbish.opts.greeting`) and is
|
||||||
always printed by default. To disable it, set the opt to false.
|
always printed by default. To disable it, set the opt to false.
|
||||||
|
- **Breaking Change:** `command.no-perm` hook has been replaced with `command.not-executable`
|
||||||
- History is now fetched from Lua, which means users can override `hilbish.history`
|
- History is now fetched from Lua, which means users can override `hilbish.history`
|
||||||
methods to make it act how they want.
|
methods to make it act how they want.
|
||||||
- `guide` has been removed. See the [website](https://rosettea.github.io/Hilbish/)
|
- `guide` has been removed. See the [website](https://rosettea.github.io/Hilbish/)
|
||||||
|
@ -112,7 +267,7 @@ replacing the last character.
|
||||||
- `hilbish.login` being the wrong value.
|
- `hilbish.login` being the wrong value.
|
||||||
- Put full input in history if prompted for continued input
|
- Put full input in history if prompted for continued input
|
||||||
- Don't put alias expanded command in history (sound familiar?)
|
- Don't put alias expanded command in history (sound familiar?)
|
||||||
- Handle cases of stdin being nonblocking (in the case of [#130](https://github.com/Rosettea/Hilbish/issues/130))
|
- Handle cases of stdin being nonblocking (in the case of [#136](https://github.com/Rosettea/Hilbish/issues/136))
|
||||||
- Don't prompt for continued input if non interactive
|
- Don't prompt for continued input if non interactive
|
||||||
- Don't insert unhandled control keys.
|
- Don't insert unhandled control keys.
|
||||||
- Handle sh syntax error in alias
|
- Handle sh syntax error in alias
|
||||||
|
@ -122,11 +277,12 @@ certain color rules.
|
||||||
- Home/End keys now go to the actual start/end of the input.
|
- Home/End keys now go to the actual start/end of the input.
|
||||||
- Input getting cut off on enter in certain cases.
|
- Input getting cut off on enter in certain cases.
|
||||||
- Go to the next line properly if input reaches end of terminal width.
|
- Go to the next line properly if input reaches end of terminal width.
|
||||||
- Cursor position with CJK characters. ([#145](https://github.com/Rosettea/Hilbish/pull/145))
|
- Cursor position with CJK characters has been corrected ([#145](https://github.com/Rosettea/Hilbish/pull/145))
|
||||||
- Files with same name as parent folder in completions getting cut off [#136](https://github.com/Rosettea/Hilbish/issues/136))
|
- Files with same name as parent folder in completions getting cut off [#130](https://github.com/Rosettea/Hilbish/issues/130))
|
||||||
- `hilbish.which` now works with commanders and aliases.
|
- `hilbish.which` now works with commanders and aliases.
|
||||||
- Background jobs no longer take stdin so they do not interfere with shell
|
- Background jobs no longer take stdin so they do not interfere with shell
|
||||||
input.
|
input.
|
||||||
|
- Full name of completion entry is used instead of being cut off
|
||||||
- Completions are fixed in cases where the query/line is an alias alone
|
- Completions are fixed in cases where the query/line is an alias alone
|
||||||
where it can also resolve to the beginning of command names.
|
where it can also resolve to the beginning of command names.
|
||||||
(reference [this commit](https://github.com/Rosettea/Hilbish/commit/2790982ad123115c6ddbc5764677fdca27668cea))
|
(reference [this commit](https://github.com/Rosettea/Hilbish/commit/2790982ad123115c6ddbc5764677fdca27668cea))
|
||||||
|
@ -145,6 +301,29 @@ menu is open.
|
||||||
- Escape codes now work.
|
- Escape codes now work.
|
||||||
- Escape percentage symbols in completion entries, so you will no longer see
|
- Escape percentage symbols in completion entries, so you will no longer see
|
||||||
an error of missing format variable
|
an error of missing format variable
|
||||||
|
- Fix an error with sh syntax in aliases
|
||||||
|
- Prompt now works with east asian characters (CJK)
|
||||||
|
- Set back the prompt to normal after exiting the continue prompt with ctrl-d
|
||||||
|
- Take into account newline in input when calculating input width. Prevents
|
||||||
|
extra reprinting of the prompt, but input with newlines inserted is still a problem
|
||||||
|
- Put cursor at the end of input when exiting $EDITOR with Vim mode bind
|
||||||
|
- Calculate width of virtual input properly (completion candidates)
|
||||||
|
- Users can now tab complete files with spaces while quoted or with escaped spaces.
|
||||||
|
This means a query of `Files\ to\ ` with file names of `Files to tab complete` and `Files to complete`
|
||||||
|
will result in the files being completed.
|
||||||
|
- Fixed grid menu display if cell width ends up being the width of the terminal
|
||||||
|
- Cut off item names in grid menu if its longer than cell width
|
||||||
|
- Fix completion search menu disappearing
|
||||||
|
- Make binary completion work with bins that have spaces in the name
|
||||||
|
- Completion paths having duplicated characters if it's escaped
|
||||||
|
- Get custom completion command properly to call from Lua
|
||||||
|
- Put proper command on the line when using up and down arrow keys to go through command history
|
||||||
|
- Don't do anything if length of input rune slice is 0 ([commit for explanation](https://github.com/Rosettea/Hilbish/commit/8d40179a73fe5942707cd43f9c0463dee53eedd8))
|
||||||
|
|
||||||
|
## [2.0.0-rc1] - 2022-09-14
|
||||||
|
This is a pre-release version of Hilbish for testing. To see the changelog,
|
||||||
|
refer to the `Unreleased` section of the [full changelog](CHANGELOG.md)
|
||||||
|
(version 2.0.0 for future reference).
|
||||||
|
|
||||||
## [1.2.0] - 2022-03-17
|
## [1.2.0] - 2022-03-17
|
||||||
### Added
|
### Added
|
||||||
|
@ -569,6 +748,17 @@ This input for example will prompt for more input to complete:
|
||||||
|
|
||||||
First "stable" release of Hilbish.
|
First "stable" release of Hilbish.
|
||||||
|
|
||||||
|
[2.2.3]: https://github.com/Rosettea/Hilbish/compare/v2.2.2...v2.2.3
|
||||||
|
[2.2.2]: https://github.com/Rosettea/Hilbish/compare/v2.2.1...v2.2.2
|
||||||
|
[2.2.1]: https://github.com/Rosettea/Hilbish/compare/v2.2.0...v2.2.1
|
||||||
|
[2.2.0]: https://github.com/Rosettea/Hilbish/compare/v2.1.0...v2.2.0
|
||||||
|
[2.1.2]: https://github.com/Rosettea/Hilbish/compare/v2.1.1...v2.1.2
|
||||||
|
[2.1.1]: https://github.com/Rosettea/Hilbish/compare/v2.1.0...v2.1.1
|
||||||
|
[2.1.0]: https://github.com/Rosettea/Hilbish/compare/v2.0.1...v2.1.0
|
||||||
|
[2.0.1]: https://github.com/Rosettea/Hilbish/compare/v2.0.0...v2.0.1
|
||||||
|
[2.0.0]: https://github.com/Rosettea/Hilbish/compare/v1.2.0...v2.0.0
|
||||||
|
[2.0.0-rc1]: https://github.com/Rosettea/Hilbish/compare/v1.2.0...v2.0.0-rc1
|
||||||
|
[1.2.0]: https://github.com/Rosettea/Hilbish/compare/v1.1.4...v1.2.0
|
||||||
[1.1.0]: https://github.com/Rosettea/Hilbish/compare/v1.0.4...v1.1.0
|
[1.1.0]: https://github.com/Rosettea/Hilbish/compare/v1.0.4...v1.1.0
|
||||||
[1.0.4]: https://github.com/Rosettea/Hilbish/compare/v1.0.3...v1.0.4
|
[1.0.4]: https://github.com/Rosettea/Hilbish/compare/v1.0.3...v1.0.4
|
||||||
[1.0.3]: https://github.com/Rosettea/Hilbish/compare/v1.0.2...v1.0.3
|
[1.0.3]: https://github.com/Rosettea/Hilbish/compare/v1.0.2...v1.0.3
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2022 Rosettea
|
Copyright (c) 2021-2023 Rosettea
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
83
README.md
83
README.md
|
@ -1,74 +1,45 @@
|
||||||
<div align="center">
|
<img src="./assets/hilbish-logo-and-text.png" width=512><br>
|
||||||
<img src="./assets/hilbish-flower.png" width=128><br>
|
<blockquote>
|
||||||
<img src="./assets/hilbish-text.png" width=256><br>
|
🌓 The Moon-powered shell! A comfy and extensible shell for Lua fans! 🌺 ✨
|
||||||
<blockquote>
|
</blockquote>
|
||||||
🌺 The flower shell. A comfy and nice little shell for Lua fans!
|
|
||||||
</blockquote>
|
|
||||||
<p align="center">
|
|
||||||
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/Rosettea/Hilbish?style=flat-square">
|
|
||||||
<img alt="GitHub commits since latest release (by date)" src="https://img.shields.io/github/commits-since/Rosettea/Hilbish/latest?style=flat-square">
|
|
||||||
<img alt="GitHub contributors" src="https://img.shields.io/github/contributors/Rosettea/Hilbish?style=flat-square"><br>
|
|
||||||
<a href="https://github.com/Rosettea/Hilbish/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22"><img src="https://img.shields.io/github/issues/Hilbis/Hilbish/help%20wanted?style=flat-square&color=green" alt="help wanted"></a>
|
|
||||||
<a href="https://github.com/Rosettea/Hilbish/blob/master/LICENSE"><img alt="GitHub license" src="https://img.shields.io/github/license/Rosettea/Hilbish?style=flat-square"></a>
|
|
||||||
<a href="https://discord.gg/3PDdcQz"><img alt="Discord" src="https://img.shields.io/discord/732357621503229962?color=blue&style=flat-square"></a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Hilbish is a extensible shell (framework). It was made to be very customizable
|
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/Rosettea/Hilbish?style=flat-square"><img alt="GitHub commits since latest release (by date)" src="https://img.shields.io/github/commits-since/Rosettea/Hilbish/latest?style=flat-square"><img alt="GitHub contributors" src="https://img.shields.io/github/contributors/Rosettea/Hilbish?style=flat-square"><br>
|
||||||
via the Lua programming language. It aims to be easy to use for the casual
|
<a href="https://github.com/Rosettea/Hilbish/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22"><img src="https://img.shields.io/github/issues/Hilbis/Hilbish/help%20wanted?style=flat-square&color=green" alt="help wanted"></a>
|
||||||
people but powerful for those who want to tinker more with their shell,
|
<a href="https://github.com/Rosettea/Hilbish/blob/master/LICENSE"><img alt="GitHub license" src="https://img.shields.io/github/license/Rosettea/Hilbish?style=flat-square"></a>
|
||||||
the thing used to interface with most of the system.
|
<a href="https://discord.gg/3PDdcQz"><img alt="Discord" src="https://img.shields.io/discord/732357621503229962?color=blue&style=flat-square"></a>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
Hilbish is an extensible shell designed to be highly customizable.
|
||||||
|
It is configured in Lua and provides a good range of features.
|
||||||
|
It aims to be easy to use for anyone but powerful enough for
|
||||||
|
those who need it.
|
||||||
|
|
||||||
The motivation for choosing Lua was that its simpler and better to use
|
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,
|
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
|
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!
|
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)
|
|
||||||
- [Contributing](#Contributing)
|
|
||||||
|
|
||||||
# Screenshots
|
# Screenshots
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="gallery/default.png"><br><br>
|
<img src="gallery/tab.png">
|
||||||
<img src="gallery/terminal.png"><br><br>
|
|
||||||
<img src="gallery/pillprompt.png">
|
<img src="gallery/pillprompt.png">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
# Installation
|
# Getting Hilbish
|
||||||
## Prebuilt binaries
|
**NOTE:** Hilbish is not guaranteed to work properly on Windows, starting
|
||||||
Go [here](https://nightly.link/Rosettea/Hilbish/workflows/build/master) for
|
from the 2.0 version. It will still be able to compile, but functionality
|
||||||
builds on the master branch.
|
may be lacking. If you want to contribute to make the situation better,
|
||||||
|
comment on the Windows discussion.
|
||||||
|
|
||||||
## AUR
|
You can check the [install page](https://rosettea.github.io/Hilbish/install/)
|
||||||
[![AUR maintainer](https://img.shields.io/aur/maintainer/hilbish?logo=arch-linux&style=flat-square)](https://aur.archlinux.org/packages/hilbish)
|
on the website for distributed binaries from GitHub or other package repositories.
|
||||||
Arch Linux users can install Hilbish from the AUR with the following command:
|
Otherwise, continue reading for steps on compiling.
|
||||||
```sh
|
|
||||||
yay -S hilbish
|
|
||||||
```
|
|
||||||
|
|
||||||
[![AUR maintainer](https://img.shields.io/aur/maintainer/hilbish?logo=arch-linux&style=flat-square)](https://aur.archlinux.org/packages/hilbish-git)
|
## Prerequisites
|
||||||
Or from the latest `master` commit with:
|
- [Go 1.22+](https://go.dev)
|
||||||
```sh
|
- [Task](https://taskfile.dev/installation/) (**Go on the hyperlink here to see Task's install method for your OS.**)
|
||||||
yay -S hilbish-git
|
|
||||||
```
|
|
||||||
|
|
||||||
## Nixpkgs
|
## Build
|
||||||
Nix/NixOS users can install Hilbish from the central repository, nixpkgs, through the usual ways.
|
|
||||||
If you're new to nix you should probably read up on how to do that [here](https://nixos.wiki/wiki/Cheatsheet).
|
|
||||||
|
|
||||||
## Manual Build
|
|
||||||
### Prerequisites
|
|
||||||
- [Go 1.17+](https://go.dev)
|
|
||||||
- [Task](https://taskfile.dev/#/)
|
|
||||||
|
|
||||||
### Build
|
|
||||||
First, clone Hilbish. The recursive is required, as some Lua libraries
|
First, clone Hilbish. The recursive is required, as some Lua libraries
|
||||||
are submodules.
|
are submodules.
|
||||||
```sh
|
```sh
|
||||||
|
|
|
@ -3,24 +3,35 @@
|
||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
PREFIX: '{{default "/usr" .PREFIX}}'
|
PREFIX: '{{default "/usr/local" .PREFIX}}'
|
||||||
bindir__: '{{.PREFIX}}/bin'
|
bindir__: '{{.PREFIX}}/bin'
|
||||||
BINDIR: '{{default .bindir__ .BINDIR}}'
|
BINDIR: '{{default .bindir__ .BINDIR}}'
|
||||||
libdir__: '{{.PREFIX}}/share/hilbish'
|
libdir__: '{{.PREFIX}}/share/hilbish'
|
||||||
LIBDIR: '{{default .libdir__ .LIBDIR}}'
|
LIBDIR: '{{default .libdir__ .LIBDIR}}'
|
||||||
GOFLAGS: '-ldflags "-s -w"'
|
goflags__: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}}"'
|
||||||
|
GOFLAGS: '{{default .goflags__ .GOFLAGS}}'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
default:
|
default:
|
||||||
cmds:
|
cmds:
|
||||||
- go build {{.GOFLAGS}}
|
- go build {{.GOFLAGS}}
|
||||||
vars:
|
vars:
|
||||||
GOFLAGS: '-ldflags "-s -w -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"'
|
GOFLAGS: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}} -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"'
|
||||||
|
|
||||||
|
default-nocgo:
|
||||||
|
cmds:
|
||||||
|
- CGO_ENABLED=0 go build {{.GOFLAGS}}
|
||||||
|
vars:
|
||||||
|
GOFLAGS: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}} -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"'
|
||||||
|
|
||||||
build:
|
build:
|
||||||
cmds:
|
cmds:
|
||||||
- go build {{.GOFLAGS}}
|
- go build {{.GOFLAGS}}
|
||||||
|
|
||||||
|
build-nocgo:
|
||||||
|
cmds:
|
||||||
|
- CGO_ENABLED=0 go build {{.GOFLAGS}}
|
||||||
|
|
||||||
install:
|
install:
|
||||||
cmds:
|
cmds:
|
||||||
- install -v -d "{{.DESTDIR}}{{.BINDIR}}/" && install -m 0755 -v hilbish "{{.DESTDIR}}{{.BINDIR}}/hilbish"
|
- install -v -d "{{.DESTDIR}}{{.BINDIR}}/" && install -m 0755 -v hilbish "{{.DESTDIR}}{{.BINDIR}}/hilbish"
|
||||||
|
@ -33,4 +44,4 @@ tasks:
|
||||||
- rm -vrf
|
- rm -vrf
|
||||||
"{{.DESTDIR}}{{.BINDIR}}/hilbish"
|
"{{.DESTDIR}}{{.BINDIR}}/hilbish"
|
||||||
"{{.DESTDIR}}{{.LIBDIR}}"
|
"{{.DESTDIR}}{{.LIBDIR}}"
|
||||||
- sed -i '/hilbish/d' /etc/shells
|
- grep -v 'hilbish' /etc/shells > /tmp/shells.hilbish_uninstall && mv /tmp/shells.hilbish_uninstall /etc/shells
|
||||||
|
|
82
aliases.go
82
aliases.go
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -9,46 +11,69 @@ import (
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var aliases *aliasHandler
|
var aliases *aliasModule
|
||||||
|
|
||||||
type aliasHandler struct {
|
type aliasModule struct {
|
||||||
aliases map[string]string
|
aliases map[string]string
|
||||||
mu *sync.RWMutex
|
mu *sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize aliases map
|
// initialize aliases map
|
||||||
func newAliases() *aliasHandler {
|
func newAliases() *aliasModule {
|
||||||
return &aliasHandler{
|
return &aliasModule{
|
||||||
aliases: make(map[string]string),
|
aliases: make(map[string]string),
|
||||||
mu: &sync.RWMutex{},
|
mu: &sync.RWMutex{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aliasHandler) Add(alias, cmd string) {
|
func (a *aliasModule) Add(alias, cmd string) {
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
defer a.mu.Unlock()
|
defer a.mu.Unlock()
|
||||||
|
|
||||||
a.aliases[alias] = cmd
|
a.aliases[alias] = cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aliasHandler) All() map[string]string {
|
func (a *aliasModule) All() map[string]string {
|
||||||
return a.aliases
|
return a.aliases
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aliasHandler) Delete(alias string) {
|
func (a *aliasModule) Delete(alias string) {
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
defer a.mu.Unlock()
|
defer a.mu.Unlock()
|
||||||
|
|
||||||
delete(a.aliases, alias)
|
delete(a.aliases, alias)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aliasHandler) Resolve(cmdstr string) string {
|
func (a *aliasModule) Resolve(cmdstr string) string {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
defer a.mu.RUnlock()
|
defer a.mu.RUnlock()
|
||||||
|
|
||||||
args := strings.Split(cmdstr, " ")
|
arg, _ := regexp.Compile(`[\\]?%\d+`)
|
||||||
|
|
||||||
|
args, _ := splitInput(cmdstr)
|
||||||
|
if len(args) == 0 {
|
||||||
|
// this shouldnt reach but...????
|
||||||
|
return cmdstr
|
||||||
|
}
|
||||||
|
|
||||||
for a.aliases[args[0]] != "" {
|
for a.aliases[args[0]] != "" {
|
||||||
alias := a.aliases[args[0]]
|
alias := a.aliases[args[0]]
|
||||||
|
alias = arg.ReplaceAllStringFunc(alias, func(a string) string {
|
||||||
|
idx, _ := strconv.Atoi(a[1:])
|
||||||
|
if strings.HasPrefix(a, "\\") || idx == 0 {
|
||||||
|
return strings.TrimPrefix(a, "\\")
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx + 1 > len(args) {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
val := args[idx]
|
||||||
|
args = cut(args, idx)
|
||||||
|
cmdstr = strings.Join(args, " ")
|
||||||
|
|
||||||
|
return val
|
||||||
|
})
|
||||||
|
|
||||||
cmdstr = alias + strings.TrimPrefix(cmdstr, args[0])
|
cmdstr = alias + strings.TrimPrefix(cmdstr, args[0])
|
||||||
cmdArgs, _ := splitInput(cmdstr)
|
cmdArgs, _ := splitInput(cmdstr)
|
||||||
args = cmdArgs
|
args = cmdArgs
|
||||||
|
@ -66,7 +91,10 @@ func (a *aliasHandler) Resolve(cmdstr string) string {
|
||||||
|
|
||||||
// lua section
|
// lua section
|
||||||
|
|
||||||
func (a *aliasHandler) Loader(rtm *rt.Runtime) *rt.Table {
|
// #interface aliases
|
||||||
|
// command aliasing
|
||||||
|
// The alias interface deals with all command aliases in Hilbish.
|
||||||
|
func (a *aliasModule) Loader(rtm *rt.Runtime) *rt.Table {
|
||||||
// create a lua module with our functions
|
// create a lua module with our functions
|
||||||
hshaliasesLua := map[string]util.LuaExport{
|
hshaliasesLua := map[string]util.LuaExport{
|
||||||
"add": util.LuaExport{hlalias, 2, false},
|
"add": util.LuaExport{hlalias, 2, false},
|
||||||
|
@ -81,7 +109,26 @@ func (a *aliasHandler) Loader(rtm *rt.Runtime) *rt.Table {
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aliasHandler) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
// #interface aliases
|
||||||
|
// add(alias, cmd)
|
||||||
|
// This is an alias (ha) for the [hilbish.alias](../#alias) function.
|
||||||
|
// --- @param alias string
|
||||||
|
// --- @param cmd string
|
||||||
|
func _hlalias() {}
|
||||||
|
|
||||||
|
// #interface aliases
|
||||||
|
// list() -> table[string, string]
|
||||||
|
// Get a table of all aliases, with string keys as the alias and the value as the command.
|
||||||
|
// #returns table[string, string]
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
hilbish.aliases.add('hi', 'echo hi')
|
||||||
|
|
||||||
|
local aliases = hilbish.aliases.list()
|
||||||
|
-- -> {hi = 'echo hi'}
|
||||||
|
#example
|
||||||
|
*/
|
||||||
|
func (a *aliasModule) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
aliasesList := rt.NewTable()
|
aliasesList := rt.NewTable()
|
||||||
for k, v := range a.All() {
|
for k, v := range a.All() {
|
||||||
aliasesList.Set(rt.StringValue(k), rt.StringValue(v))
|
aliasesList.Set(rt.StringValue(k), rt.StringValue(v))
|
||||||
|
@ -90,7 +137,11 @@ func (a *aliasHandler) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.PushingNext1(t.Runtime, rt.TableValue(aliasesList)), nil
|
return c.PushingNext1(t.Runtime, rt.TableValue(aliasesList)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aliasHandler) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
// #interface aliases
|
||||||
|
// delete(name)
|
||||||
|
// Removes an alias.
|
||||||
|
// #param name string
|
||||||
|
func (a *aliasModule) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -103,7 +154,12 @@ func (a *aliasHandler) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aliasHandler) luaResolve(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
// #interface aliases
|
||||||
|
// resolve(alias) -> string?
|
||||||
|
// Resolves an alias to its original command. Will thrown an error if the alias doesn't exist.
|
||||||
|
// #param alias string
|
||||||
|
// #returns string
|
||||||
|
func (a *aliasModule) luaResolve(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
503
api.go
503
api.go
|
@ -1,12 +1,22 @@
|
||||||
// Here is the core api for the hilbi shell itself
|
// the core Hilbish API
|
||||||
// Basically, stuff about the shell itself and other functions
|
// The Hilbish module includes the core API, containing
|
||||||
// go here.
|
// interfaces and functions which directly relate to shell functionality.
|
||||||
|
// #field ver The version of Hilbish
|
||||||
|
// #field goVersion The version of Go that Hilbish was compiled with
|
||||||
|
// #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 Exit code of the last executed command
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -18,8 +28,8 @@ import (
|
||||||
|
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
"github.com/arnodel/golua/lib/packagelib"
|
"github.com/arnodel/golua/lib/packagelib"
|
||||||
|
"github.com/arnodel/golua/lib/iolib"
|
||||||
"github.com/maxlandon/readline"
|
"github.com/maxlandon/readline"
|
||||||
"github.com/blackfireio/osinfo"
|
|
||||||
"mvdan.cc/sh/v3/interp"
|
"mvdan.cc/sh/v3/interp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,47 +61,8 @@ var hilbishLoader = packagelib.Loader{
|
||||||
}
|
}
|
||||||
|
|
||||||
func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
fakeMod := rt.NewTable()
|
|
||||||
modmt := rt.NewTable()
|
|
||||||
mod := rt.NewTable()
|
mod := rt.NewTable()
|
||||||
|
|
||||||
modIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
arg := c.Arg(1)
|
|
||||||
val := mod.Get(arg)
|
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, val), nil
|
|
||||||
}
|
|
||||||
modNewIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
k, err := c.StringArg(1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
v := c.Arg(2)
|
|
||||||
if k == "highlighter" {
|
|
||||||
var err error
|
|
||||||
// fine to assign, since itll be either nil or a closure
|
|
||||||
highlighter, err = c.ClosureArg(2)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("hilbish.highlighter has to be a function")
|
|
||||||
}
|
|
||||||
} else if k == "hinter" {
|
|
||||||
var err error
|
|
||||||
hinter, err = c.ClosureArg(2)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("hilbish.hinter has to be a function")
|
|
||||||
}
|
|
||||||
} else if modVal := mod.Get(rt.StringValue(k)); modVal != rt.NilValue {
|
|
||||||
return nil, errors.New("not allowed to override in hilbish table")
|
|
||||||
}
|
|
||||||
mod.Set(rt.StringValue(k), v)
|
|
||||||
|
|
||||||
return c.Next(), nil
|
|
||||||
}
|
|
||||||
modmt.Set(rt.StringValue("__newindex"), rt.FunctionValue(rt.NewGoFunction(modNewIndex, "__newindex", 3, false)))
|
|
||||||
modmt.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(modIndex, "__index", 2, false)))
|
|
||||||
fakeMod.SetMetatable(modmt)
|
|
||||||
|
|
||||||
util.SetExports(rtm, mod, exports)
|
util.SetExports(rtm, mod, exports)
|
||||||
hshMod = mod
|
hshMod = mod
|
||||||
|
|
||||||
|
@ -102,81 +73,68 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
username = strings.Split(username, "\\")[1] // for some reason Username includes the hostname on windows
|
username = strings.Split(username, "\\")[1] // for some reason Username includes the hostname on windows
|
||||||
}
|
}
|
||||||
|
|
||||||
util.SetFieldProtected(fakeMod, mod, "ver", rt.StringValue(getVersion()), "Hilbish version")
|
util.SetField(rtm, mod, "ver", rt.StringValue(getVersion()))
|
||||||
util.SetFieldProtected(fakeMod, mod, "user", rt.StringValue(username), "Username of user")
|
util.SetField(rtm, mod, "goVersion", rt.StringValue(runtime.Version()))
|
||||||
util.SetFieldProtected(fakeMod, mod, "host", rt.StringValue(host), "Host name of the machine")
|
util.SetField(rtm, mod, "user", rt.StringValue(username))
|
||||||
util.SetFieldProtected(fakeMod, mod, "home", rt.StringValue(curuser.HomeDir), "Home directory of the user")
|
util.SetField(rtm, mod, "host", rt.StringValue(host))
|
||||||
util.SetFieldProtected(fakeMod, mod, "dataDir", rt.StringValue(dataDir), "Directory for Hilbish's data files")
|
util.SetField(rtm, mod, "home", rt.StringValue(curuser.HomeDir))
|
||||||
util.SetFieldProtected(fakeMod, mod, "interactive", rt.BoolValue(interactive), "If this is an interactive shell")
|
util.SetField(rtm, mod, "dataDir", rt.StringValue(dataDir))
|
||||||
util.SetFieldProtected(fakeMod, mod, "login", rt.BoolValue(login), "Whether this is a login shell")
|
util.SetField(rtm, mod, "interactive", rt.BoolValue(interactive))
|
||||||
util.SetFieldProtected(fakeMod, mod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)")
|
util.SetField(rtm, mod, "login", rt.BoolValue(login))
|
||||||
util.SetFieldProtected(fakeMod, mod, "exitCode", rt.IntValue(0), "Exit code of last exected command")
|
util.SetField(rtm, mod, "vimMode", rt.NilValue)
|
||||||
util.Document(fakeMod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.")
|
util.SetField(rtm, mod, "exitCode", rt.IntValue(0))
|
||||||
|
|
||||||
// hilbish.userDir table
|
// hilbish.userDir table
|
||||||
hshuser := rt.NewTable()
|
hshuser := userDirLoader(rtm)
|
||||||
|
|
||||||
util.SetField(rtm, hshuser, "config", rt.StringValue(confDir), "User's config directory")
|
|
||||||
util.SetField(rtm, hshuser, "data", rt.StringValue(userDataDir), "XDG data directory")
|
|
||||||
util.Document(hshuser, "User directories to store configs and/or modules.")
|
|
||||||
mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser))
|
mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser))
|
||||||
|
|
||||||
// hilbish.os table
|
// hilbish.os table
|
||||||
hshos := rt.NewTable()
|
hshos := hshosLoader(rtm)
|
||||||
info, _ := osinfo.GetOSInfo()
|
|
||||||
|
|
||||||
util.SetField(rtm, hshos, "family", rt.StringValue(info.Family), "Family name of the current OS")
|
|
||||||
util.SetField(rtm, hshos, "name", rt.StringValue(info.Name), "Pretty name of the current OS")
|
|
||||||
util.SetField(rtm, hshos, "version", rt.StringValue(info.Version), "Version of the current OS")
|
|
||||||
util.Document(hshos, "OS info interface")
|
|
||||||
mod.Set(rt.StringValue("os"), rt.TableValue(hshos))
|
mod.Set(rt.StringValue("os"), rt.TableValue(hshos))
|
||||||
|
|
||||||
// hilbish.aliases table
|
// hilbish.aliases table
|
||||||
aliases = newAliases()
|
aliases = newAliases()
|
||||||
aliasesModule := aliases.Loader(rtm)
|
aliasesModule := aliases.Loader(rtm)
|
||||||
util.Document(aliasesModule, "Alias inferface for Hilbish.")
|
|
||||||
mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule))
|
mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule))
|
||||||
|
|
||||||
// hilbish.history table
|
// hilbish.history table
|
||||||
historyModule := lr.Loader(rtm)
|
historyModule := lr.Loader(rtm)
|
||||||
mod.Set(rt.StringValue("history"), rt.TableValue(historyModule))
|
mod.Set(rt.StringValue("history"), rt.TableValue(historyModule))
|
||||||
util.Document(historyModule, "History interface for Hilbish.")
|
|
||||||
|
|
||||||
// hilbish.completion table
|
// hilbish.completion table
|
||||||
hshcomp := completionLoader(rtm)
|
hshcomp := completionLoader(rtm)
|
||||||
util.Document(hshcomp, "Completions interface for Hilbish.")
|
// TODO: REMOVE "completion" AND ONLY USE "completions" WITH AN S
|
||||||
mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp))
|
mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp))
|
||||||
|
mod.Set(rt.StringValue("completions"), rt.TableValue(hshcomp))
|
||||||
|
|
||||||
// hilbish.runner table
|
// hilbish.runner table
|
||||||
runnerModule := runnerModeLoader(rtm)
|
runnerModule := runnerModeLoader(rtm)
|
||||||
util.Document(runnerModule, "Runner/exec interface for Hilbish.")
|
|
||||||
mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule))
|
mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule))
|
||||||
|
|
||||||
// hilbish.jobs table
|
// hilbish.jobs table
|
||||||
jobs = newJobHandler()
|
jobs = newJobHandler()
|
||||||
jobModule := jobs.loader(rtm)
|
jobModule := jobs.loader(rtm)
|
||||||
util.Document(jobModule, "(Background) job interface.")
|
|
||||||
mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule))
|
mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule))
|
||||||
|
|
||||||
// hilbish.timers table
|
// hilbish.timers table
|
||||||
timers = newTimerHandler()
|
timers = newTimersModule()
|
||||||
timerModule := timers.loader(rtm)
|
timersModule := timers.loader(rtm)
|
||||||
util.Document(timerModule, "Timer interface, for control of all intervals and timeouts.")
|
mod.Set(rt.StringValue("timers"), rt.TableValue(timersModule))
|
||||||
mod.Set(rt.StringValue("timers"), rt.TableValue(timerModule))
|
|
||||||
|
|
||||||
editorModule := editorLoader(rtm)
|
editorModule := editorLoader(rtm)
|
||||||
util.Document(editorModule, "")
|
|
||||||
mod.Set(rt.StringValue("editor"), rt.TableValue(editorModule))
|
mod.Set(rt.StringValue("editor"), rt.TableValue(editorModule))
|
||||||
|
|
||||||
versionModule := rt.NewTable()
|
versionModule := rt.NewTable()
|
||||||
util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch), "Git branch Hilbish was compiled from")
|
util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch))
|
||||||
util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion()), "Full version info, including release name")
|
util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion()))
|
||||||
util.SetField(rtm, versionModule, "commit", rt.StringValue(gitCommit), "Git commit Hilbish was compiled from")
|
util.SetField(rtm, versionModule, "commit", rt.StringValue(gitCommit))
|
||||||
util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName), "Release name")
|
util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName))
|
||||||
util.Document(versionModule, "Version info interface.")
|
|
||||||
mod.Set(rt.StringValue("version"), rt.TableValue(versionModule))
|
mod.Set(rt.StringValue("version"), rt.TableValue(versionModule))
|
||||||
|
|
||||||
return rt.TableValue(fakeMod), nil
|
pluginModule := moduleLoader(rtm)
|
||||||
|
mod.Set(rt.StringValue("module"), rt.TableValue(pluginModule))
|
||||||
|
|
||||||
|
return rt.TableValue(mod), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getenv(key, fallback string) string {
|
func getenv(key, fallback string) string {
|
||||||
|
@ -188,20 +146,72 @@ func getenv(key, fallback string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setVimMode(mode string) {
|
func setVimMode(mode string) {
|
||||||
util.SetField(l, hshMod, "vimMode", rt.StringValue(mode), "Current Vim mode of Hilbish (nil if not in Vim mode)")
|
util.SetField(l, hshMod, "vimMode", rt.StringValue(mode))
|
||||||
hooks.Emit("hilbish.vimMode", mode)
|
hooks.Emit("hilbish.vimMode", mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unsetVimMode() {
|
func unsetVimMode() {
|
||||||
util.SetField(l, hshMod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)")
|
util.SetField(l, hshMod, "vimMode", rt.NilValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// run(cmd, returnOut) -> exitCode, stdout, stderr
|
func handleStream(v rt.Value, strms *streams, errStream bool) error {
|
||||||
// Runs `cmd` in Hilbish's sh interpreter.
|
ud, ok := v.TryUserData()
|
||||||
// If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
|
if !ok {
|
||||||
// 3rd values instead of being outputted to the terminal.
|
return errors.New("expected metatable argument")
|
||||||
// --- @param cmd string
|
}
|
||||||
|
|
||||||
|
val := ud.Value()
|
||||||
|
var varstrm io.Writer
|
||||||
|
if f, ok := val.(*iolib.File); ok {
|
||||||
|
varstrm = f.Handle()
|
||||||
|
}
|
||||||
|
|
||||||
|
if f, ok := val.(*sink); ok {
|
||||||
|
varstrm = f.writer
|
||||||
|
}
|
||||||
|
|
||||||
|
if varstrm == nil {
|
||||||
|
return errors.New("expected either a sink or file")
|
||||||
|
}
|
||||||
|
|
||||||
|
if errStream {
|
||||||
|
strms.stderr = varstrm
|
||||||
|
} else {
|
||||||
|
strms.stdout = varstrm
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// run(cmd, streams) -> exitCode (number), stdout (string), stderr (string)
|
||||||
|
// Runs `cmd` in Hilbish's shell script interpreter.
|
||||||
|
// The `streams` parameter specifies the output and input streams the command should use.
|
||||||
|
// For example, to write command output to a sink.
|
||||||
|
// As a table, the caller can directly specify the standard output, error, and input
|
||||||
|
// streams of the command with the table keys `out`, `err`, and `input` respectively.
|
||||||
|
// As a boolean, it specifies whether the command should use standard output or return its output streams.
|
||||||
|
// #param cmd string
|
||||||
|
// #param streams table|boolean
|
||||||
|
// #returns number, string, string
|
||||||
|
// #example
|
||||||
|
/*
|
||||||
|
// This code is the same as `ls -l | wc -l`
|
||||||
|
local fs = require 'fs'
|
||||||
|
local pr, pw = fs.pipe()
|
||||||
|
hilbish.run('ls -l', {
|
||||||
|
stdout = pw,
|
||||||
|
stderr = pw,
|
||||||
|
})
|
||||||
|
|
||||||
|
pw:close()
|
||||||
|
|
||||||
|
hilbish.run('wc -l', {
|
||||||
|
stdin = pr
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
// #example
|
||||||
func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
// TODO: ON BREAKING RELEASE, DO NOT ACCEPT `streams` AS A BOOLEAN.
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -210,20 +220,57 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strms := &streams{}
|
||||||
var terminalOut bool
|
var terminalOut bool
|
||||||
if len(c.Etc()) != 0 {
|
if len(c.Etc()) != 0 {
|
||||||
tout := c.Etc()[0]
|
tout := c.Etc()[0]
|
||||||
termOut, ok := tout.TryBool()
|
|
||||||
terminalOut = termOut
|
var ok bool
|
||||||
|
terminalOut, ok = tout.TryBool()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("bad argument to run (expected boolean, got " + tout.TypeName() + ")")
|
luastreams, ok := tout.TryTable()
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("bad argument to run (expected boolean or table, got " + tout.TypeName() + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
handleStream(luastreams.Get(rt.StringValue("out")), strms, false)
|
||||||
|
handleStream(luastreams.Get(rt.StringValue("err")), strms, true)
|
||||||
|
|
||||||
|
stdinstrm := luastreams.Get(rt.StringValue("input"))
|
||||||
|
if !stdinstrm.IsNil() {
|
||||||
|
ud, ok := stdinstrm.TryUserData()
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("bad type as run stdin stream (expected userdata as either sink or file, got " + stdinstrm.TypeName() + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
val := ud.Value()
|
||||||
|
var varstrm io.Reader
|
||||||
|
if f, ok := val.(*iolib.File); ok {
|
||||||
|
varstrm = f.Handle()
|
||||||
|
}
|
||||||
|
|
||||||
|
if f, ok := val.(*sink); ok {
|
||||||
|
varstrm = f.reader
|
||||||
|
}
|
||||||
|
|
||||||
|
if varstrm == nil {
|
||||||
|
return nil, errors.New("bad type as run stdin stream (expected userdata as either sink or file)")
|
||||||
|
}
|
||||||
|
|
||||||
|
strms.stdin = varstrm
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !terminalOut {
|
||||||
|
strms = &streams{
|
||||||
|
stdout: new(bytes.Buffer),
|
||||||
|
stderr: new(bytes.Buffer),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
terminalOut = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var exitcode uint8
|
var exitcode uint8
|
||||||
stdout, stderr, err := execCommand(cmd, terminalOut)
|
stdout, stderr, err := execCommand(cmd, strms)
|
||||||
|
|
||||||
if code, ok := interp.IsExitStatus(err); ok {
|
if code, ok := interp.IsExitStatus(err); ok {
|
||||||
exitcode = code
|
exitcode = code
|
||||||
|
@ -231,18 +278,20 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
exitcode = 1
|
exitcode = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
stdoutStr := ""
|
var stdoutStr, stderrStr string
|
||||||
stderrStr := ""
|
if stdoutBuf, ok := stdout.(*bytes.Buffer); ok {
|
||||||
if !terminalOut {
|
stdoutStr = stdoutBuf.String()
|
||||||
stdoutStr = stdout.(*bytes.Buffer).String()
|
}
|
||||||
stderrStr = stderr.(*bytes.Buffer).String()
|
if stderrBuf, ok := stderr.(*bytes.Buffer); ok {
|
||||||
|
stderrStr = stderrBuf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitcode)), rt.StringValue(stdoutStr), rt.StringValue(stderrStr)), nil
|
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitcode)), rt.StringValue(stdoutStr), rt.StringValue(stderrStr)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// cwd()
|
// cwd() -> string
|
||||||
// Returns the current directory of the shell
|
// Returns the current directory of the shell.
|
||||||
|
// #returns string
|
||||||
func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
cwd, _ := os.Getwd()
|
cwd, _ := os.Getwd()
|
||||||
|
|
||||||
|
@ -250,21 +299,28 @@ func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// read(prompt) -> input?
|
// read(prompt) -> input (string)
|
||||||
// Read input from the user, using Hilbish's line editor/input reader.
|
// Read input from the user, using Hilbish's line editor/input reader.
|
||||||
// This is a separate instance from the one Hilbish actually uses.
|
// This is a separate instance from the one Hilbish actually uses.
|
||||||
// Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
|
// Returns `input`, will be nil if Ctrl-D is pressed, or an error occurs.
|
||||||
// --- @param prompt string
|
// #param prompt? string Text to print before input, can be empty.
|
||||||
|
// #returns string|nil
|
||||||
func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
luaprompt := c.Arg(0)
|
||||||
return nil, err
|
if typ := luaprompt.Type(); typ != rt.StringType && typ != rt.NilType {
|
||||||
|
return nil, errors.New("expected #1 to be a string")
|
||||||
}
|
}
|
||||||
luaprompt, err := c.StringArg(0)
|
prompt, ok := luaprompt.TryString()
|
||||||
if err != nil {
|
if !ok {
|
||||||
return nil, err
|
// if we are here and `luaprompt` is not a string, it's nil
|
||||||
|
// substitute with an empty string
|
||||||
|
prompt = ""
|
||||||
}
|
}
|
||||||
lualr := newLineReader("", true)
|
|
||||||
lualr.SetPrompt(luaprompt)
|
lualr := &lineReader{
|
||||||
|
rl: readline.NewInstance(),
|
||||||
|
}
|
||||||
|
lualr.SetPrompt(prompt)
|
||||||
|
|
||||||
input, err := lualr.Read()
|
input, err := lualr.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -275,15 +331,22 @@ func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
prompt(str, typ?)
|
prompt(str, typ)
|
||||||
Changes the shell prompt to `str`
|
Changes the shell prompt to the provided string.
|
||||||
There are a few verbs that can be used in the prompt text.
|
There are a few verbs that can be used in the prompt text.
|
||||||
These will be formatted and replaced with the appropriate values.
|
These will be formatted and replaced with the appropriate values.
|
||||||
`%d` - Current working directory
|
`%d` - Current working directory
|
||||||
`%u` - Name of current user
|
`%u` - Name of current user
|
||||||
`%h` - Hostname of device
|
`%h` - Hostname of device
|
||||||
--- @param str string
|
#param str string
|
||||||
--- @param typ string Type of prompt, being left or right. Left by default.
|
#param typ? string Type of prompt, being left or right. Left by default.
|
||||||
|
#example
|
||||||
|
-- the default hilbish prompt without color
|
||||||
|
hilbish.prompt '%u %d ∆'
|
||||||
|
-- or something of old:
|
||||||
|
hilbish.prompt '%u@%h :%d $'
|
||||||
|
-- prompt: user@hostname: ~/directory $
|
||||||
|
#example
|
||||||
*/
|
*/
|
||||||
func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
err := c.Check1Arg()
|
err := c.Check1Arg()
|
||||||
|
@ -317,8 +380,28 @@ func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// multiprompt(str)
|
// multiprompt(str)
|
||||||
// Changes the continued line prompt to `str`
|
// Changes the text prompt when Hilbish asks for more input.
|
||||||
// --- @param str string
|
// This will show up when text is incomplete, like a missing quote
|
||||||
|
// #param str string
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
--[[
|
||||||
|
imagine this is your text input:
|
||||||
|
user ~ ∆ echo "hey
|
||||||
|
|
||||||
|
but there's a missing quote! hilbish will now prompt you so the terminal
|
||||||
|
will look like:
|
||||||
|
user ~ ∆ echo "hey
|
||||||
|
--> ...!"
|
||||||
|
|
||||||
|
so then you get
|
||||||
|
user ~ ∆ echo "hey
|
||||||
|
--> ...!"
|
||||||
|
hey ...!
|
||||||
|
]]--
|
||||||
|
hilbish.multiprompt '-->'
|
||||||
|
#example
|
||||||
|
*/
|
||||||
func hlmultiprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlmultiprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -333,9 +416,19 @@ func hlmultiprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// alias(cmd, orig)
|
// alias(cmd, orig)
|
||||||
// Sets an alias of `cmd` to `orig`
|
// Sets an alias, with a name of `cmd` to another command.
|
||||||
// --- @param cmd string
|
// #param cmd string Name of the alias
|
||||||
// --- @param orig string
|
// #param orig string Command that will be aliased
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
-- With this, "ga file" will turn into "git add file"
|
||||||
|
hilbish.alias('ga', 'git add')
|
||||||
|
|
||||||
|
-- Numbered substitutions are supported here!
|
||||||
|
hilbish.alias('dircount', 'ls %1 | wc -l')
|
||||||
|
-- "dircount ~" would count how many files are in ~ (home directory).
|
||||||
|
#example
|
||||||
|
*/
|
||||||
func hlalias(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlalias(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.CheckNArgs(2); err != nil {
|
if err := c.CheckNArgs(2); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -355,8 +448,20 @@ func hlalias(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendPath(dir)
|
// appendPath(dir)
|
||||||
// Appends `dir` to $PATH
|
// Appends the provided dir to the command path (`$PATH`)
|
||||||
// --- @param dir string|table
|
// #param dir string|table Directory (or directories) to append to path
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
hilbish.appendPath '~/go/bin'
|
||||||
|
-- Will add ~/go/bin to the command path.
|
||||||
|
|
||||||
|
-- Or do multiple:
|
||||||
|
hilbish.appendPath {
|
||||||
|
'~/go/bin',
|
||||||
|
'~/.local/bin'
|
||||||
|
}
|
||||||
|
#example
|
||||||
|
*/
|
||||||
func hlappendPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlappendPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -390,8 +495,9 @@ func appendPath(dir string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// exec(cmd)
|
// exec(cmd)
|
||||||
// Replaces running hilbish with `cmd`
|
// Replaces the currently running Hilbish instance with the supplied command.
|
||||||
// --- @param cmd string
|
// This can be used to do an in-place restart.
|
||||||
|
// #param cmd string
|
||||||
func hlexec(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlexec(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -425,8 +531,11 @@ func hlexec(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// goro(fn)
|
// goro(fn)
|
||||||
// Puts `fn` in a goroutine
|
// Puts `fn` in a Goroutine.
|
||||||
// --- @param fn function
|
// This can be used to run any function in another thread at the same time as other Lua code.
|
||||||
|
// **NOTE: THIS FUNCTION MAY CRASH HILBISH IF OUTSIDE VARIABLES ARE ACCESSED.**
|
||||||
|
// **This is a limitation of the Lua runtime.**
|
||||||
|
// #param fn function
|
||||||
func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -438,6 +547,12 @@ func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
|
||||||
// call fn
|
// call fn
|
||||||
go func() {
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
// do something here?
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
_, err := rt.Call1(l.MainThread(), rt.FunctionValue(fn), c.Etc()...)
|
_, err := rt.Call1(l.MainThread(), rt.FunctionValue(fn), c.Etc()...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "Error in goro function:\n\n", err)
|
fmt.Fprintln(os.Stderr, "Error in goro function:\n\n", err)
|
||||||
|
@ -447,12 +562,12 @@ func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// timeout(cb, time)
|
// timeout(cb, time) -> @Timer
|
||||||
// Runs the `cb` function after `time` in milliseconds
|
// Executed the `cb` function after a period of `time`.
|
||||||
// Returns a `timer` object (see `doc timers`).
|
// This creates a Timer that starts ticking immediately.
|
||||||
// --- @param cb function
|
// #param cb function
|
||||||
// --- @param time number
|
// #param time number Time to run in milliseconds.
|
||||||
// --- @return table
|
// #returns Timer
|
||||||
func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.CheckNArgs(2); err != nil {
|
if err := c.CheckNArgs(2); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -473,12 +588,12 @@ func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil
|
return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// interval(cb, time)
|
// interval(cb, time) -> @Timer
|
||||||
// Runs the `cb` function every `time` milliseconds.
|
// Runs the `cb` function every specified amount of `time`.
|
||||||
// Returns a `timer` object (see `doc timers`).
|
// This creates a timer that ticking immediately.
|
||||||
// --- @param cb function
|
// #param cb function
|
||||||
// --- @param time number
|
// #param time number Time in milliseconds.
|
||||||
// --- @return table
|
// #return Timer
|
||||||
func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.CheckNArgs(2); err != nil {
|
if err := c.CheckNArgs(2); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -500,13 +615,40 @@ func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// complete(scope, cb)
|
// complete(scope, cb)
|
||||||
// Registers a completion handler for `scope`.
|
// Registers a completion handler for the specified scope.
|
||||||
// A `scope` is currently only expected to be `command.<cmd>`,
|
// A `scope` is expected to be `command.<cmd>`,
|
||||||
// replacing <cmd> with the name of the command (for example `command.git`).
|
// replacing <cmd> with the name of the command (for example `command.git`).
|
||||||
// `cb` must be a function that returns a table of "completion groups."
|
// The documentation for completions, under Features/Completions or `doc completions`
|
||||||
// Check `doc completions` for more information.
|
// provides more details.
|
||||||
// --- @param scope string
|
// #param scope string
|
||||||
// --- @param cb function
|
// #param cb function
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
-- This is a very simple example. Read the full doc for completions for details.
|
||||||
|
hilbish.complete('command.sudo', function(query, ctx, fields)
|
||||||
|
if #fields == 0 then
|
||||||
|
-- complete for commands
|
||||||
|
local comps, pfx = hilbish.completion.bins(query, ctx, fields)
|
||||||
|
local compGroup = {
|
||||||
|
items = comps, -- our list of items to complete
|
||||||
|
type = 'grid' -- what our completions will look like.
|
||||||
|
}
|
||||||
|
|
||||||
|
return {compGroup}, pfx
|
||||||
|
end
|
||||||
|
|
||||||
|
-- otherwise just be boring and return files
|
||||||
|
|
||||||
|
local comps, pfx = hilbish.completion.files(query, ctx, fields)
|
||||||
|
local compGroup = {
|
||||||
|
items = comps,
|
||||||
|
type = 'grid'
|
||||||
|
}
|
||||||
|
|
||||||
|
return {compGroup}, pfx
|
||||||
|
end)
|
||||||
|
#example
|
||||||
|
*/
|
||||||
func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
scope, cb, err := util.HandleStrCallback(t, c)
|
scope, cb, err := util.HandleStrCallback(t, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -518,8 +660,8 @@ func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// prependPath(dir)
|
// prependPath(dir)
|
||||||
// Prepends `dir` to $PATH
|
// Prepends `dir` to $PATH.
|
||||||
// --- @param dir string
|
// #param dir string
|
||||||
func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -539,9 +681,11 @@ func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// which(name)
|
// which(name) -> string
|
||||||
// Checks if `name` is a valid command
|
// Checks if `name` is a valid command.
|
||||||
// --- @param binName string
|
// Will return the path of the binary, or a basename if it's a commander.
|
||||||
|
// #param name string
|
||||||
|
// #returns string
|
||||||
func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -551,10 +695,13 @@ func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := aliases.Resolve(name)
|
// itll return either the original command or what was passed
|
||||||
|
// if name isnt empty its not an issue
|
||||||
|
alias := aliases.Resolve(name)
|
||||||
|
cmd := strings.Split(alias, " ")[0]
|
||||||
|
|
||||||
// check for commander
|
// check for commander
|
||||||
if commands[cmd] != nil {
|
if cmds.Commands[cmd] != nil {
|
||||||
// they dont resolve to a path, so just send the cmd
|
// they dont resolve to a path, so just send the cmd
|
||||||
return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
|
return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
|
||||||
}
|
}
|
||||||
|
@ -568,8 +715,10 @@ func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// inputMode(mode)
|
// inputMode(mode)
|
||||||
// Sets the input mode for Hilbish's line reader. Accepts either emacs or vim
|
// Sets the input mode for Hilbish's line reader.
|
||||||
// --- @param mode string
|
// `emacs` is the default. Setting it to `vim` changes behavior of input to be
|
||||||
|
// Vim-like with modes and Vim keybinds.
|
||||||
|
// #param mode string Can be set to either `emacs` or `vim`
|
||||||
func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -594,12 +743,14 @@ func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// runnerMode(mode)
|
// runnerMode(mode)
|
||||||
// Sets the execution/runner mode for interactive Hilbish. This determines whether
|
// Sets the execution/runner mode for interactive Hilbish.
|
||||||
// Hilbish wll try to run input as Lua and/or sh or only do one of either.
|
// 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),
|
// 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
|
// sh, and lua. It also accepts a function, to which if it is passed one
|
||||||
// will call it to execute user input instead.
|
// will call it to execute user input instead.
|
||||||
// --- @param mode string|function
|
// Read [about runner mode](../features/runner-mode) for more information.
|
||||||
|
// #param mode string|function
|
||||||
func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -625,18 +776,42 @@ func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
// line and cursor position. It is expected to return a string which is used
|
// 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,
|
// as the text for the hint. This is by default a shim. To set hints,
|
||||||
// override this function with your custom handler.
|
// override this function with your custom handler.
|
||||||
// --- @param line string
|
// #param line string
|
||||||
// --- @param pos int
|
// #param pos number Position of cursor in line. Usually equals string.len(line)
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
-- this will display "hi" after the cursor in a dimmed color.
|
||||||
|
function hilbish.hinter(line, pos)
|
||||||
|
return 'hi'
|
||||||
|
end
|
||||||
|
#example
|
||||||
|
*/
|
||||||
func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// highlighter(line)
|
// highlighter(line)
|
||||||
// Line highlighter handler. This is mainly for syntax highlighting, but in
|
// Line highlighter handler.
|
||||||
// reality could set the input of the prompt to *display* anything. The
|
// This is mainly for syntax highlighting, but in reality could set the input
|
||||||
// callback is passed the current line and is expected to return a line that
|
// of the prompt to *display* anything. The callback is passed the current line
|
||||||
// will be used as the input display.
|
// and is expected to return a line that will be used as the input display.
|
||||||
// --- @param line string
|
// Note that to set a highlighter, one has to override this function.
|
||||||
|
// #example
|
||||||
|
// --This code will highlight all double quoted strings in green.
|
||||||
|
// function hilbish.highlighter(line)
|
||||||
|
// return line:gsub('"%w+"', function(c) return lunacolors.green(c) end)
|
||||||
|
// end
|
||||||
|
// #example
|
||||||
|
// #param line string
|
||||||
func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
line, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.PushingNext1(t.Runtime, rt.StringValue(line)), nil
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 45 KiB |
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
Binary file not shown.
Before Width: | Height: | Size: 18 KiB |
|
@ -7,29 +7,301 @@ import (
|
||||||
"go/doc"
|
"go/doc"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
md "github.com/atsushinee/go-markdown-generator/doc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EmmyPiece struct {
|
var header = `---
|
||||||
FuncName string
|
title: %s %s
|
||||||
Docs []string
|
description: %s
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "API"
|
||||||
|
---
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
type emmyPiece struct {
|
||||||
|
DocPiece *docPiece
|
||||||
|
Annotations []string
|
||||||
Params []string // we only need to know param name to put in function
|
Params []string // we only need to know param name to put in function
|
||||||
|
FuncName string
|
||||||
}
|
}
|
||||||
type DocPiece struct {
|
|
||||||
|
type module struct {
|
||||||
|
Types []docPiece
|
||||||
|
Docs []docPiece
|
||||||
|
Fields []docPiece
|
||||||
|
Properties []docPiece
|
||||||
|
ShortDescription string
|
||||||
|
Description string
|
||||||
|
ParentModule string
|
||||||
|
HasInterfaces bool
|
||||||
|
HasTypes bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type param struct{
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
Doc []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type docPiece struct {
|
||||||
Doc []string
|
Doc []string
|
||||||
FuncSig string
|
FuncSig string
|
||||||
FuncName string
|
FuncName string
|
||||||
|
Interfacing string
|
||||||
|
ParentModule string
|
||||||
|
GoFuncName string
|
||||||
|
IsInterface bool
|
||||||
|
IsMember bool
|
||||||
|
IsType bool
|
||||||
|
Fields []docPiece
|
||||||
|
Properties []docPiece
|
||||||
|
Params []param
|
||||||
|
Tags map[string][]tag
|
||||||
|
}
|
||||||
|
|
||||||
|
type tag struct {
|
||||||
|
id string
|
||||||
|
fields []string
|
||||||
|
startIdx int
|
||||||
|
}
|
||||||
|
|
||||||
|
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 idx, 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, startIdx: idx},
|
||||||
|
}
|
||||||
|
if len(tagParts) >= 2 {
|
||||||
|
tags[tagParts[0]][0].fields = tagParts[2:]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if tagParts[0] == "example" {
|
||||||
|
exampleIdx := tags["example"][0].startIdx
|
||||||
|
exampleCode := pts[exampleIdx+1:idx]
|
||||||
|
|
||||||
|
tags["example"][0].fields = exampleCode
|
||||||
|
parts = strings.Split(strings.Replace(strings.Join(parts, "\n"), strings.TrimPrefix(strings.Join(exampleCode, "\n"), "#example\n"), "", -1), "\n")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fleds := []string{}
|
||||||
|
if len(tagParts) >= 2 {
|
||||||
|
fleds = tagParts[2:]
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
Tags: tags,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
var params []param
|
||||||
|
if paramsRaw := tags["param"]; paramsRaw != nil {
|
||||||
|
params = make([]param, len(paramsRaw))
|
||||||
|
for i, p := range paramsRaw {
|
||||||
|
params[i] = param{
|
||||||
|
Name: p.id,
|
||||||
|
Type: p.fields[0],
|
||||||
|
Doc: p.fields[1:],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range doc {
|
||||||
|
if strings.HasPrefix(d, "---") {
|
||||||
|
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,
|
||||||
|
Params: params,
|
||||||
|
Tags: tags,
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(dps.GoFuncName, strings.ToLower("loader")) {
|
||||||
|
dps.Doc = parts
|
||||||
|
}
|
||||||
|
em.DocPiece = dps
|
||||||
|
|
||||||
|
emmyDocs[mod] = append(emmyDocs[mod], em)
|
||||||
|
return dps
|
||||||
}
|
}
|
||||||
|
|
||||||
// feel free to clean this up
|
|
||||||
// it works, dont really care about the code
|
|
||||||
func main() {
|
func main() {
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
os.Mkdir("docs", 0777)
|
os.Mkdir("docs", 0777)
|
||||||
|
os.Mkdir("docs/api", 0777)
|
||||||
os.Mkdir("emmyLuaDocs", 0777)
|
os.Mkdir("emmyLuaDocs", 0777)
|
||||||
|
|
||||||
|
|
||||||
dirs := []string{"./"}
|
dirs := []string{"./"}
|
||||||
filepath.Walk("golibs/", func (path string, info os.FileInfo, err error) error {
|
filepath.Walk("golibs/", func (path string, info os.FileInfo, err error) error {
|
||||||
if !info.IsDir() {
|
if !info.IsDir() {
|
||||||
|
@ -51,120 +323,356 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix := map[string]string{
|
interfaceModules := make(map[string]*module)
|
||||||
"hilbish": "hl",
|
|
||||||
"fs": "f",
|
|
||||||
"commander": "c",
|
|
||||||
"bait": "b",
|
|
||||||
"terminal": "term",
|
|
||||||
}
|
|
||||||
docs := make(map[string][]DocPiece)
|
|
||||||
emmyDocs := make(map[string][]EmmyPiece)
|
|
||||||
|
|
||||||
for l, f := range pkgs {
|
for l, f := range pkgs {
|
||||||
p := doc.New(f, "./", doc.AllDecls)
|
p := doc.New(f, "./", doc.AllDecls)
|
||||||
|
pieces := []docPiece{}
|
||||||
|
typePieces := []docPiece{}
|
||||||
|
mod := l
|
||||||
|
if mod == "main" {
|
||||||
|
mod = "hilbish"
|
||||||
|
}
|
||||||
|
var hasInterfaces bool
|
||||||
for _, t := range p.Funcs {
|
for _, t := range p.Funcs {
|
||||||
mod := l
|
piece := setupDoc(mod, t)
|
||||||
if strings.HasPrefix(t.Name, "hl") { mod = "hilbish" }
|
if piece == nil {
|
||||||
if !strings.HasPrefix(t.Name, prefix[mod]) || t.Name == "Loader" { continue }
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dps := DocPiece{
|
pieces = append(pieces, *piece)
|
||||||
Doc: funcdoc,
|
if piece.IsInterface {
|
||||||
FuncSig: funcsig,
|
hasInterfaces = true
|
||||||
FuncName: strings.TrimPrefix(t.Name, prefix[mod]),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
docs[mod] = append(docs[mod], dps)
|
|
||||||
emmyDocs[mod] = append(emmyDocs[mod], em)
|
|
||||||
}
|
}
|
||||||
for _, t := range p.Types {
|
for _, t := range p.Types {
|
||||||
for _, m := range t.Methods {
|
typePiece := setupDocType(mod, t)
|
||||||
if !strings.HasPrefix(m.Name, prefix[l]) || m.Name == "Loader" { continue }
|
if typePiece != nil {
|
||||||
parts := strings.Split(strings.TrimSpace(m.Doc), "\n")
|
typePieces = append(typePieces, *typePiece)
|
||||||
funcsig := parts[0]
|
if typePiece.IsInterface {
|
||||||
doc := parts[1:]
|
hasInterfaces = true
|
||||||
funcdoc := []string{}
|
|
||||||
em := EmmyPiece{FuncName: strings.TrimPrefix(m.Name, prefix[l])}
|
|
||||||
for _, d := range doc {
|
|
||||||
if strings.HasPrefix(d, "---") {
|
|
||||||
emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
|
|
||||||
emmyLinePieces := strings.Split(emmyLine, " ")
|
|
||||||
emmyType := emmyLinePieces[0]
|
|
||||||
if emmyType == "@param" {
|
|
||||||
em.Params = append(em.Params, emmyLinePieces[1])
|
|
||||||
}
|
|
||||||
if emmyType == "@vararg" {
|
|
||||||
em.Params = append(em.Params, "...") // add vararg
|
|
||||||
}
|
|
||||||
em.Docs = append(em.Docs, d)
|
|
||||||
} else {
|
|
||||||
funcdoc = append(funcdoc, d)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dps := DocPiece{
|
}
|
||||||
Doc: funcdoc,
|
|
||||||
FuncSig: funcsig,
|
for _, m := range t.Methods {
|
||||||
FuncName: strings.TrimPrefix(m.Name, prefix[l]),
|
piece := setupDoc(mod, m)
|
||||||
|
if piece == nil {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
docs[l] = append(docs[l], dps)
|
pieces = append(pieces, *piece)
|
||||||
emmyDocs[l] = append(emmyDocs[l], em)
|
if piece.IsInterface {
|
||||||
|
hasInterfaces = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tags, descParts := getTagsAndDocs(strings.TrimSpace(p.Doc))
|
||||||
|
shortDesc := descParts[0]
|
||||||
|
desc := descParts[1:]
|
||||||
|
filteredPieces := []docPiece{}
|
||||||
|
filteredTypePieces := []docPiece{}
|
||||||
|
for _, piece := range pieces {
|
||||||
|
if !piece.IsInterface {
|
||||||
|
filteredPieces = append(filteredPieces, piece)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
modname := piece.ParentModule + "." + piece.Interfacing
|
||||||
|
if interfaceModules[modname] == nil {
|
||||||
|
interfaceModules[modname] = &module{
|
||||||
|
ParentModule: piece.ParentModule,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(piece.GoFuncName, strings.ToLower("loader")) {
|
||||||
|
shortDesc := piece.Doc[0]
|
||||||
|
desc := piece.Doc[1:]
|
||||||
|
interfaceModules[modname].ShortDescription = shortDesc
|
||||||
|
interfaceModules[modname].Description = strings.Join(desc, "\n")
|
||||||
|
interfaceModules[modname].Fields = piece.Fields
|
||||||
|
interfaceModules[modname].Properties = piece.Properties
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
interfaceModules[modname].Docs = append(interfaceModules[modname].Docs, piece)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, piece := range typePieces {
|
||||||
|
if !piece.IsInterface {
|
||||||
|
filteredTypePieces = append(filteredTypePieces, piece)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
modname := piece.ParentModule + "." + piece.Interfacing
|
||||||
|
if interfaceModules[modname] == nil {
|
||||||
|
interfaceModules[modname] = &module{
|
||||||
|
ParentModule: piece.ParentModule,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interfaceModules[modname].Types = append(interfaceModules[modname].Types, piece)
|
||||||
|
}
|
||||||
|
|
||||||
|
docs[mod] = module{
|
||||||
|
Types: filteredTypePieces,
|
||||||
|
Docs: filteredPieces,
|
||||||
|
ShortDescription: shortDesc,
|
||||||
|
Description: strings.Join(desc, "\n"),
|
||||||
|
HasInterfaces: hasInterfaces,
|
||||||
|
Properties: docPieceTag("property", tags),
|
||||||
|
Fields: docPieceTag("field", tags),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for key, mod := range interfaceModules {
|
||||||
|
docs[key] = *mod
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(len(docs) * 2)
|
||||||
|
|
||||||
for mod, v := range docs {
|
for mod, v := range docs {
|
||||||
if mod == "main" { continue }
|
docPath := "docs/api/" + mod + ".md"
|
||||||
f, _ := os.Create("docs/" + mod + ".txt")
|
if v.HasInterfaces {
|
||||||
for _, dps := range v {
|
os.Mkdir("docs/api/" + mod, 0777)
|
||||||
f.WriteString(dps.FuncSig + " > ")
|
os.Remove(docPath) // remove old doc path if it exists
|
||||||
for _, doc := range dps.Doc {
|
docPath = "docs/api/" + mod + "/_index.md"
|
||||||
if !strings.HasPrefix(doc, "---") {
|
}
|
||||||
f.WriteString(doc + "\n")
|
if v.ParentModule != "" {
|
||||||
}
|
docPath = "docs/api/" + v.ParentModule + "/" + mod + ".md"
|
||||||
}
|
|
||||||
f.WriteString("\n")
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for mod, v := range emmyDocs {
|
go func(modname, docPath string, modu module) {
|
||||||
if mod == "main" { continue }
|
defer wg.Done()
|
||||||
f, _ := os.Create("emmyLuaDocs/" + mod + ".lua")
|
modOrIface := "Module"
|
||||||
f.WriteString("--- @meta\n\nlocal " + mod + " = {}\n\n")
|
if modu.ParentModule != "" {
|
||||||
for _, em := range v {
|
modOrIface = "Module"
|
||||||
var funcdocs []string
|
}
|
||||||
for _, dps := range docs[mod] {
|
lastHeader := ""
|
||||||
if dps.FuncName == em.FuncName {
|
|
||||||
funcdocs = dps.Doc
|
f, _ := os.Create(docPath)
|
||||||
|
f.WriteString(fmt.Sprintf(header, modOrIface, modname, modu.ShortDescription))
|
||||||
|
typeTag, _ := regexp.Compile(`\B@\w+`)
|
||||||
|
modDescription := typeTag.ReplaceAllStringFunc(strings.Replace(strings.Replace(modu.Description, "<", `\<`, -1), "{{\\<", "{{<", -1), func(typ string) string {
|
||||||
|
typName := typ[1:]
|
||||||
|
typLookup := typeTable[strings.ToLower(typName)]
|
||||||
|
ifaces := typLookup[0] + "." + typLookup[1] + "/"
|
||||||
|
if typLookup[1] == "" {
|
||||||
|
ifaces = ""
|
||||||
|
}
|
||||||
|
linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s#%s", typLookup[0], ifaces, strings.ToLower(typName))
|
||||||
|
return fmt.Sprintf(`<a href="%s" style="text-decoration: none;">%s</a>`, linkedTyp, typName)
|
||||||
|
})
|
||||||
|
f.WriteString(fmt.Sprintf("## Introduction\n%s\n\n", modDescription))
|
||||||
|
if len(modu.Docs) != 0 {
|
||||||
|
funcCount := 0
|
||||||
|
for _, dps := range modu.Docs {
|
||||||
|
if dps.IsMember {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
funcCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
f.WriteString("## Functions\n")
|
||||||
|
lastHeader = "functions"
|
||||||
|
|
||||||
|
mdTable := md.NewTable(funcCount, 2)
|
||||||
|
mdTable.SetTitle(0, "")
|
||||||
|
mdTable.SetTitle(1, "")
|
||||||
|
|
||||||
|
diff := 0
|
||||||
|
for i, dps := range modu.Docs {
|
||||||
|
if dps.IsMember {
|
||||||
|
diff++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mdTable.SetContent(i - diff, 0, fmt.Sprintf(`<a href="#%s">%s</a>`, dps.FuncName, dps.FuncSig))
|
||||||
|
if len(dps.Doc) == 0 {
|
||||||
|
fmt.Printf("WARNING! Function %s on module %s has no documentation!\n", dps.FuncName, modname)
|
||||||
|
} else {
|
||||||
|
mdTable.SetContent(i - diff, 1, dps.Doc[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.WriteString(mdTable.String())
|
||||||
|
f.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(modu.Fields) != 0 {
|
||||||
|
f.WriteString("## Static module fields\n")
|
||||||
|
|
||||||
|
mdTable := md.NewTable(len(modu.Fields), 2)
|
||||||
|
mdTable.SetTitle(0, "")
|
||||||
|
mdTable.SetTitle(1, "")
|
||||||
|
|
||||||
|
|
||||||
|
for i, dps := range modu.Fields {
|
||||||
|
mdTable.SetContent(i, 0, dps.FuncName)
|
||||||
|
mdTable.SetContent(i, 1, strings.Join(dps.Doc, " "))
|
||||||
|
}
|
||||||
|
f.WriteString(mdTable.String())
|
||||||
|
f.WriteString("\n")
|
||||||
|
}
|
||||||
|
if len(modu.Properties) != 0 {
|
||||||
|
f.WriteString("## Object properties\n")
|
||||||
|
|
||||||
|
mdTable := md.NewTable(len(modu.Fields), 2)
|
||||||
|
mdTable.SetTitle(0, "")
|
||||||
|
mdTable.SetTitle(1, "")
|
||||||
|
|
||||||
|
|
||||||
|
for i, dps := range modu.Properties {
|
||||||
|
mdTable.SetContent(i, 0, dps.FuncName)
|
||||||
|
mdTable.SetContent(i, 1, strings.Join(dps.Doc, " "))
|
||||||
|
}
|
||||||
|
f.WriteString(mdTable.String())
|
||||||
|
f.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(modu.Docs) != 0 {
|
||||||
|
if lastHeader != "functions" {
|
||||||
|
f.WriteString("## Functions\n")
|
||||||
|
}
|
||||||
|
for _, dps := range modu.Docs {
|
||||||
|
if dps.IsMember {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
f.WriteString(fmt.Sprintf("<hr>\n<div id='%s'>", dps.FuncName))
|
||||||
|
htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(modname + "." + dps.FuncSig, "<", `\<`, -1), func(typ string) string {
|
||||||
|
typName := typ[1:]
|
||||||
|
typLookup := typeTable[strings.ToLower(typName)]
|
||||||
|
ifaces := typLookup[0] + "." + typLookup[1] + "/"
|
||||||
|
if typLookup[1] == "" {
|
||||||
|
ifaces = ""
|
||||||
|
}
|
||||||
|
linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s#%s", typLookup[0], ifaces, strings.ToLower(typName))
|
||||||
|
return fmt.Sprintf(`<a href="%s" style="text-decoration: none;" id="lol">%s</a>`, linkedTyp, typName)
|
||||||
|
})
|
||||||
|
f.WriteString(fmt.Sprintf(`
|
||||||
|
<h4 class='heading'>
|
||||||
|
%s
|
||||||
|
<a href="#%s" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
`, htmlSig, dps.FuncName))
|
||||||
|
for _, doc := range dps.Doc {
|
||||||
|
if !strings.HasPrefix(doc, "---") && doc != "" {
|
||||||
|
f.WriteString(doc + " \n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.WriteString("\n#### Parameters\n")
|
||||||
|
if len(dps.Params) == 0 {
|
||||||
|
f.WriteString("This function has no parameters. \n")
|
||||||
|
}
|
||||||
|
for _, p := range dps.Params {
|
||||||
|
isVariadic := false
|
||||||
|
typ := p.Type
|
||||||
|
if strings.HasPrefix(p.Type, "...") {
|
||||||
|
isVariadic = true
|
||||||
|
typ = p.Type[3:]
|
||||||
|
}
|
||||||
|
|
||||||
|
f.WriteString(fmt.Sprintf("`%s` **`%s`**", typ, p.Name))
|
||||||
|
if isVariadic {
|
||||||
|
f.WriteString(" (This type is variadic. You can pass an infinite amount of parameters with this type.)")
|
||||||
|
}
|
||||||
|
f.WriteString(" \n")
|
||||||
|
f.WriteString(strings.Join(p.Doc, " "))
|
||||||
|
f.WriteString("\n\n")
|
||||||
|
}
|
||||||
|
if codeExample := dps.Tags["example"]; codeExample != nil {
|
||||||
|
f.WriteString("#### Example\n")
|
||||||
|
f.WriteString(fmt.Sprintf("```lua\n%s\n```\n", strings.Join(codeExample[0].fields, "\n")))
|
||||||
|
}
|
||||||
|
f.WriteString("</div>")
|
||||||
|
f.WriteString("\n\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.WriteString("--- " + strings.Join(funcdocs, "\n--- ") + "\n")
|
|
||||||
if len(em.Docs) != 0 {
|
if len(modu.Types) != 0 {
|
||||||
f.WriteString(strings.Join(em.Docs, "\n") + "\n")
|
f.WriteString("## Types\n")
|
||||||
|
for _, dps := range modu.Types {
|
||||||
|
f.WriteString("<hr>\n\n")
|
||||||
|
f.WriteString(fmt.Sprintf("## %s\n", dps.FuncName))
|
||||||
|
for _, doc := range dps.Doc {
|
||||||
|
if !strings.HasPrefix(doc, "---") {
|
||||||
|
f.WriteString(doc + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(dps.Properties) != 0 {
|
||||||
|
f.WriteString("## Object properties\n")
|
||||||
|
|
||||||
|
mdTable := md.NewTable(len(dps.Properties), 2)
|
||||||
|
mdTable.SetTitle(0, "")
|
||||||
|
mdTable.SetTitle(1, "")
|
||||||
|
|
||||||
|
for i, d := range dps.Properties {
|
||||||
|
mdTable.SetContent(i, 0, d.FuncName)
|
||||||
|
mdTable.SetContent(i, 1, strings.Join(d.Doc, " "))
|
||||||
|
}
|
||||||
|
f.WriteString(mdTable.String())
|
||||||
|
f.WriteString("\n")
|
||||||
|
}
|
||||||
|
f.WriteString("\n")
|
||||||
|
f.WriteString("### Methods\n")
|
||||||
|
for _, dps := range modu.Docs {
|
||||||
|
if !dps.IsMember {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(dps.FuncSig, "<", `\<`, -1), func(typ string) string {
|
||||||
|
typName := regexp.MustCompile(`\w+`).FindString(typ[1:])
|
||||||
|
typLookup := typeTable[strings.ToLower(typName)]
|
||||||
|
fmt.Printf("%+q, \n", typLookup)
|
||||||
|
linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s/#%s", typLookup[0], typLookup[0] + "." + typLookup[1], strings.ToLower(typName))
|
||||||
|
return fmt.Sprintf(`<a href="#%s" style="text-decoration: none;">%s</a>`, linkedTyp, typName)
|
||||||
|
})
|
||||||
|
f.WriteString(fmt.Sprintf("#### %s\n", htmlSig))
|
||||||
|
for _, doc := range dps.Doc {
|
||||||
|
if !strings.HasPrefix(doc, "---") {
|
||||||
|
f.WriteString(doc + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.WriteString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
f.WriteString("function " + mod + "." + em.FuncName + "(" + strings.Join(em.Params, ", ") + ") end\n\n")
|
}(mod, docPath, v)
|
||||||
}
|
|
||||||
f.WriteString("return " + mod + "\n")
|
go func(md, modname string, modu module) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
if modu.ParentModule != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ff, _ := os.Create("emmyLuaDocs/" + modname + ".lua")
|
||||||
|
ff.WriteString("--- @meta\n\nlocal " + modname + " = {}\n\n")
|
||||||
|
for _, em := range emmyDocs[modname] {
|
||||||
|
if strings.HasSuffix(em.DocPiece.GoFuncName, strings.ToLower("loader")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dps := em.DocPiece
|
||||||
|
funcdocs := dps.Doc
|
||||||
|
ff.WriteString("--- " + strings.Join(funcdocs, "\n--- ") + "\n")
|
||||||
|
if len(em.Annotations) != 0 {
|
||||||
|
ff.WriteString(strings.Join(em.Annotations, "\n") + "\n")
|
||||||
|
}
|
||||||
|
accessor := "."
|
||||||
|
if dps.IsMember {
|
||||||
|
accessor = ":"
|
||||||
|
}
|
||||||
|
signature := strings.Split(dps.FuncSig, " ->")[0]
|
||||||
|
var intrface string
|
||||||
|
if dps.IsInterface {
|
||||||
|
intrface = "." + dps.Interfacing
|
||||||
|
}
|
||||||
|
ff.WriteString("function " + modname + intrface + accessor + signature + " end\n\n")
|
||||||
|
}
|
||||||
|
ff.WriteString("return " + modname + "\n")
|
||||||
|
}(mod, mod, v)
|
||||||
}
|
}
|
||||||
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
local fs = require 'fs'
|
||||||
|
local emmyPattern = '^%-%-%- (.+)'
|
||||||
|
local modpattern = '^%-+ @module (%w+)'
|
||||||
|
local pieces = {}
|
||||||
|
|
||||||
|
local files = fs.readdir 'nature'
|
||||||
|
for _, fname in ipairs(files) do
|
||||||
|
local isScript = fname:match'%.lua$'
|
||||||
|
if not isScript then goto continue end
|
||||||
|
|
||||||
|
local f = io.open(string.format('nature/%s', fname))
|
||||||
|
local header = f:read '*l'
|
||||||
|
local mod = header:match(modpattern)
|
||||||
|
if not mod then goto continue end
|
||||||
|
|
||||||
|
print(fname, mod)
|
||||||
|
pieces[mod] = {}
|
||||||
|
|
||||||
|
local docPiece = {}
|
||||||
|
local lines = {}
|
||||||
|
local lineno = 0
|
||||||
|
for line in f:lines() do
|
||||||
|
lineno = lineno + 1
|
||||||
|
lines[lineno] = line
|
||||||
|
|
||||||
|
if line == header then goto continue2 end
|
||||||
|
if not line:match(emmyPattern) then
|
||||||
|
if line:match '^function' then
|
||||||
|
local pattern = (string.format('^function %s%%.', mod) .. '(%w+)')
|
||||||
|
local funcName = line:match(pattern)
|
||||||
|
if not funcName then goto continue2 end
|
||||||
|
|
||||||
|
local dps = {
|
||||||
|
description = {},
|
||||||
|
params = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
local offset = 1
|
||||||
|
while true do
|
||||||
|
local prev = lines[lineno - offset]
|
||||||
|
|
||||||
|
local docline = prev:match '^%-+ (.+)'
|
||||||
|
if docline then
|
||||||
|
local emmy = docline:match '@(%w+)'
|
||||||
|
local cut = 0
|
||||||
|
|
||||||
|
if emmy then cut = emmy:len() + 3 end
|
||||||
|
local emmythings = string.split(docline:sub(cut), ' ')
|
||||||
|
|
||||||
|
if emmy then
|
||||||
|
if emmy == 'param' then
|
||||||
|
table.insert(dps.params, 1, {
|
||||||
|
name = emmythings[1],
|
||||||
|
type = emmythings[2]
|
||||||
|
})
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(dps.description, 1, docline)
|
||||||
|
end
|
||||||
|
offset = offset + 1
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
pieces[mod][funcName] = dps
|
||||||
|
end
|
||||||
|
docPiece = {}
|
||||||
|
goto continue2
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(docPiece, line)
|
||||||
|
::continue2::
|
||||||
|
end
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
|
||||||
|
local header = [[---
|
||||||
|
title: %s %s
|
||||||
|
description: %s
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "Nature"
|
||||||
|
---
|
||||||
|
|
||||||
|
]]
|
||||||
|
|
||||||
|
for iface, dps in pairs(pieces) do
|
||||||
|
local mod = iface:match '(%w+)%.' or 'nature'
|
||||||
|
local path = string.format('docs/%s/%s.md', mod, iface)
|
||||||
|
fs.mkdir(fs.dir(path), true)
|
||||||
|
local f <close> = io.open(path, 'w')
|
||||||
|
f:write(string.format(header, 'Module', iface, 'No description.'))
|
||||||
|
print(f)
|
||||||
|
|
||||||
|
print(mod, path)
|
||||||
|
|
||||||
|
for func, docs in pairs(dps) do
|
||||||
|
f:write(string.format('<hr>\n<div id=\'%s\'>', func))
|
||||||
|
local sig = string.format('%s.%s(', iface, func)
|
||||||
|
for idx, param in ipairs(docs.params) do
|
||||||
|
sig = sig .. ((param.name:gsub('%?$', '')))
|
||||||
|
if idx ~= #docs.params then sig = sig .. ', ' end
|
||||||
|
end
|
||||||
|
sig = sig .. ')'
|
||||||
|
f:write(string.format([[
|
||||||
|
<h4 class='heading'>
|
||||||
|
%s
|
||||||
|
<a href="#%s" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
]], sig, func))
|
||||||
|
|
||||||
|
f:write(table.concat(docs.description, '\n') .. '\n')
|
||||||
|
f:write '#### Parameters\n'
|
||||||
|
if #docs.params == 0 then
|
||||||
|
f:write 'This function has no parameters. \n'
|
||||||
|
end
|
||||||
|
for _, param in ipairs(docs.params) do
|
||||||
|
f:write(string.format('`%s` **`%s`**\n', param.name:gsub('%?$', ''), param.type))
|
||||||
|
end
|
||||||
|
--[[
|
||||||
|
local params = table.filter(docs, function(t)
|
||||||
|
return t:match '^%-%-%- @param'
|
||||||
|
end)
|
||||||
|
for i, str in ipairs(params) do
|
||||||
|
if i ~= 1 then
|
||||||
|
f:write ', '
|
||||||
|
end
|
||||||
|
f:write(str:match '^%-%-%- @param ([%w]+) ')
|
||||||
|
end
|
||||||
|
f:write(')\n')
|
||||||
|
|
||||||
|
for _, str in ipairs(docs) do
|
||||||
|
if not str:match '^%-%-%- @' then
|
||||||
|
f:write(str:match '^%-%-%- (.+)' .. '\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
]]--
|
||||||
|
f:write('</div>')
|
||||||
|
f:write('\n\n')
|
||||||
|
end
|
||||||
|
end
|
211
complete.go
211
complete.go
|
@ -11,15 +11,49 @@ import (
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func splitQuote(str string) []string {
|
var charEscapeMap = []string{
|
||||||
|
"\"", "\\\"",
|
||||||
|
"'", "\\'",
|
||||||
|
"`", "\\`",
|
||||||
|
" ", "\\ ",
|
||||||
|
"(", "\\(",
|
||||||
|
")", "\\)",
|
||||||
|
"[", "\\[",
|
||||||
|
"]", "\\]",
|
||||||
|
"$", "\\$",
|
||||||
|
"&", "\\&",
|
||||||
|
"*", "\\*",
|
||||||
|
">", "\\>",
|
||||||
|
"<", "\\<",
|
||||||
|
"|", "\\|",
|
||||||
|
}
|
||||||
|
var charEscapeMapInvert = invert(charEscapeMap)
|
||||||
|
var escapeReplaer = strings.NewReplacer(charEscapeMap...)
|
||||||
|
var escapeInvertReplaer = strings.NewReplacer(charEscapeMapInvert...)
|
||||||
|
|
||||||
|
func invert(m []string) []string {
|
||||||
|
newM := make([]string, len(charEscapeMap))
|
||||||
|
for i := range m {
|
||||||
|
if (i + 1) % 2 == 0 {
|
||||||
|
newM[i] = m[i - 1]
|
||||||
|
newM[i - 1] = m[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newM
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitForFile(str string) []string {
|
||||||
split := []string{}
|
split := []string{}
|
||||||
sb := &strings.Builder{}
|
sb := &strings.Builder{}
|
||||||
quoted := false
|
quoted := false
|
||||||
|
|
||||||
for _, r := range str {
|
for i, r := range str {
|
||||||
if r == '"' {
|
if r == '"' {
|
||||||
quoted = !quoted
|
quoted = !quoted
|
||||||
sb.WriteRune(r)
|
sb.WriteRune(r)
|
||||||
|
} else if r == ' ' && str[i - 1] == '\\' {
|
||||||
|
sb.WriteRune(r)
|
||||||
} else if !quoted && r == ' ' {
|
} else if !quoted && r == ' ' {
|
||||||
split = append(split, sb.String())
|
split = append(split, sb.String())
|
||||||
sb.Reset()
|
sb.Reset()
|
||||||
|
@ -39,12 +73,22 @@ func splitQuote(str string) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileComplete(query, ctx string, fields []string) ([]string, string) {
|
func fileComplete(query, ctx string, fields []string) ([]string, string) {
|
||||||
q := splitQuote(ctx)
|
q := splitForFile(ctx)
|
||||||
|
path := ""
|
||||||
|
if len(q) != 0 {
|
||||||
|
path = q[len(q) - 1]
|
||||||
|
}
|
||||||
|
|
||||||
return matchPath(q[len(q) - 1])
|
return matchPath(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
||||||
|
q := splitForFile(ctx)
|
||||||
|
query = ""
|
||||||
|
if len(q) != 0 {
|
||||||
|
query = q[len(q) - 1]
|
||||||
|
}
|
||||||
|
|
||||||
var completions []string
|
var completions []string
|
||||||
|
|
||||||
prefixes := []string{"./", "../", "/", "~/"}
|
prefixes := []string{"./", "../", "/", "~/"}
|
||||||
|
@ -54,7 +98,7 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
||||||
if len(fileCompletions) != 0 {
|
if len(fileCompletions) != 0 {
|
||||||
for _, f := range fileCompletions {
|
for _, f := range fileCompletions {
|
||||||
fullPath, _ := filepath.Abs(util.ExpandHome(query + strings.TrimPrefix(f, filePref)))
|
fullPath, _ := filepath.Abs(util.ExpandHome(query + strings.TrimPrefix(f, filePref)))
|
||||||
if err := findExecutable(fullPath, false, true); err != nil {
|
if err := findExecutable(escapeInvertReplaer.Replace(fullPath), false, true); err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
completions = append(completions, f)
|
completions = append(completions, f)
|
||||||
|
@ -66,7 +110,6 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
||||||
|
|
||||||
// filter out executables, but in path
|
// filter out executables, but in path
|
||||||
for _, dir := range filepath.SplitList(os.Getenv("PATH")) {
|
for _, dir := range filepath.SplitList(os.Getenv("PATH")) {
|
||||||
// print dir to stderr for debugging
|
|
||||||
// search for an executable which matches our query string
|
// search for an executable which matches our query string
|
||||||
if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil {
|
if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil {
|
||||||
// get basename from matches
|
// get basename from matches
|
||||||
|
@ -85,7 +128,7 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add lua registered commands to completions
|
// add lua registered commands to completions
|
||||||
for cmdName := range commands {
|
for cmdName := range cmds.Commands {
|
||||||
if strings.HasPrefix(cmdName, query) {
|
if strings.HasPrefix(cmdName, query) {
|
||||||
completions = append(completions, cmdName)
|
completions = append(completions, cmdName)
|
||||||
}
|
}
|
||||||
|
@ -102,6 +145,7 @@ func matchPath(query string) ([]string, string) {
|
||||||
var entries []string
|
var entries []string
|
||||||
var baseName string
|
var baseName string
|
||||||
|
|
||||||
|
query = escapeInvertReplaer.Replace(query)
|
||||||
path, _ := filepath.Abs(util.ExpandHome(filepath.Dir(query)))
|
path, _ := filepath.Abs(util.ExpandHome(filepath.Dir(query)))
|
||||||
if string(query) == "" {
|
if string(query) == "" {
|
||||||
// filepath base below would give us "."
|
// filepath base below would give us "."
|
||||||
|
@ -112,7 +156,16 @@ func matchPath(query string) ([]string, string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
files, _ := os.ReadDir(path)
|
files, _ := os.ReadDir(path)
|
||||||
for _, file := range files {
|
for _, entry := range files {
|
||||||
|
// should we handle errors here?
|
||||||
|
file, err := entry.Info()
|
||||||
|
if err == nil && file.Mode() & os.ModeSymlink != 0 {
|
||||||
|
path, err := filepath.EvalSymlinks(filepath.Join(path, file.Name()))
|
||||||
|
if err == nil {
|
||||||
|
file, err = os.Lstat(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(file.Name(), baseName) {
|
if strings.HasPrefix(file.Name(), baseName) {
|
||||||
entry := file.Name()
|
entry := file.Name()
|
||||||
if file.IsDir() {
|
if file.IsDir() {
|
||||||
|
@ -124,38 +177,26 @@ func matchPath(query string) ([]string, string) {
|
||||||
entries = append(entries, entry)
|
entries = append(entries, entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !strings.HasPrefix(oldQuery, "\"") {
|
||||||
|
baseName = escapeFilename(baseName)
|
||||||
|
}
|
||||||
|
|
||||||
return entries, baseName
|
return entries, baseName
|
||||||
}
|
}
|
||||||
|
|
||||||
func escapeFilename(fname string) string {
|
func escapeFilename(fname string) string {
|
||||||
args := []string{
|
return escapeReplaer.Replace(fname)
|
||||||
"\"", "\\\"",
|
|
||||||
"'", "\\'",
|
|
||||||
"`", "\\`",
|
|
||||||
" ", "\\ ",
|
|
||||||
"(", "\\(",
|
|
||||||
")", "\\)",
|
|
||||||
"[", "\\[",
|
|
||||||
"]", "\\]",
|
|
||||||
"$", "\\$",
|
|
||||||
"&", "\\&",
|
|
||||||
"*", "\\*",
|
|
||||||
">", "\\>",
|
|
||||||
"<", "\\<",
|
|
||||||
"|", "\\|",
|
|
||||||
}
|
|
||||||
|
|
||||||
r := strings.NewReplacer(args...)
|
|
||||||
return r.Replace(fname)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface completion
|
||||||
|
// tab completions
|
||||||
|
// The completions interface deals with tab completions.
|
||||||
func completionLoader(rtm *rt.Runtime) *rt.Table {
|
func completionLoader(rtm *rt.Runtime) *rt.Table {
|
||||||
exports := map[string]util.LuaExport{
|
exports := map[string]util.LuaExport{
|
||||||
"files": {luaFileComplete, 3, false},
|
"bins": {hcmpBins, 3, false},
|
||||||
"bins": {luaBinaryComplete, 3, false},
|
"call": {hcmpCall, 4, false},
|
||||||
"call": {callLuaCompleter, 4, false},
|
"files": {hcmpFiles, 3, false},
|
||||||
"handler": {completionHandler, 2, false},
|
"handler": {hcmpHandler, 2, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
mod := rt.NewTable()
|
mod := rt.NewTable()
|
||||||
|
@ -164,12 +205,58 @@ func completionLoader(rtm *rt.Runtime) *rt.Table {
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
|
|
||||||
// left as a shim, might doc in the same way as hilbish functions
|
// #interface completion
|
||||||
func completionHandler(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
// bins(query, ctx, fields) -> entries (table), prefix (string)
|
||||||
return c.Next(), nil
|
// Return binaries/executables based on the provided parameters.
|
||||||
|
// This function is meant to be used as a helper in a command completion handler.
|
||||||
|
// #param query string
|
||||||
|
// #param ctx string
|
||||||
|
// #param fields table
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
-- an extremely simple completer for sudo.
|
||||||
|
hilbish.complete('command.sudo', function(query, ctx, fields)
|
||||||
|
table.remove(fields, 1)
|
||||||
|
if #fields[1] then
|
||||||
|
-- return commands because sudo runs a command as root..!
|
||||||
|
|
||||||
|
local entries, pfx = hilbish.completion.bins(query, ctx, fields)
|
||||||
|
return {
|
||||||
|
type = 'grid',
|
||||||
|
items = entries
|
||||||
|
}, pfx
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ... else suggest files or anything else ..
|
||||||
|
end)
|
||||||
|
#example
|
||||||
|
*/
|
||||||
|
func hcmpBins(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
query, ctx, fds, err := getCompleteParams(t, c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
completions, pfx := binaryComplete(query, ctx, fds)
|
||||||
|
luaComps := rt.NewTable()
|
||||||
|
|
||||||
|
for i, comp := range completions {
|
||||||
|
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
// #interface completion
|
||||||
|
// call(name, query, ctx, fields) -> completionGroups (table), prefix (string)
|
||||||
|
// Calls a completer function. This is mainly used to call a command completer, which will have a `name`
|
||||||
|
// in the form of `command.name`, example: `command.git`.
|
||||||
|
// You can check the Completions doc or `doc completions` for info on the `completionGroups` return value.
|
||||||
|
// #param name string
|
||||||
|
// #param query string
|
||||||
|
// #param ctx string
|
||||||
|
// #param fields table
|
||||||
|
func hcmpCall(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.CheckNArgs(4); err != nil {
|
if err := c.CheckNArgs(4); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -197,18 +284,26 @@ func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// we must keep the holy 80 cols
|
// we must keep the holy 80 cols
|
||||||
completerReturn, err := rt.Call1(l.MainThread(),
|
cont := c.Next()
|
||||||
rt.FunctionValue(completecb), rt.StringValue(query),
|
err = rt.Call(l.MainThread(), rt.FunctionValue(completecb),
|
||||||
rt.StringValue(ctx), rt.TableValue(fields))
|
[]rt.Value{rt.StringValue(query), rt.StringValue(ctx), rt.TableValue(fields)},
|
||||||
|
cont)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, completerReturn), nil
|
return cont, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
// #interface completion
|
||||||
|
// files(query, ctx, fields) -> entries (table), prefix (string)
|
||||||
|
// Returns file matches based on the provided parameters.
|
||||||
|
// This function is meant to be used as a helper in a command completion handler.
|
||||||
|
// #param query string
|
||||||
|
// #param ctx string
|
||||||
|
// #param fields table
|
||||||
|
func hcmpFiles(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
query, ctx, fds, err := getCompleteParams(t, c)
|
query, ctx, fds, err := getCompleteParams(t, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -224,22 +319,32 @@ func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil
|
return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
// #interface completion
|
||||||
query, ctx, fds, err := getCompleteParams(t, c)
|
// handler(line, pos)
|
||||||
if err != nil {
|
// This function contains the general completion handler for Hilbish. This function handles
|
||||||
return nil, err
|
// completion of everything, which includes calling other command handlers, binaries, and files.
|
||||||
}
|
// This function can be overriden to supply a custom handler. Note that alias resolution is required to be done in this function.
|
||||||
|
// #param line string The current Hilbish command line
|
||||||
|
// #param pos number Numerical position of the cursor
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
-- stripped down version of the default implementation
|
||||||
|
function hilbish.completion.handler(line, pos)
|
||||||
|
local query = fields[#fields]
|
||||||
|
|
||||||
completions, pfx := binaryComplete(query, ctx, fds)
|
if #fields == 1 then
|
||||||
luaComps := rt.NewTable()
|
-- call bins handler here
|
||||||
|
else
|
||||||
for i, comp := range completions {
|
-- call command completer or files completer here
|
||||||
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
|
end
|
||||||
}
|
end
|
||||||
|
#example
|
||||||
return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil
|
*/
|
||||||
|
func hcmpHandler(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, error) {
|
func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, error) {
|
||||||
if err := c.CheckNArgs(3); err != nil {
|
if err := c.CheckNArgs(3); err != nil {
|
||||||
return "", "", []string{}, err
|
return "", "", []string{}, err
|
||||||
|
|
|
@ -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.
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
title: API
|
||||||
|
layout: doc
|
||||||
|
weight: -100
|
||||||
|
menu: docs
|
||||||
|
---
|
||||||
|
|
||||||
|
Welcome to the API documentation for Hilbish. This documents Lua functions
|
||||||
|
provided by Hilbish.
|
|
@ -0,0 +1,167 @@
|
||||||
|
---
|
||||||
|
title: Module bait
|
||||||
|
description: the event emitter
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "API"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Bait is the event emitter for Hilbish. Much like Node.js and
|
||||||
|
its `events` system, many actions in Hilbish emit events.
|
||||||
|
Unlike Node.js, Hilbish events are global. So make sure to
|
||||||
|
pick a unique name!
|
||||||
|
|
||||||
|
Usage of the Bait module consists of userstanding
|
||||||
|
event-driven architecture, but it's pretty simple:
|
||||||
|
If you want to act on a certain event, you can `catch` it.
|
||||||
|
You can act on events via callback functions.
|
||||||
|
|
||||||
|
Examples of this are in the Hilbish default config!
|
||||||
|
Consider this part of it:
|
||||||
|
```lua
|
||||||
|
bait.catch('command.exit', function(code)
|
||||||
|
running = false
|
||||||
|
doPrompt(code ~= 0)
|
||||||
|
doNotifyPrompt()
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
What this does is, whenever the `command.exit` event is thrown,
|
||||||
|
this function will set the user prompt.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|<a href="#catch">catch(name, cb)</a>|Catches an event. This function can be used to act on events.|
|
||||||
|
|<a href="#catchOnce">catchOnce(name, cb)</a>|Catches an event, but only once. This will remove the hook immediately after it runs for the first time.|
|
||||||
|
|<a href="#hooks">hooks(name) -> table</a>|Returns a table of functions that are hooked on an event with the corresponding `name`.|
|
||||||
|
|<a href="#release">release(name, catcher)</a>|Removes the `catcher` for the event with `name`.|
|
||||||
|
|<a href="#throw">throw(name, ...args)</a>|Throws a hook with `name` with the provided `args`.|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='catch'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
bait.catch(name, cb)
|
||||||
|
<a href="#catch" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Catches an event. This function can be used to act on events.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`name`**
|
||||||
|
The name of the hook.
|
||||||
|
|
||||||
|
`function` **`cb`**
|
||||||
|
The function that will be called when the hook is thrown.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
bait.catch('hilbish.exit', function()
|
||||||
|
print 'Goodbye Hilbish!'
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='catchOnce'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
bait.catchOnce(name, cb)
|
||||||
|
<a href="#catchOnce" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Catches an event, but only once. This will remove the hook immediately after it runs for the first time.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`name`**
|
||||||
|
The name of the event
|
||||||
|
|
||||||
|
`function` **`cb`**
|
||||||
|
The function that will be called when the event is thrown.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='hooks'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
bait.hooks(name) -> table
|
||||||
|
<a href="#hooks" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns a table of functions that are hooked on an event with the corresponding `name`.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`name`**
|
||||||
|
The name of the hook
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='release'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
bait.release(name, catcher)
|
||||||
|
<a href="#release" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Removes the `catcher` for the event with `name`.
|
||||||
|
For this to work, `catcher` has to be the same function used to catch
|
||||||
|
an event, like one saved to a variable.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`name`**
|
||||||
|
Name of the event the hook is on
|
||||||
|
|
||||||
|
`function` **`catcher`**
|
||||||
|
Hook function to remove
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
local hookCallback = function() print 'hi' end
|
||||||
|
|
||||||
|
bait.catch('event', hookCallback)
|
||||||
|
|
||||||
|
-- a little while later....
|
||||||
|
bait.release('event', hookCallback)
|
||||||
|
-- and now hookCallback will no longer be ran for the event.
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='throw'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
bait.throw(name, ...args)
|
||||||
|
<a href="#throw" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Throws a hook with `name` with the provided `args`.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`name`**
|
||||||
|
The name of the hook.
|
||||||
|
|
||||||
|
`any` **`args`** (This type is variadic. You can pass an infinite amount of parameters with this type.)
|
||||||
|
The arguments to pass to the hook.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
bait.throw('greeting', 'world')
|
||||||
|
|
||||||
|
-- This can then be listened to via
|
||||||
|
bait.catch('gretting', function(greetTo)
|
||||||
|
print('Hello ' .. greetTo)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
---
|
||||||
|
title: Module commander
|
||||||
|
description: library for custom commands
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "API"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Commander is the library which handles Hilbish commands. This makes
|
||||||
|
the user able to add Lua-written commands to their shell without making
|
||||||
|
a separate script in a bin folder. Instead, you may simply use the Commander
|
||||||
|
library in your Hilbish config.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local commander = require 'commander'
|
||||||
|
|
||||||
|
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: `input`, `out`, and `err`.
|
||||||
|
There is an `in` alias to `input`, but it requires using the string accessor syntax (`sinks['in']`)
|
||||||
|
as `in` is also a Lua keyword, so `input` is preferred for use.
|
||||||
|
All of them are a <a href="/Hilbish/docs/api/hilbish/#sink" style="text-decoration: none;">Sink</a>.
|
||||||
|
In the future, `sinks.in` will be removed.
|
||||||
|
|
||||||
|
- `in` is the standard input.
|
||||||
|
You may use the read functions on this sink to get input from the user.
|
||||||
|
- `out` is standard output.
|
||||||
|
This is usually where command output should go.
|
||||||
|
- `err` is standard error.
|
||||||
|
This sink is for writing errors, as the name would suggest.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|<a href="#deregister">deregister(name)</a>|Removes the named command. Note that this will only remove Commander-registered commands.|
|
||||||
|
|<a href="#register">register(name, cb)</a>|Adds a new command with the given `name`. When Hilbish has to run a command with a name,|
|
||||||
|
|<a href="#registry">registry() -> table</a>|Returns all registered commanders. Returns a list of tables with the following keys:|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='deregister'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
commander.deregister(name)
|
||||||
|
<a href="#deregister" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Removes the named command. Note that this will only remove Commander-registered commands.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`name`**
|
||||||
|
Name of the command to remove.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='register'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
commander.register(name, cb)
|
||||||
|
<a href="#register" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Adds a new command with the given `name`. When Hilbish has to run a command with a name,
|
||||||
|
it will run the function providing the arguments and sinks.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`name`**
|
||||||
|
Name of the command
|
||||||
|
|
||||||
|
`function` **`cb`**
|
||||||
|
Callback to handle command invocation
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
-- When you run the command `hello` in the shell, it will print `Hello world`.
|
||||||
|
-- If you run it with, for example, `hello Hilbish`, it will print 'Hello Hilbish'
|
||||||
|
commander.register('hello', function(args, sinks)
|
||||||
|
local name = 'world'
|
||||||
|
if #args > 0 then name = args[1] end
|
||||||
|
|
||||||
|
sinks.out:writeln('Hello ' .. name)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='registry'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
commander.registry() -> table
|
||||||
|
<a href="#registry" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns all registered commanders. Returns a list of tables with the following keys:
|
||||||
|
- `exec`: The function used to run the commander. Commanders require args and sinks to be passed.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,257 @@
|
||||||
|
---
|
||||||
|
title: Module fs
|
||||||
|
description: filesystem interaction and functionality library
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "API"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
The fs module provides filesystem functions to Hilbish. While Lua's standard
|
||||||
|
library has some I/O functions, they're missing a lot of the basics. The `fs`
|
||||||
|
library offers more functions and will work on any operating system Hilbish does.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|<a href="#abs">abs(path) -> string</a>|Returns an absolute version of the `path`.|
|
||||||
|
|<a href="#basename">basename(path) -> string</a>|Returns the "basename," or the last part of the provided `path`. If path is empty,|
|
||||||
|
|<a href="#cd">cd(dir)</a>|Changes Hilbish's directory to `dir`.|
|
||||||
|
|<a href="#dir">dir(path) -> string</a>|Returns the directory part of `path`. If a file path like|
|
||||||
|
|<a href="#glob">glob(pattern) -> matches (table)</a>|Match all files based on the provided `pattern`.|
|
||||||
|
|<a href="#join">join(...path) -> string</a>|Takes any list of paths and joins them based on the operating system's path separator.|
|
||||||
|
|<a href="#mkdir">mkdir(name, recursive)</a>|Creates a new directory with the provided `name`.|
|
||||||
|
|<a href="#pipe">fpipe() -> File, File</a>|Returns a pair of connected files, also known as a pipe.|
|
||||||
|
|<a href="#readdir">readdir(path) -> table[string]</a>|Returns a list of all files and directories in the provided path.|
|
||||||
|
|<a href="#stat">stat(path) -> {}</a>|Returns the information about a given `path`.|
|
||||||
|
|
||||||
|
## Static module fields
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|pathSep|The operating system's path separator.|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='abs'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
fs.abs(path) -> string
|
||||||
|
<a href="#abs" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns an absolute version of the `path`.
|
||||||
|
This can be used to resolve short paths like `..` to `/home/user`.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`path`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='basename'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
fs.basename(path) -> string
|
||||||
|
<a href="#basename" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns the "basename," or the last part of the provided `path`. If path is empty,
|
||||||
|
`.` will be returned.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`path`**
|
||||||
|
Path to get the base name of.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='cd'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
fs.cd(dir)
|
||||||
|
<a href="#cd" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Changes Hilbish's directory to `dir`.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`dir`**
|
||||||
|
Path to change directory to.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='dir'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
fs.dir(path) -> string
|
||||||
|
<a href="#dir" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns the directory part of `path`. If a file path like
|
||||||
|
`~/Documents/doc.txt` then this function will return `~/Documents`.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`path`**
|
||||||
|
Path to get the directory for.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='glob'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
fs.glob(pattern) -> matches (table)
|
||||||
|
<a href="#glob" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Match all files based on the provided `pattern`.
|
||||||
|
For the syntax' refer to Go's filepath.Match function: https://pkg.go.dev/path/filepath#Match
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`pattern`**
|
||||||
|
Pattern to compare files with.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
--[[
|
||||||
|
Within a folder that contains the following files:
|
||||||
|
a.txt
|
||||||
|
init.lua
|
||||||
|
code.lua
|
||||||
|
doc.pdf
|
||||||
|
]]--
|
||||||
|
local matches = fs.glob './*.lua'
|
||||||
|
print(matches)
|
||||||
|
-- -> {'init.lua', 'code.lua'}
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='join'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
fs.join(...path) -> string
|
||||||
|
<a href="#join" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Takes any list of paths and joins them based on the operating system's path separator.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`path`** (This type is variadic. You can pass an infinite amount of parameters with this type.)
|
||||||
|
Paths to join together
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
-- This prints the directory for Hilbish's config!
|
||||||
|
print(fs.join(hilbish.userDir.config, 'hilbish'))
|
||||||
|
-- -> '/home/user/.config/hilbish' on Linux
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='mkdir'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
fs.mkdir(name, recursive)
|
||||||
|
<a href="#mkdir" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Creates a new directory with the provided `name`.
|
||||||
|
With `recursive`, mkdir will create parent directories.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`name`**
|
||||||
|
Name of the directory
|
||||||
|
|
||||||
|
`boolean` **`recursive`**
|
||||||
|
Whether to create parent directories for the provided name
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
-- This will create the directory foo, then create the directory bar in the
|
||||||
|
-- foo directory. If recursive is false in this case, it will fail.
|
||||||
|
fs.mkdir('./foo/bar', true)
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='pipe'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
fs.fpipe() -> File, File
|
||||||
|
<a href="#pipe" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns a pair of connected files, also known as a pipe.
|
||||||
|
The type returned is a Lua file, same as returned from `io` functions.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='readdir'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
fs.readdir(path) -> table[string]
|
||||||
|
<a href="#readdir" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns a list of all files and directories in the provided path.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`dir`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='stat'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
fs.stat(path) -> {}
|
||||||
|
<a href="#stat" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns the information about a given `path`.
|
||||||
|
The returned table contains the following values:
|
||||||
|
name (string) - Name of the path
|
||||||
|
size (number) - Size of the path in bytes
|
||||||
|
mode (string) - Unix permission mode in an octal format string (with leading 0)
|
||||||
|
isDir (boolean) - If the path is a directory
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`path`**
|
||||||
|
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
local inspect = require 'inspect'
|
||||||
|
|
||||||
|
local stat = fs.stat '~'
|
||||||
|
print(inspect(stat))
|
||||||
|
--[[
|
||||||
|
Would print the following:
|
||||||
|
{
|
||||||
|
isDir = true,
|
||||||
|
mode = "0755",
|
||||||
|
name = "username",
|
||||||
|
size = 12288
|
||||||
|
}
|
||||||
|
]]--
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,544 @@
|
||||||
|
---
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|<a href="#alias">alias(cmd, orig)</a>|Sets an alias, with a name of `cmd` to another command.|
|
||||||
|
|<a href="#appendPath">appendPath(dir)</a>|Appends the provided dir to the command path (`$PATH`)|
|
||||||
|
|<a href="#complete">complete(scope, cb)</a>|Registers a completion handler for the specified scope.|
|
||||||
|
|<a href="#cwd">cwd() -> string</a>|Returns the current directory of the shell.|
|
||||||
|
|<a href="#exec">exec(cmd)</a>|Replaces the currently running Hilbish instance with the supplied command.|
|
||||||
|
|<a href="#goro">goro(fn)</a>|Puts `fn` in a Goroutine.|
|
||||||
|
|<a href="#highlighter">highlighter(line)</a>|Line highlighter handler.|
|
||||||
|
|<a href="#hinter">hinter(line, pos)</a>|The command line hint handler. It gets called on every key insert to|
|
||||||
|
|<a href="#inputMode">inputMode(mode)</a>|Sets the input mode for Hilbish's line reader.|
|
||||||
|
|<a href="#interval">interval(cb, time) -> @Timer</a>|Runs the `cb` function every specified amount of `time`.|
|
||||||
|
|<a href="#multiprompt">multiprompt(str)</a>|Changes the text prompt when Hilbish asks for more input.|
|
||||||
|
|<a href="#prependPath">prependPath(dir)</a>|Prepends `dir` to $PATH.|
|
||||||
|
|<a href="#prompt">prompt(str, typ)</a>|Changes the shell prompt to the provided string.|
|
||||||
|
|<a href="#read">read(prompt) -> input (string)</a>|Read input from the user, using Hilbish's line editor/input reader.|
|
||||||
|
|<a href="#run">run(cmd, streams) -> exitCode (number), stdout (string), stderr (string)</a>|Runs `cmd` in Hilbish's shell script interpreter.|
|
||||||
|
|<a href="#runnerMode">runnerMode(mode)</a>|Sets the execution/runner mode for interactive Hilbish.|
|
||||||
|
|<a href="#timeout">timeout(cb, time) -> @Timer</a>|Executed the `cb` function after a period of `time`.|
|
||||||
|
|<a href="#which">which(name) -> string</a>|Checks if `name` is a valid command.|
|
||||||
|
|
||||||
|
## Static module fields
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|ver|The version of Hilbish|
|
||||||
|
|goVersion|The version of Go that Hilbish was compiled with|
|
||||||
|
|user|Username of the user|
|
||||||
|
|host|Hostname of the machine|
|
||||||
|
|dataDir|Directory for Hilbish data files, including the docs and default modules|
|
||||||
|
|interactive|Is Hilbish in an interactive shell?|
|
||||||
|
|login|Is Hilbish the login shell?|
|
||||||
|
|vimMode|Current Vim input mode of Hilbish (will be nil if not in Vim input mode)|
|
||||||
|
|exitCode|Exit code of the last executed command|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='alias'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.alias(cmd, orig)
|
||||||
|
<a href="#alias" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Sets an alias, with a name of `cmd` to another command.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`cmd`**
|
||||||
|
Name of the alias
|
||||||
|
|
||||||
|
`string` **`orig`**
|
||||||
|
Command that will be aliased
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
-- With this, "ga file" will turn into "git add file"
|
||||||
|
hilbish.alias('ga', 'git add')
|
||||||
|
|
||||||
|
-- Numbered substitutions are supported here!
|
||||||
|
hilbish.alias('dircount', 'ls %1 | wc -l')
|
||||||
|
-- "dircount ~" would count how many files are in ~ (home directory).
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='appendPath'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.appendPath(dir)
|
||||||
|
<a href="#appendPath" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Appends the provided dir to the command path (`$PATH`)
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string|table` **`dir`**
|
||||||
|
Directory (or directories) to append to path
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
hilbish.appendPath '~/go/bin'
|
||||||
|
-- Will add ~/go/bin to the command path.
|
||||||
|
|
||||||
|
-- Or do multiple:
|
||||||
|
hilbish.appendPath {
|
||||||
|
'~/go/bin',
|
||||||
|
'~/.local/bin'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='complete'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.complete(scope, cb)
|
||||||
|
<a href="#complete" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Registers a completion handler for the specified scope.
|
||||||
|
A `scope` is expected to be `command.<cmd>`,
|
||||||
|
replacing <cmd> with the name of the command (for example `command.git`).
|
||||||
|
The documentation for completions, under Features/Completions or `doc completions`
|
||||||
|
provides more details.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`scope`**
|
||||||
|
|
||||||
|
|
||||||
|
`function` **`cb`**
|
||||||
|
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
-- This is a very simple example. Read the full doc for completions for details.
|
||||||
|
hilbish.complete('command.sudo', function(query, ctx, fields)
|
||||||
|
if #fields == 0 then
|
||||||
|
-- complete for commands
|
||||||
|
local comps, pfx = hilbish.completion.bins(query, ctx, fields)
|
||||||
|
local compGroup = {
|
||||||
|
items = comps, -- our list of items to complete
|
||||||
|
type = 'grid' -- what our completions will look like.
|
||||||
|
}
|
||||||
|
|
||||||
|
return {compGroup}, pfx
|
||||||
|
end
|
||||||
|
|
||||||
|
-- otherwise just be boring and return files
|
||||||
|
|
||||||
|
local comps, pfx = hilbish.completion.files(query, ctx, fields)
|
||||||
|
local compGroup = {
|
||||||
|
items = comps,
|
||||||
|
type = 'grid'
|
||||||
|
}
|
||||||
|
|
||||||
|
return {compGroup}, pfx
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='cwd'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.cwd() -> string
|
||||||
|
<a href="#cwd" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns the current directory of the shell.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='exec'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.exec(cmd)
|
||||||
|
<a href="#exec" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Replaces the currently running Hilbish instance with the supplied command.
|
||||||
|
This can be used to do an in-place restart.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`cmd`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='goro'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.goro(fn)
|
||||||
|
<a href="#goro" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Puts `fn` in a Goroutine.
|
||||||
|
This can be used to run any function in another thread at the same time as other Lua code.
|
||||||
|
**NOTE: THIS FUNCTION MAY CRASH HILBISH IF OUTSIDE VARIABLES ARE ACCESSED.**
|
||||||
|
**This is a limitation of the Lua runtime.**
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`function` **`fn`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='highlighter'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.highlighter(line)
|
||||||
|
<a href="#highlighter" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Line highlighter handler.
|
||||||
|
This is mainly for syntax highlighting, but in reality could set the input
|
||||||
|
of the prompt to *display* anything. The callback is passed the current line
|
||||||
|
and is expected to return a line that will be used as the input display.
|
||||||
|
Note that to set a highlighter, one has to override this function.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`line`**
|
||||||
|
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
--This code will highlight all double quoted strings in green.
|
||||||
|
function hilbish.highlighter(line)
|
||||||
|
return line:gsub('"%w+"', function(c) return lunacolors.green(c) end)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='hinter'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.hinter(line, pos)
|
||||||
|
<a href="#hinter" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`line`**
|
||||||
|
|
||||||
|
|
||||||
|
`number` **`pos`**
|
||||||
|
Position of cursor in line. Usually equals string.len(line)
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
-- this will display "hi" after the cursor in a dimmed color.
|
||||||
|
function hilbish.hinter(line, pos)
|
||||||
|
return 'hi'
|
||||||
|
end
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='inputMode'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.inputMode(mode)
|
||||||
|
<a href="#inputMode" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Sets the input mode for Hilbish's line reader.
|
||||||
|
`emacs` is the default. Setting it to `vim` changes behavior of input to be
|
||||||
|
Vim-like with modes and Vim keybinds.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`mode`**
|
||||||
|
Can be set to either `emacs` or `vim`
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='interval'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.interval(cb, time) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;" id="lol">Timer</a>
|
||||||
|
<a href="#interval" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Runs the `cb` function every specified amount of `time`.
|
||||||
|
This creates a timer that ticking immediately.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`function` **`cb`**
|
||||||
|
|
||||||
|
|
||||||
|
`number` **`time`**
|
||||||
|
Time in milliseconds.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='multiprompt'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.multiprompt(str)
|
||||||
|
<a href="#multiprompt" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Changes the text prompt when Hilbish asks for more input.
|
||||||
|
This will show up when text is incomplete, like a missing quote
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`str`**
|
||||||
|
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
--[[
|
||||||
|
imagine this is your text input:
|
||||||
|
user ~ ∆ echo "hey
|
||||||
|
|
||||||
|
but there's a missing quote! hilbish will now prompt you so the terminal
|
||||||
|
will look like:
|
||||||
|
user ~ ∆ echo "hey
|
||||||
|
--> ...!"
|
||||||
|
|
||||||
|
so then you get
|
||||||
|
user ~ ∆ echo "hey
|
||||||
|
--> ...!"
|
||||||
|
hey ...!
|
||||||
|
]]--
|
||||||
|
hilbish.multiprompt '-->'
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='prependPath'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.prependPath(dir)
|
||||||
|
<a href="#prependPath" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Prepends `dir` to $PATH.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`dir`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='prompt'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.prompt(str, typ)
|
||||||
|
<a href="#prompt" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Changes the shell prompt to the provided string.
|
||||||
|
There are a few verbs that can be used in the prompt text.
|
||||||
|
These will be formatted and replaced with the appropriate values.
|
||||||
|
`%d` - Current working directory
|
||||||
|
`%u` - Name of current user
|
||||||
|
`%h` - Hostname of device
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`str`**
|
||||||
|
|
||||||
|
|
||||||
|
`string` **`typ?`**
|
||||||
|
Type of prompt, being left or right. Left by default.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
-- the default hilbish prompt without color
|
||||||
|
hilbish.prompt '%u %d ∆'
|
||||||
|
-- or something of old:
|
||||||
|
hilbish.prompt '%u@%h :%d $'
|
||||||
|
-- prompt: user@hostname: ~/directory $
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='read'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.read(prompt) -> input (string)
|
||||||
|
<a href="#read" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Read input from the user, using Hilbish's line editor/input reader.
|
||||||
|
This is a separate instance from the one Hilbish actually uses.
|
||||||
|
Returns `input`, will be nil if Ctrl-D is pressed, or an error occurs.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`prompt?`**
|
||||||
|
Text to print before input, can be empty.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='run'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.run(cmd, streams) -> exitCode (number), stdout (string), stderr (string)
|
||||||
|
<a href="#run" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Runs `cmd` in Hilbish's shell script interpreter.
|
||||||
|
The `streams` parameter specifies the output and input streams the command should use.
|
||||||
|
For example, to write command output to a sink.
|
||||||
|
As a table, the caller can directly specify the standard output, error, and input
|
||||||
|
streams of the command with the table keys `out`, `err`, and `input` respectively.
|
||||||
|
As a boolean, it specifies whether the command should use standard output or return its output streams.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`cmd`**
|
||||||
|
|
||||||
|
|
||||||
|
`table|boolean` **`streams`**
|
||||||
|
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
|
||||||
|
// This code is the same as `ls -l | wc -l`
|
||||||
|
local fs = require 'fs'
|
||||||
|
local pr, pw = fs.pipe()
|
||||||
|
hilbish.run('ls -l', {
|
||||||
|
stdout = pw,
|
||||||
|
stderr = pw,
|
||||||
|
})
|
||||||
|
|
||||||
|
pw:close()
|
||||||
|
|
||||||
|
hilbish.run('wc -l', {
|
||||||
|
stdin = pr
|
||||||
|
})
|
||||||
|
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='runnerMode'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.runnerMode(mode)
|
||||||
|
<a href="#runnerMode" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Sets the execution/runner mode for interactive Hilbish.
|
||||||
|
This determines whether Hilbish wll try to run input as Lua
|
||||||
|
and/or sh or only do one of either.
|
||||||
|
Accepted values for mode are hybrid (the default), hybridRev (sh first then Lua),
|
||||||
|
sh, and lua. It also accepts a function, to which if it is passed one
|
||||||
|
will call it to execute user input instead.
|
||||||
|
Read [about runner mode](../features/runner-mode) for more information.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string|function` **`mode`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='timeout'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.timeout(cb, time) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;" id="lol">Timer</a>
|
||||||
|
<a href="#timeout" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Executed the `cb` function after a period of `time`.
|
||||||
|
This creates a Timer that starts ticking immediately.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`function` **`cb`**
|
||||||
|
|
||||||
|
|
||||||
|
`number` **`time`**
|
||||||
|
Time to run in milliseconds.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='which'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.which(name) -> string
|
||||||
|
<a href="#which" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Checks if `name` is a valid command.
|
||||||
|
Will return the path of the binary, or a basename if it's a commander.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`name`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Types
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
## Sink
|
||||||
|
A sink is a structure that has input and/or output to/from
|
||||||
|
a desination.
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
#### autoFlush(auto)
|
||||||
|
Sets/toggles the option of automatically flushing output.
|
||||||
|
A call with no argument will toggle the value.
|
||||||
|
|
||||||
|
#### flush()
|
||||||
|
Flush writes all buffered input to the sink.
|
||||||
|
|
||||||
|
#### read() -> string
|
||||||
|
Reads a liine of input from the sink.
|
||||||
|
|
||||||
|
#### readAll() -> string
|
||||||
|
Reads all input from the sink.
|
||||||
|
|
||||||
|
#### write(str)
|
||||||
|
Writes data to a sink.
|
||||||
|
|
||||||
|
#### writeln(str)
|
||||||
|
Writes data to a sink with a newline at the end.
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
---
|
||||||
|
title: Module hilbish.aliases
|
||||||
|
description: command aliasing
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "API"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
The alias interface deals with all command aliases in Hilbish.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|<a href="#aliases.add">add(alias, cmd)</a>|This is an alias (ha) for the [hilbish.alias](../#alias) function.|
|
||||||
|
|<a href="#aliases.delete">delete(name)</a>|Removes an alias.|
|
||||||
|
|<a href="#aliases.list">list() -> table[string, string]</a>|Get a table of all aliases, with string keys as the alias and the value as the command.|
|
||||||
|
|<a href="#aliases.resolve">resolve(alias) -> string?</a>|Resolves an alias to its original command. Will thrown an error if the alias doesn't exist.|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='aliases.add'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.aliases.add(alias, cmd)
|
||||||
|
<a href="#aliases.add" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
This is an alias (ha) for the [hilbish.alias](../#alias) function.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='aliases.delete'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.aliases.delete(name)
|
||||||
|
<a href="#aliases.delete" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Removes an alias.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`name`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='aliases.list'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.aliases.list() -> table[string, string]
|
||||||
|
<a href="#aliases.list" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Get a table of all aliases, with string keys as the alias and the value as the command.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
hilbish.aliases.add('hi', 'echo hi')
|
||||||
|
|
||||||
|
local aliases = hilbish.aliases.list()
|
||||||
|
-- -> {hi = 'echo hi'}
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='aliases.resolve'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.aliases.resolve(alias) -> string?
|
||||||
|
<a href="#aliases.resolve" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Resolves an alias to its original command. Will thrown an error if the alias doesn't exist.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`alias`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
---
|
||||||
|
title: Module hilbish.completion
|
||||||
|
description: tab completions
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "API"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
The completions interface deals with tab completions.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|<a href="#completion.bins">bins(query, ctx, fields) -> entries (table), prefix (string)</a>|Return binaries/executables based on the provided parameters.|
|
||||||
|
|<a href="#completion.call">call(name, query, ctx, fields) -> completionGroups (table), prefix (string)</a>|Calls a completer function. This is mainly used to call a command completer, which will have a `name`|
|
||||||
|
|<a href="#completion.files">files(query, ctx, fields) -> entries (table), prefix (string)</a>|Returns file matches based on the provided parameters.|
|
||||||
|
|<a href="#completion.handler">handler(line, pos)</a>|This function contains the general completion handler for Hilbish. This function handles|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='completion.bins'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.completion.bins(query, ctx, fields) -> entries (table), prefix (string)
|
||||||
|
<a href="#completion.bins" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Return binaries/executables based on the provided parameters.
|
||||||
|
This function is meant to be used as a helper in a command completion handler.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`query`**
|
||||||
|
|
||||||
|
|
||||||
|
`string` **`ctx`**
|
||||||
|
|
||||||
|
|
||||||
|
`table` **`fields`**
|
||||||
|
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
-- an extremely simple completer for sudo.
|
||||||
|
hilbish.complete('command.sudo', function(query, ctx, fields)
|
||||||
|
table.remove(fields, 1)
|
||||||
|
if #fields[1] then
|
||||||
|
-- return commands because sudo runs a command as root..!
|
||||||
|
|
||||||
|
local entries, pfx = hilbish.completion.bins(query, ctx, fields)
|
||||||
|
return {
|
||||||
|
type = 'grid',
|
||||||
|
items = entries
|
||||||
|
}, pfx
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ... else suggest files or anything else ..
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='completion.call'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.completion.call(name, query, ctx, fields) -> completionGroups (table), prefix (string)
|
||||||
|
<a href="#completion.call" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Calls a completer function. This is mainly used to call a command completer, which will have a `name`
|
||||||
|
in the form of `command.name`, example: `command.git`.
|
||||||
|
You can check the Completions doc or `doc completions` for info on the `completionGroups` return value.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`name`**
|
||||||
|
|
||||||
|
|
||||||
|
`string` **`query`**
|
||||||
|
|
||||||
|
|
||||||
|
`string` **`ctx`**
|
||||||
|
|
||||||
|
|
||||||
|
`table` **`fields`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='completion.files'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.completion.files(query, ctx, fields) -> entries (table), prefix (string)
|
||||||
|
<a href="#completion.files" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns file matches based on the provided parameters.
|
||||||
|
This function is meant to be used as a helper in a command completion handler.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`query`**
|
||||||
|
|
||||||
|
|
||||||
|
`string` **`ctx`**
|
||||||
|
|
||||||
|
|
||||||
|
`table` **`fields`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='completion.handler'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.completion.handler(line, pos)
|
||||||
|
<a href="#completion.handler" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
This function contains the general completion handler for Hilbish. This function handles
|
||||||
|
completion of everything, which includes calling other command handlers, binaries, and files.
|
||||||
|
This function can be overriden to supply a custom handler. Note that alias resolution is required to be done in this function.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`line`**
|
||||||
|
The current Hilbish command line
|
||||||
|
|
||||||
|
`number` **`pos`**
|
||||||
|
Numerical position of the cursor
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
-- stripped down version of the default implementation
|
||||||
|
function hilbish.completion.handler(line, pos)
|
||||||
|
local query = fields[#fields]
|
||||||
|
|
||||||
|
if #fields == 1 then
|
||||||
|
-- call bins handler here
|
||||||
|
else
|
||||||
|
-- call command completer or files completer here
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
---
|
||||||
|
title: Module 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
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|<a href="#editor.getLine">getLine() -> string</a>|Returns the current input line.|
|
||||||
|
|<a href="#editor.getVimRegister">getVimRegister(register) -> string</a>|Returns the text that is at the register.|
|
||||||
|
|<a href="#editor.insert">insert(text)</a>|Inserts text into the Hilbish command line.|
|
||||||
|
|<a href="#editor.getChar">getChar() -> string</a>|Reads a keystroke from the user. This is in a format of something like Ctrl-L.|
|
||||||
|
|<a href="#editor.setVimRegister">setVimRegister(register, text)</a>|Sets the vim register at `register` to hold the passed text.|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='editor.getLine'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.editor.getLine() -> string
|
||||||
|
<a href="#editor.getLine" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns the current input line.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='editor.getVimRegister'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.editor.getVimRegister(register) -> string
|
||||||
|
<a href="#editor.getVimRegister" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns the text that is at the register.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`register`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='editor.insert'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.editor.insert(text)
|
||||||
|
<a href="#editor.insert" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Inserts text into the Hilbish command line.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`text`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='editor.getChar'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.editor.getChar() -> string
|
||||||
|
<a href="#editor.getChar" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Reads a keystroke from the user. This is in a format of something like Ctrl-L.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='editor.setVimRegister'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.editor.setVimRegister(register, text)
|
||||||
|
<a href="#editor.setVimRegister" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Sets the vim register at `register` to hold the passed text.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`text`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
---
|
||||||
|
title: Module 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
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|<a href="#history.add">add(cmd)</a>|Adds a command to the history.|
|
||||||
|
|<a href="#history.all">all() -> table</a>|Retrieves all history as a table.|
|
||||||
|
|<a href="#history.clear">clear()</a>|Deletes all commands from the history.|
|
||||||
|
|<a href="#history.get">get(index)</a>|Retrieves a command from the history based on the `index`.|
|
||||||
|
|<a href="#history.size">size() -> number</a>|Returns the amount of commands in the history.|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='history.add'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.history.add(cmd)
|
||||||
|
<a href="#history.add" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Adds a command to the history.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`cmd`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='history.all'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.history.all() -> table
|
||||||
|
<a href="#history.all" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Retrieves all history as a table.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='history.clear'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.history.clear()
|
||||||
|
<a href="#history.clear" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Deletes all commands from the history.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='history.get'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.history.get(index)
|
||||||
|
<a href="#history.get" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Retrieves a command from the history based on the `index`.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`number` **`index`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='history.size'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.history.size() -> number
|
||||||
|
<a href="#history.size" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns the amount of commands in the history.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
---
|
||||||
|
title: Module 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
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|<a href="#jobs.add">add(cmdstr, args, execPath)</a>|Creates a new job. This function does not run the job. This function is intended to be|
|
||||||
|
|<a href="#jobs.all">all() -> table[@Job]</a>|Returns a table of all job objects.|
|
||||||
|
|<a href="#jobs.disown">disown(id)</a>|Disowns a job. This simply deletes it from the list of jobs without stopping it.|
|
||||||
|
|<a href="#jobs.get">get(id) -> @Job</a>|Get a job object via its ID.|
|
||||||
|
|<a href="#jobs.last">last() -> @Job</a>|Returns the last added job to the table.|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='jobs.add'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.jobs.add(cmdstr, args, execPath)
|
||||||
|
<a href="#jobs.add" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Creates a new job. This function does not run the job. This function is intended to be
|
||||||
|
used by runners, but can also be used to create jobs via Lua. Commanders cannot be ran as jobs.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`cmdstr`**
|
||||||
|
String that a user would write for the job
|
||||||
|
|
||||||
|
`table` **`args`**
|
||||||
|
Arguments for the commands. Has to include the name of the command.
|
||||||
|
|
||||||
|
`string` **`execPath`**
|
||||||
|
Binary to use to run the command. Needs to be an absolute path.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
hilbish.jobs.add('go build', {'go', 'build'}, '/usr/bin/go')
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='jobs.all'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.jobs.all() -> table[<a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;" id="lol">Job</a>]
|
||||||
|
<a href="#jobs.all" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns a table of all job objects.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='jobs.disown'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.jobs.disown(id)
|
||||||
|
<a href="#jobs.disown" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Disowns a job. This simply deletes it from the list of jobs without stopping it.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`number` **`id`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='jobs.get'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.jobs.get(id) -> <a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;" id="lol">Job</a>
|
||||||
|
<a href="#jobs.get" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Get a job object via its ID.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='jobs.last'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.jobs.last() -> <a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;" id="lol">Job</a>
|
||||||
|
<a href="#jobs.last" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns the last added job to the table.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Types
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
## Job
|
||||||
|
The Job type describes a Hilbish job.
|
||||||
|
## Object properties
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|cmd|The user entered command string for the job.|
|
||||||
|
|running|Whether the job is running or not.|
|
||||||
|
|id|The ID of the job in the job table|
|
||||||
|
|pid|The Process ID|
|
||||||
|
|exitCode|The last exit code of the job.|
|
||||||
|
|stdout|The standard output of the job. This just means the normal logs of the process.|
|
||||||
|
|stderr|The standard error stream of the process. This (usually) includes error messages of the job.|
|
||||||
|
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
#### background()
|
||||||
|
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.
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
---
|
||||||
|
title: Module hilbish.module
|
||||||
|
description: native module loading
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "API"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
The hilbish.module interface provides a function to load
|
||||||
|
Hilbish plugins/modules. Hilbish modules are Go-written
|
||||||
|
plugins (see https://pkg.go.dev/plugin) that are used to add functionality
|
||||||
|
to Hilbish that cannot be written in Lua for any reason.
|
||||||
|
|
||||||
|
Note that you don't ever need to use the load function that is here as
|
||||||
|
modules can be loaded with a `require` call like Lua C modules, and the
|
||||||
|
search paths can be changed with the `paths` property here.
|
||||||
|
|
||||||
|
To make a valid native module, the Go plugin has to export a Loader function
|
||||||
|
with a signature like so: `func(*rt.Runtime) rt.Value`.
|
||||||
|
|
||||||
|
`rt` in this case refers to the Runtime type at
|
||||||
|
https://pkg.go.dev/github.com/arnodel/golua@master/runtime#Runtime
|
||||||
|
|
||||||
|
Hilbish uses this package as its Lua runtime. You will need to read
|
||||||
|
it to use it for a native plugin.
|
||||||
|
|
||||||
|
Here is some code for an example plugin:
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
rt "github.com/arnodel/golua/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Loader(rtm *rt.Runtime) rt.Value {
|
||||||
|
return rt.StringValue("hello world!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This can be compiled with `go build -buildmode=plugin plugin.go`.
|
||||||
|
If you attempt to require and print the result (`print(require 'plugin')`), it will show "hello world!"
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|<a href="#module.load">load(path)</a>|Loads a module at the designated `path`.|
|
||||||
|
|
||||||
|
## Static module fields
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|paths|A list of paths to search when loading native modules. This is in the style of Lua search paths and will be used when requiring native modules. Example: `?.so;?/?.so`|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='module.load'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.module.load(path)
|
||||||
|
<a href="#module.load" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Loads a module at the designated `path`.
|
||||||
|
It will throw if any error occurs.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`path`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
title: Module hilbish.os
|
||||||
|
description: operating system info
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "API"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
Provides simple text information properties about the current operating system.
|
||||||
|
This mainly includes the name and version.
|
||||||
|
|
||||||
|
## Static module fields
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|family|Family name of the current OS|
|
||||||
|
|name|Pretty name of the current OS|
|
||||||
|
|version|Version of the current OS|
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
---
|
||||||
|
title: Module 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.
|
||||||
|
|
||||||
|
Runners are functions that evaluate user input. The default runners in
|
||||||
|
Hilbish can run shell script and Lua code.
|
||||||
|
|
||||||
|
A runner is passed the input and has to return a table with these values.
|
||||||
|
All are not required, only the useful ones the runner needs to return.
|
||||||
|
(So if there isn't an error, just omit `err`.)
|
||||||
|
|
||||||
|
- `exitCode` (number): A numerical code to indicate the exit result.
|
||||||
|
- `input` (string): The user input. This will be used to add
|
||||||
|
to the history.
|
||||||
|
- `err` (string): A string to indicate an interal error for the runner.
|
||||||
|
It can be set to a few special values for Hilbish to throw the right hooks and have a better looking message:
|
||||||
|
|
||||||
|
`[command]: not-found` will throw a command.not-found hook based on what `[command]` is.
|
||||||
|
|
||||||
|
`[command]: not-executable` will throw a command.not-executable hook.
|
||||||
|
- `continue` (boolean): Whether to prompt the user for more input.
|
||||||
|
|
||||||
|
Here is a simple example of a fennel runner. It falls back to
|
||||||
|
shell script if fennel eval has an error.
|
||||||
|
```lua
|
||||||
|
local fennel = require 'fennel'
|
||||||
|
|
||||||
|
hilbish.runnerMode(function(input)
|
||||||
|
local ok = pcall(fennel.eval, input)
|
||||||
|
if ok then
|
||||||
|
return {
|
||||||
|
input = input
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return hilbish.runner.sh(input)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|<a href="#runner.setMode">setMode(cb)</a>|This is the same as the `hilbish.runnerMode` function.|
|
||||||
|
|<a href="#runner.lua">lua(cmd)</a>|Evaluates `cmd` as Lua input. This is the same as using `dofile`|
|
||||||
|
|<a href="#runner.sh">sh(cmd)</a>|Runs a command in Hilbish's shell script interpreter.|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='runner.setMode'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.runner.setMode(cb)
|
||||||
|
<a href="#runner.setMode" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
This is the same as the `hilbish.runnerMode` function.
|
||||||
|
It takes a callback, which will be used to execute all interactive input.
|
||||||
|
In normal cases, neither callbacks should be overrided by the user,
|
||||||
|
as the higher level functions listed below this will handle it.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`function` **`cb`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='runner.lua'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.runner.lua(cmd)
|
||||||
|
<a href="#runner.lua" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Evaluates `cmd` as Lua input. This is the same as using `dofile`
|
||||||
|
or `load`, but is appropriated for the runner interface.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`cmd`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='runner.sh'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.runner.sh(cmd)
|
||||||
|
<a href="#runner.sh" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Runs a command in Hilbish's shell script interpreter.
|
||||||
|
This is the equivalent of using `source`.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`string` **`cmd`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
---
|
||||||
|
title: Module 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`, or `Module hilbish` on the Website).
|
||||||
|
|
||||||
|
An example of usage:
|
||||||
|
```lua
|
||||||
|
local t = hilbish.timers.create(hilbish.timers.TIMEOUT, 5000, function()
|
||||||
|
print 'hello!'
|
||||||
|
end)
|
||||||
|
|
||||||
|
t:start()
|
||||||
|
print(t.running) // true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|<a href="#timers.create">create(type, time, callback) -> @Timer</a>|Creates a timer that runs based on the specified `time`.|
|
||||||
|
|<a href="#timers.get">get(id) -> @Timer</a>|Retrieves a timer via its ID.|
|
||||||
|
|
||||||
|
## Static module fields
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|INTERVAL|Constant for an interval timer type|
|
||||||
|
|TIMEOUT|Constant for a timeout timer type|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='timers.create'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.timers.create(type, time, callback) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;" id="lol">Timer</a>
|
||||||
|
<a href="#timers.create" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Creates a timer that runs based on the specified `time`.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`number` **`type`**
|
||||||
|
What kind of timer to create, can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT`
|
||||||
|
|
||||||
|
`number` **`time`**
|
||||||
|
The amount of time the function should run in milliseconds.
|
||||||
|
|
||||||
|
`function` **`callback`**
|
||||||
|
The function to run for the timer.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='timers.get'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
hilbish.timers.get(id) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;" id="lol">Timer</a>
|
||||||
|
<a href="#timers.get" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Retrieves a timer via its ID.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`number` **`id`**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Types
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
## Timer
|
||||||
|
The Job type describes a Hilbish timer.
|
||||||
|
## Object properties
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|type|What type of timer it is|
|
||||||
|
|running|If the timer is running|
|
||||||
|
|duration|The duration in milliseconds that the timer will run|
|
||||||
|
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
#### start()
|
||||||
|
Starts a timer.
|
||||||
|
|
||||||
|
#### stop()
|
||||||
|
Stops a timer.
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
title: Module 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.
|
||||||
|
|
||||||
|
## Static module fields
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|config|The user's config directory|
|
||||||
|
|data|The user's directory for program data|
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
---
|
||||||
|
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
|
||||||
|
|||
|
||||||
|
|----|----|
|
||||||
|
|<a href="#restoreState">restoreState()</a>|Restores the last saved state of the terminal|
|
||||||
|
|<a href="#saveState">saveState()</a>|Saves the current state of the terminal.|
|
||||||
|
|<a href="#setRaw">setRaw()</a>|Puts the terminal into raw mode.|
|
||||||
|
|<a href="#size">size()</a>|Gets the dimensions of the terminal. Returns a table with `width` and `height`|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='restoreState'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
terminal.restoreState()
|
||||||
|
<a href="#restoreState" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Restores the last saved state of the terminal
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='saveState'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
terminal.saveState()
|
||||||
|
<a href="#saveState" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Saves the current state of the terminal.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='setRaw'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
terminal.setRaw()
|
||||||
|
<a href="#setRaw" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Puts the terminal into raw mode.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='size'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
terminal.size()
|
||||||
|
<a href="#size" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Gets the dimensions of the terminal. Returns a table with `width` and `height`
|
||||||
|
NOTE: The size refers to the amount of columns and rows of text that can fit in the terminal.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
|
@ -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`
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
---
|
||||||
|
title: Completions
|
||||||
|
description: Tab completion for commands.
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "Features"
|
||||||
|
---
|
||||||
|
|
||||||
|
Completions for commands can be created with the [`hilbish.complete`](../api/hilbish#complete)
|
||||||
|
function. See the link for how to use it.
|
||||||
|
|
||||||
|
To create completions for a command is simple.
|
||||||
|
The callback will be passed 3 parameters:
|
||||||
|
- `query` (string): The text that the user is currently trying to complete.
|
||||||
|
This should be used to match entries.
|
||||||
|
- `ctx` (string): Contains the entire line. Use this if
|
||||||
|
more text is needed to be parsed for context.
|
||||||
|
- `fields` (string): The `ctx` split up by spaces.
|
||||||
|
|
||||||
|
In most cases, the completer just uses `fields` to check the amount
|
||||||
|
and `query` on what to match entries on.
|
||||||
|
|
||||||
|
In order to return your results, it has to go within a "completion group."
|
||||||
|
Then you return a table of completion groups and a prefix. The prefix will
|
||||||
|
usually just be the `query`.
|
||||||
|
|
||||||
|
Hilbish allows one to mix completion menus of different types, so
|
||||||
|
a grid menu and a list menu can be used and complete and display at the same time.
|
||||||
|
A completion group is a table with these keys:
|
||||||
|
- `type` (string): type of completion menu, either `grid` or `list`.
|
||||||
|
- `items` (table): a list of items.
|
||||||
|
|
||||||
|
The requirements of the `items` table is different based on the
|
||||||
|
`type`. If it is a `grid`, it can simply be a table of strings.
|
||||||
|
|
||||||
|
Otherwise if it is a `list` then each entry can
|
||||||
|
either be a string or a table.
|
||||||
|
Example:
|
||||||
|
```lua
|
||||||
|
local cg = {
|
||||||
|
items = {
|
||||||
|
'list item 1',
|
||||||
|
['--command-flag-here'] = {'this does a thing', '--the-flag-alias'}
|
||||||
|
},
|
||||||
|
type = 'list'
|
||||||
|
}
|
||||||
|
local cg2 = {
|
||||||
|
items = {'just', 'a bunch', 'of items', 'here', 'hehe'},
|
||||||
|
type = 'grid'
|
||||||
|
}
|
||||||
|
|
||||||
|
return {cg, cg2}, prefix
|
||||||
|
```
|
||||||
|
|
||||||
|
Which looks like this:
|
||||||
|
{{< video src="https://safe.saya.moe/t4CiLK6dgPbD.mp4" >}}
|
||||||
|
|
||||||
|
# Completion Handler
|
||||||
|
Like most parts of Hilbish, it's made to be extensible and
|
||||||
|
customizable. The default handler for completions in general can
|
||||||
|
be overwritten to provide more advanced completions if needed.
|
||||||
|
This usually doesn't need to be done though, unless you know
|
||||||
|
what you're doing.
|
||||||
|
|
||||||
|
The default completion handler provides 3 things:
|
||||||
|
binaries (with a plain name requested to complete, those in
|
||||||
|
$PATH), files, or command completions. It will try to run a handler
|
||||||
|
for the command or fallback to file completions.
|
||||||
|
|
||||||
|
To overwrite it, just assign a function to `hilbish.completion.handler` like so:
|
||||||
|
```lua
|
||||||
|
-- line is the entire line as a string
|
||||||
|
-- pos is the position of the cursor.
|
||||||
|
function hilbish.completion.handler(line, pos)
|
||||||
|
-- do things
|
||||||
|
end
|
||||||
|
```
|
|
@ -1,44 +0,0 @@
|
||||||
Hilbish has a pretty good completion system. It has a nice looking menu,
|
|
||||||
with 2 types of menus: grid (like file completions) or list.
|
|
||||||
|
|
||||||
Like most parts of Hilbish, it's made to be extensible and customizable.
|
|
||||||
The default handler for completions in general can be overwritten to provide
|
|
||||||
more advanced completions if needed.
|
|
||||||
|
|
||||||
# Completion Handler
|
|
||||||
By default, it provides 3 things: for the first argument, binaries (with a
|
|
||||||
plain name requested to complete, those in $PATH), files, or command
|
|
||||||
completions. With the default completion handler, it will try to run a
|
|
||||||
handler for the command or fallback to file completions.
|
|
||||||
|
|
||||||
To overwrite it, just assign a function to `hilbish.completion.handler`
|
|
||||||
like so:
|
|
||||||
function hilbish.completion.handler(line, pos)
|
|
||||||
-- do things
|
|
||||||
end
|
|
||||||
It is passed 2 arguments, the entire line, and the current cursor position.
|
|
||||||
The functions in the completion interface take 3 arguments: query, ctx,
|
|
||||||
and fields. The `query`, which what the user is currently trying to complete,
|
|
||||||
`ctx`, being just the entire line, and `fields` being a table of arguments.
|
|
||||||
It's just `ctx` split up, delimited by spaces.
|
|
||||||
It's expected to return 2 things: a table of completion groups, and a prefix.
|
|
||||||
A completion group is defined as a table with 2 keys: `items` and `type`.
|
|
||||||
The `items` field is just a table of items to use for completions.
|
|
||||||
The `type` is for the completion menu type, being either `grid` or `list`.
|
|
||||||
The prefix is what all the completions start with. It should be empty
|
|
||||||
if the user doesn't have a query. If the beginning of the completion
|
|
||||||
item does not match the prefix, it will be replaced and fixed properly
|
|
||||||
in the line. It is case sensitive.
|
|
||||||
|
|
||||||
If you want to overwrite the functionality of the general completion handler,
|
|
||||||
or make your command completion have files as well (and filter them),
|
|
||||||
then there is the `files` function, which is mentioned below.
|
|
||||||
|
|
||||||
# Completion Interface
|
|
||||||
## Functions
|
|
||||||
- `files(query, ctx, fields)` -> table, prefix: get file completions, based
|
|
||||||
on the user's query.
|
|
||||||
- `bins(query, ctx, fields)` -> table, prefix: get binary/executable
|
|
||||||
completions, based on user query.
|
|
||||||
- `call(scope, query, ctx, fields)` -> table, prefix: call a completion handler
|
|
||||||
with `scope`, usually being in the form of `command.<name>`
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
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).
|
||||||
|
|
||||||
|
# Why?
|
||||||
|
Hilbish emerged from the desire of a Lua configured shell.
|
||||||
|
It was the initial reason that it was created, but now it's more:
|
||||||
|
to be hyper extensible, simpler and more user friendly.
|
||||||
|
|
||||||
|
# Does it have "autocompletion" or "tab completion"
|
||||||
|
Of course! This is a modern shell. Hilbish provides a way for users
|
||||||
|
to write tab completion for any command and/or the whole shell.
|
||||||
|
Inline hinting and syntax highlighting are also available.
|
|
@ -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
|
||||||
|
new ones are always being added. 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.
|
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
title: Notification
|
||||||
|
description: Get notified of shell actions.
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "Features"
|
||||||
|
---
|
||||||
|
|
||||||
|
Hilbish features a simple notification system which can be
|
||||||
|
used by other plugins and parts of the shell to notify the user
|
||||||
|
of various actions. This is used via the `hilbish.message` interface.
|
||||||
|
|
||||||
|
A `message` is defined as a table with the following properties:
|
||||||
|
- `icon`: A unicode/emoji icon for the notification.
|
||||||
|
- `title`: The title of the message
|
||||||
|
- `text`: Message text/body
|
||||||
|
- `channel`: The source of the message. This should be a
|
||||||
|
unique and easily readable text identifier.
|
||||||
|
- `summary`: A short summary of the notification and message.
|
||||||
|
If this is not present and you are using this to display messages,
|
||||||
|
you should take part of the `text` instead.
|
||||||
|
|
||||||
|
The `hilbish.message` interface provides the following functions:
|
||||||
|
- `send(message)`: Sends a message and emits the `hilbish.notification`
|
||||||
|
signal. DO NOT emit the `hilbish.notification` signal directly, or
|
||||||
|
the message will not be stored by the message handler.
|
||||||
|
- `read(idx)`: Marks message at `idx` as read.
|
||||||
|
- `delete(idx)`: Removes message at `idx`.
|
||||||
|
- `readAll()`: Marks all messages as read.
|
||||||
|
- `clear()`: Deletes all messages.
|
||||||
|
|
||||||
|
There are a few simple use cases of this notification/messaging system.
|
||||||
|
It could also be used as some "inter-shell" messaging system (???) but
|
||||||
|
is intended to display to users.
|
||||||
|
|
||||||
|
An example is notifying users of completed jobs/commands ran in the background.
|
||||||
|
Any Hilbish-native command (think the upcoming Greenhouse pager) can display
|
||||||
|
it.
|
|
@ -0,0 +1,78 @@
|
||||||
|
---
|
||||||
|
title: Options
|
||||||
|
description: Simple customizable options.
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "Features"
|
||||||
|
---
|
||||||
|
|
||||||
|
Opts are simple toggle or value options a user can set in Hilbish.
|
||||||
|
As toggles, there are things like `autocd` or history saving. As values,
|
||||||
|
there is the `motd` which the user can either change to a custom string or disable.
|
||||||
|
|
||||||
|
Opts are accessed from the `hilbish.opts` table. Here they can either
|
||||||
|
be read or modified
|
||||||
|
|
||||||
|
### `autocd`
|
||||||
|
#### Value: `boolean`
|
||||||
|
#### Default: `false`
|
||||||
|
|
||||||
|
The autocd opt makes it so that lone directories attempted to be executed are
|
||||||
|
instead set as the shell's directory.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
~/Directory
|
||||||
|
∆ ~
|
||||||
|
~
|
||||||
|
∆ Downloads
|
||||||
|
~/Downloads
|
||||||
|
∆ ../Documents
|
||||||
|
~/Documents
|
||||||
|
∆
|
||||||
|
```
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
### `history`
|
||||||
|
#### Value: `boolean`
|
||||||
|
#### Default: `true`
|
||||||
|
Sets whether command history will be saved or not.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
### `greeting`
|
||||||
|
#### Value: `boolean` or `string`
|
||||||
|
The greeting is the message that Hilbish shows on startup
|
||||||
|
(the one which says Welcome to Hilbish).
|
||||||
|
|
||||||
|
This can be set to either true/false to enable/disable or a custom greeting string.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
### `motd`
|
||||||
|
#### Value: `boolean`
|
||||||
|
#### Default: `true`
|
||||||
|
The message of the day shows the current major.minor version and
|
||||||
|
includes a small range of things added in the current release.
|
||||||
|
|
||||||
|
This can be set to `false` to disable the message.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
### `fuzzy`
|
||||||
|
#### Value: `boolean`
|
||||||
|
#### Default: `false`
|
||||||
|
Toggles the functionality of fuzzy history searching, usable
|
||||||
|
via the menu in Ctrl-R. Fuzzy searching is an approximate searching
|
||||||
|
method, which means results that match *closest* will be shown instead
|
||||||
|
of an exact match.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
### `notifyJobFinish`
|
||||||
|
#### Value: `boolean`
|
||||||
|
#### Default: `true`
|
||||||
|
If this is enabled, when a background job is finished,
|
||||||
|
a [notification](../notifications) will be sent.
|
|
@ -0,0 +1,66 @@
|
||||||
|
---
|
||||||
|
title: Runner Mode
|
||||||
|
description: Customize the interactive script/command runner.
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "Features"
|
||||||
|
---
|
||||||
|
|
||||||
|
Hilbish allows you to change how interactive text can be interpreted.
|
||||||
|
This is mainly due to the fact that the default method Hilbish uses
|
||||||
|
is that it runs Lua first and then falls back to shell script.
|
||||||
|
|
||||||
|
In some cases, someone might want to switch to just shell script to avoid
|
||||||
|
it while interactive but still have a Lua config, or go full Lua to use
|
||||||
|
Hilbish as a REPL. This also allows users to add alternative languages like
|
||||||
|
Fennel as the interactive script runner.
|
||||||
|
|
||||||
|
Runner mode can also be used to handle specific kinds of input before
|
||||||
|
evaluating like normal, which is how [Link.hsh](https://github.com/TorchedSammy/Link.hsh)
|
||||||
|
handles links.
|
||||||
|
|
||||||
|
The "runner mode" of Hilbish is customizable via `hilbish.runnerMode`,
|
||||||
|
which determines how Hilbish will run user input. By default, this is
|
||||||
|
set to `hybrid` which is the previously mentioned behaviour of running Lua
|
||||||
|
first then going to shell script. If you want the reverse order, you can
|
||||||
|
set it to `hybridRev` and for isolated modes there is `sh` and `lua`
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
You can also set it to a function, which will be called everytime Hilbish
|
||||||
|
needs to run interactive input. For more detail, see the [API documentation](../../api/hilbish/hilbish.runner)
|
||||||
|
|
||||||
|
The `hilbish.runner` interface is an alternative to using `hilbish.runnerMode`
|
||||||
|
and also provides the shell script and Lua runner functions that Hilbish itself uses.
|
||||||
|
|
||||||
|
A runner function is expected to return a table with the following values:
|
||||||
|
- `exitCode` (number): Exit code of the command
|
||||||
|
- `input` (string): The text input of the user. This is used by Hilbish to append extra input, in case
|
||||||
|
more is requested.
|
||||||
|
- `err` (string): A string that represents an error from the runner.
|
||||||
|
This should only be set when, for example, there is a syntax error.
|
||||||
|
It can be set to a few special values for Hilbish to throw the right
|
||||||
|
hooks and have a better looking message.
|
||||||
|
- `<command>: not-found` will throw a `command.not-found` hook
|
||||||
|
based on what `<command>` is.
|
||||||
|
- `<command>: not-executable` will throw a `command.not-executable` hook.
|
||||||
|
- `continue` (boolean): Whether Hilbish should prompt the user for no input
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
These are the "low level" functions for the `hilbish.runner` interface.
|
||||||
|
|
||||||
|
+ setMode(mode) > The same as `hilbish.runnerMode`
|
||||||
|
+ sh(input) -> table > Runs `input` in Hilbish's sh interpreter
|
||||||
|
+ lua(input) -> table > Evals `input` as Lua code
|
||||||
|
|
||||||
|
These functions should be preferred over the previous ones.
|
||||||
|
+ setCurrent(mode) > The same as `setMode`, but works with runners managed
|
||||||
|
via the functions below.
|
||||||
|
+ add(name, runner) > Adds a runner to a table of available runners. The `runner`
|
||||||
|
argument is either a function or a table with a run callback.
|
||||||
|
+ set(name, runner) > The same as `add` but requires passing a table and
|
||||||
|
overwrites if the `name`d runner already exists.
|
||||||
|
+ get(name) > runner > Gets a runner by name. It is a table with at least a
|
||||||
|
run function, to run input.
|
||||||
|
+ exec(cmd, runnerName) > Runs `cmd` with a runner. If `runnerName` isn't passed,
|
||||||
|
the current runner mode is used.
|
22
docs/fs.txt
22
docs/fs.txt
|
@ -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`
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
---
|
||||||
|
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. Though if
|
||||||
|
you still decide to do it, 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 <user>`.
|
||||||
|
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.
|
|
@ -1,62 +0,0 @@
|
||||||
alias(cmd, orig) > Sets an alias of `cmd` to `orig`
|
|
||||||
|
|
||||||
appendPath(dir) > Appends `dir` to $PATH
|
|
||||||
|
|
||||||
complete(scope, cb) > Registers a completion handler for `scope`.
|
|
||||||
A `scope` is currently only expected to be `command.<cmd>`,
|
|
||||||
replacing <cmd> with the name of the command (for example `command.git`).
|
|
||||||
`cb` must be a function that returns a table of "completion groups."
|
|
||||||
Check `doc completions` for more information.
|
|
||||||
|
|
||||||
cwd() > Returns the current directory of the shell
|
|
||||||
|
|
||||||
exec(cmd) > Replaces running hilbish with `cmd`
|
|
||||||
|
|
||||||
goro(fn) > Puts `fn` in a goroutine
|
|
||||||
|
|
||||||
highlighter(line) > Line highlighter handler. This is mainly for syntax highlighting, but in
|
|
||||||
reality could set the input of the prompt to *display* anything. The
|
|
||||||
callback is passed the current line and is expected to return a line that
|
|
||||||
will be used as the input display.
|
|
||||||
|
|
||||||
hinter(line, pos) > The command line hint handler. It gets called on every key insert to
|
|
||||||
determine what text to use as an inline hint. It is passed the current
|
|
||||||
line and cursor position. It is expected to return a string which is used
|
|
||||||
as the text for the hint. This is by default a shim. To set hints,
|
|
||||||
override this function with your custom handler.
|
|
||||||
|
|
||||||
inputMode(mode) > Sets the input mode for Hilbish's line reader. Accepts either emacs or vim
|
|
||||||
|
|
||||||
interval(cb, time) > Runs the `cb` function every `time` milliseconds.
|
|
||||||
Returns a `timer` object (see `doc timers`).
|
|
||||||
|
|
||||||
multiprompt(str) > Changes the continued line prompt to `str`
|
|
||||||
|
|
||||||
prependPath(dir) > Prepends `dir` to $PATH
|
|
||||||
|
|
||||||
prompt(str, typ?) > Changes the shell prompt to `str`
|
|
||||||
There are a few verbs that can be used in the prompt text.
|
|
||||||
These will be formatted and replaced with the appropriate values.
|
|
||||||
`%d` - Current working directory
|
|
||||||
`%u` - Name of current user
|
|
||||||
`%h` - Hostname of device
|
|
||||||
|
|
||||||
read(prompt) -> input? > Read input from the user, using Hilbish's line editor/input reader.
|
|
||||||
This is a separate instance from the one Hilbish actually uses.
|
|
||||||
Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
|
|
||||||
|
|
||||||
run(cmd, returnOut) -> exitCode, stdout, stderr > Runs `cmd` in Hilbish's sh interpreter.
|
|
||||||
If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
|
|
||||||
3rd values instead of being outputted to the terminal.
|
|
||||||
|
|
||||||
runnerMode(mode) > Sets the execution/runner mode for interactive Hilbish. This determines whether
|
|
||||||
Hilbish wll try to run input as Lua and/or sh or only do one of either.
|
|
||||||
Accepted values for mode are hybrid (the default), hybridRev (sh first then Lua),
|
|
||||||
sh, and lua. It also accepts a function, to which if it is passed one
|
|
||||||
will call it to execute user input instead.
|
|
||||||
|
|
||||||
timeout(cb, time) > Runs the `cb` function after `time` in milliseconds
|
|
||||||
Returns a `timer` object (see `doc timers`).
|
|
||||||
|
|
||||||
which(name) > Checks if `name` is a valid command
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
title: Signals
|
||||||
|
description:
|
||||||
|
layout: doc
|
||||||
|
weight: -50
|
||||||
|
menu:
|
||||||
|
docs
|
||||||
|
---
|
||||||
|
|
||||||
|
Signals are global events emitted with the [Bait](../api/bait) module.
|
||||||
|
For more detail on how to use these signals, you may check the Bait page.
|
|
@ -0,0 +1,67 @@
|
||||||
|
---
|
||||||
|
title: Command
|
||||||
|
description:
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "Signals"
|
||||||
|
---
|
||||||
|
|
||||||
|
## command.preexec
|
||||||
|
Thrown right before a command is executed.
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
`string` **`input`**
|
||||||
|
The raw string that the user typed. This will include the text
|
||||||
|
without changes applied to it (argument substitution, alias expansion,
|
||||||
|
etc.)
|
||||||
|
|
||||||
|
`string` **`cmdStr`**
|
||||||
|
The command that will be directly executed by the current runner.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
## command.exit
|
||||||
|
Thrown after the user's ran command is finished.
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
`number` **`code`**
|
||||||
|
The exit code of what was executed.
|
||||||
|
|
||||||
|
`string` **`cmdStr`**
|
||||||
|
The command or code that was executed
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
## command.not-found
|
||||||
|
Thrown if the command attempted to execute was not found.
|
||||||
|
This can be used to customize the text printed when a command is not found.
|
||||||
|
Example:
|
||||||
|
```lua
|
||||||
|
local bait = require 'bait'
|
||||||
|
-- Remove any present handlers on `command.not-found`
|
||||||
|
|
||||||
|
local notFoundHooks = bait.hooks 'command.not-found'
|
||||||
|
for _, hook in ipairs(notFoundHooks) do
|
||||||
|
bait.release('command.not-found', hook)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- then assign custom
|
||||||
|
bait.catch('command.not-found', function(cmd)
|
||||||
|
print(string.format('The command "%s" was not found.', cmd))
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
`string` **`cmdStr`**
|
||||||
|
The name of the command.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
## command.not-executable
|
||||||
|
Thrown when the user attempts to run a file that is not executable
|
||||||
|
(like a text file, or Unix binary without +x permission).
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
`string` **`cmdStr`**
|
||||||
|
The name of the command.
|
|
@ -1,7 +0,0 @@
|
||||||
+ `command.exit` -> code, cmdStr > Thrown when a command exits.
|
|
||||||
`code` is the exit code of the command, and `cmdStr` is the command that was run.
|
|
||||||
|
|
||||||
+ `command.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.
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
---
|
||||||
|
title: Hilbish
|
||||||
|
description:
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "Signals"
|
||||||
|
---
|
||||||
|
|
||||||
|
## hilbish.exit
|
||||||
|
Sent when Hilbish is going to exit.
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
This signal returns no variables.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
## hilbish.vimMode
|
||||||
|
Sent when the Vim mode of Hilbish is changed (like from insert to normal mode).
|
||||||
|
This can be used to change the prompt and notify based on Vim mode.
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
`string` **`modeName`**
|
||||||
|
The mode that has been set.
|
||||||
|
Can be these values: `insert`, `normal`, `delete` or `replace`
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
## hilbish.cancel
|
||||||
|
Sent when the user cancels their command input with Ctrl-C
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
This signal returns no variables.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
## hilbish.notification
|
||||||
|
Thrown when a [notification](../../features/notifications) is sent.
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
`table` **`notification`**
|
||||||
|
The notification. The properties are defined in the link above.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
+ `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.
|
|
@ -1,7 +0,0 @@
|
||||||
+ `hilbish.exit` > Sent when Hilbish is about to exit.
|
|
||||||
|
|
||||||
+ `hilbish.vimMode` -> modeName > Sent when Hilbish's Vim mode is changed (example insert to normal mode),
|
|
||||||
`modeName` is the name of the mode changed to (can be `insert`, `normal`, `delete` or `replace`).
|
|
||||||
|
|
||||||
+ `hilbish.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.
|
|
|
@ -1,8 +0,0 @@
|
||||||
Here is a list of bait hooks that are thrown by Hilbish. If a hook is related
|
|
||||||
to a command, it will have the `command` scope, as example.
|
|
||||||
|
|
||||||
Here is the format for a doc for a hook:
|
|
||||||
+ <hook name> -> <args> > <description>
|
|
||||||
|
|
||||||
`<args>` just means the arguments of the hook. If a hook doc has the format
|
|
||||||
of `arg...`, it means the hook can take/recieve any number of `arg`.
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
title: Signal
|
||||||
|
description:
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "Signals"
|
||||||
|
---
|
||||||
|
|
||||||
|
## signal.sigint
|
||||||
|
Thrown when Hilbish receive the SIGINT signal,
|
||||||
|
aka when Ctrl-C is pressed.
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
This signal returns no variables.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
## signal.resize
|
||||||
|
Thrown when the terminal is resized.
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
This signal returns no variables.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
## signal.sigusr1
|
||||||
|
Thrown when SIGUSR1 is sent to Hilbish.
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
This signal returns no variables.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
## signal.sigusr2
|
||||||
|
Thrown when SIGUSR2 is sent to Hilbish.
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
This signal returns no variables.
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
+ `signal.sigint` > Sent when Hilbish receives SIGINT (on Ctrl-C).
|
|
||||||
|
|
||||||
+ `signal.resize` > Sent when the terminal is resized.
|
|
||||||
|
|
||||||
+ `signal.sigusr1`
|
|
||||||
|
|
||||||
+ `signal.sigusr2`
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
(This has mainly been replaced by [hilbish.jobs](../api/hilbish.jobs)).
|
||||||
|
|
||||||
Hilbish has pretty standard job control. It's missing one or two things,
|
Hilbish has pretty standard job control. It's missing one or two things,
|
||||||
but works well. One thing which is different from other shells
|
but works well. One thing which is different from other shells
|
||||||
(besides Hilbish) itself is the API for jobs, and of course it's in Lua.
|
(besides Hilbish) itself is the API for jobs, and of course it's in Lua.
|
|
@ -1,3 +1,10 @@
|
||||||
|
---
|
||||||
|
title: Lunacolors
|
||||||
|
layout: doc
|
||||||
|
weight: -60
|
||||||
|
menu: docs
|
||||||
|
---
|
||||||
|
|
||||||
Lunacolors is an ANSI color/styling library for Lua. It is included
|
Lunacolors is an ANSI color/styling library for Lua. It is included
|
||||||
by default in standard Hilbish distributions to provide easy styling
|
by default in standard Hilbish distributions to provide easy styling
|
||||||
for things like prompts and text.
|
for things like prompts and text.
|
|
@ -1,10 +1,17 @@
|
||||||
|
---
|
||||||
|
title: Nature
|
||||||
|
layout: doc
|
||||||
|
weight: -90
|
||||||
|
menu: docs
|
||||||
|
---
|
||||||
|
|
||||||
A bit after creation, we have the outside nature. Little plants, seeds,
|
A bit after creation, we have the outside nature. Little plants, seeds,
|
||||||
growing to their final phase: a full plant. A lot of Hilbish itself is
|
growing to their final phase: a full plant. A lot of Hilbish itself is
|
||||||
written in Go, but there are parts made in Lua, being most builtins
|
written in Go, but there are parts made in Lua, being most builtins
|
||||||
(`doc`, `cd`, cdr), completions, and other things.
|
(`doc`, `cd`, cdr), completions, and other things.
|
||||||
|
|
||||||
Hilbish's Lua core module is called `nature`. It's handled after everything
|
Hilbish's Lua core module is called `nature`.
|
||||||
on the Go side initializes, which is what that first sentence was from.
|
It runs after Hilbish's Go core does.
|
||||||
|
|
||||||
# Nature Modules
|
# Nature Modules
|
||||||
Currently, `nature` provides 1 intended public module: `nature.dirs`.
|
Currently, `nature` provides 1 intended public module: `nature.dirs`.
|
|
@ -0,0 +1,79 @@
|
||||||
|
---
|
||||||
|
title: Module dirs
|
||||||
|
description: No description.
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "Nature"
|
||||||
|
---
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='setOld'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
dirs.setOld(d)
|
||||||
|
<a href="#setOld" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Sets the old directory string.
|
||||||
|
#### Parameters
|
||||||
|
`d` **`string`**
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='push'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
dirs.push()
|
||||||
|
<a href="#push" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Add `d` to the recent directories list.
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='peak'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
dirs.peak(num)
|
||||||
|
<a href="#peak" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Look at `num` amount of recent directories, starting from the latest.
|
||||||
|
#### Parameters
|
||||||
|
`num` **`number`**
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='pop'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
dirs.pop(num)
|
||||||
|
<a href="#pop" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Remove the specified amount of dirs from the recent directories list.
|
||||||
|
#### Parameters
|
||||||
|
`num` **`number`**
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='recent'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
dirs.recent(idx)
|
||||||
|
<a href="#recent" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Get entry from recent directories list based on index.
|
||||||
|
#### Parameters
|
||||||
|
`idx` **`number`**
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
Hilbish is *unique,* when interactive it first attempts to run input as
|
|
||||||
Lua and then tries shell script. But if you're normal, you wouldn't
|
|
||||||
really be using Hilbish anyway but you'd also not want this
|
|
||||||
(or maybe want Lua only in some cases.)
|
|
||||||
|
|
||||||
The "runner mode" of Hilbish is customizable via `hilbish.runnerMode`,
|
|
||||||
which determines how Hilbish will run user input. By default, this is
|
|
||||||
set to `hybrid` which is the previously mentioned behaviour of running Lua
|
|
||||||
first then going to shell script. If you want the reverse order, you can
|
|
||||||
set it to `hybridRev` and for isolated modes there is `sh` and `lua`
|
|
||||||
respectively.
|
|
||||||
|
|
||||||
You can also set it to a function, which will be called everytime Hilbish
|
|
||||||
needs to run interactive input. For example, you can set this to a simple
|
|
||||||
function to compile and evaluate Fennel, and now you can run Fennel.
|
|
||||||
You can even mix it with sh to make a hybrid mode with Lua replaced by
|
|
||||||
Fennel.
|
|
||||||
|
|
||||||
An example:
|
|
||||||
hilbish.runnerMode(function(input)
|
|
||||||
local ok = pcall(fennel.eval, input)
|
|
||||||
if ok then
|
|
||||||
return input, 0, nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return hilbish.runner.sh(input)
|
|
||||||
end)
|
|
||||||
|
|
||||||
The `hilbish.runner` interface is an alternative to using `hilbish.runnerMode`
|
|
||||||
and also provides the sh and Lua runner functions that Hilbish itself uses.
|
|
||||||
A runner function is expected to return 3 values: the input, exit code, and an error.
|
|
||||||
The input return is there incase you need to prompt for more input.
|
|
||||||
If you don't, just return the input passed to the runner function.
|
|
||||||
The exit code has to be a number, it will be 0 otherwise and the error can be
|
|
||||||
`nil` to indicate no error.
|
|
||||||
|
|
||||||
## Functions
|
|
||||||
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
|
|
||||||
|
|
||||||
The others here are defined in Lua and have EmmyLua documentation.
|
|
||||||
These functions should be preferred over the previous ones.
|
|
||||||
+ setCurrent(mode) > The same as `setMode`, but works with runners managed
|
|
||||||
via the functions below.
|
|
||||||
+ add(name, runner) > Adds a runner to a table of available runners. The `runner`
|
|
||||||
argument is either a function or a table with a run callback.
|
|
||||||
+ set(name, runner) > The same as `add` but requires passing a table and
|
|
||||||
overwrites if the `name`d runner already exists.
|
|
||||||
+ get(name) > runner > Gets a runner by name. It is a table with at least a
|
|
||||||
run function, to run input.
|
|
||||||
+ exec(cmd, runnerName) > Runs `cmd` with a runner. If `runnerName` isn't passed,
|
|
||||||
the current runner mode is used.
|
|
|
@ -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
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
This has been moved to the `hilbish.timers` API doc (accessible by `doc api hilbish.timers`)
|
|
@ -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
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
---
|
||||||
|
title: Vim Mode
|
||||||
|
layout: doc
|
||||||
|
weight: -90
|
||||||
|
menu: docs
|
||||||
|
---
|
||||||
|
|
||||||
Hilbish has a Vim binding input mode accessible for use.
|
Hilbish has a Vim binding input mode accessible for use.
|
||||||
It can be enabled with the `hilbish.inputMode` function (check `doc hilbish`).
|
It can be enabled with the `hilbish.inputMode` function (check `doc hilbish`).
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
---
|
||||||
|
title: Actions
|
||||||
|
layout: doc
|
||||||
|
weight: -80
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "Vim Mode"
|
||||||
|
---
|
||||||
|
|
||||||
Vim actions are essentially just when a user uses a Vim keybind.
|
Vim actions are essentially just when a user uses a Vim keybind.
|
||||||
Things like yanking and pasting are Vim actions.
|
Things like yanking and pasting are Vim actions.
|
||||||
This is not an "offical Vim thing," just a Hilbish thing.
|
This is not an "offical Vim thing," just a Hilbish thing.
|
31
editor.go
31
editor.go
|
@ -6,12 +6,17 @@ import (
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// #interface editor
|
||||||
|
// interactions for Hilbish's line reader
|
||||||
|
// The hilbish.editor interface provides functions to
|
||||||
|
// directly interact with the line editor in use.
|
||||||
func editorLoader(rtm *rt.Runtime) *rt.Table {
|
func editorLoader(rtm *rt.Runtime) *rt.Table {
|
||||||
exports := map[string]util.LuaExport{
|
exports := map[string]util.LuaExport{
|
||||||
"insert": {editorInsert, 1, false},
|
"insert": {editorInsert, 1, false},
|
||||||
"setVimRegister": {editorSetRegister, 1, false},
|
"setVimRegister": {editorSetRegister, 1, false},
|
||||||
"getVimRegister": {editorGetRegister, 2, false},
|
"getVimRegister": {editorGetRegister, 2, false},
|
||||||
"getLine": {editorGetLine, 0, false},
|
"getLine": {editorGetLine, 0, false},
|
||||||
|
"readChar": {editorReadChar, 0, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
mod := rt.NewTable()
|
mod := rt.NewTable()
|
||||||
|
@ -20,6 +25,10 @@ func editorLoader(rtm *rt.Runtime) *rt.Table {
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface editor
|
||||||
|
// insert(text)
|
||||||
|
// Inserts text into the Hilbish command line.
|
||||||
|
// #param text string
|
||||||
func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -35,6 +44,11 @@ func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface editor
|
||||||
|
// setVimRegister(register, text)
|
||||||
|
// Sets the vim register at `register` to hold the passed text.
|
||||||
|
// #aram register string
|
||||||
|
// #param text string
|
||||||
func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -55,6 +69,10 @@ func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface editor
|
||||||
|
// getVimRegister(register) -> string
|
||||||
|
// Returns the text that is at the register.
|
||||||
|
// #param register string
|
||||||
func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -70,8 +88,21 @@ func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
|
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface editor
|
||||||
|
// getLine() -> string
|
||||||
|
// Returns the current input line.
|
||||||
|
// #returns string
|
||||||
func editorGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func editorGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
buf := lr.rl.GetLine()
|
buf := lr.rl.GetLine()
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
|
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface editor
|
||||||
|
// getChar() -> string
|
||||||
|
// Reads a keystroke from the user. This is in a format of something like Ctrl-L.
|
||||||
|
func editorReadChar(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
buf := lr.rl.ReadChar()
|
||||||
|
|
||||||
|
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
|
||||||
|
}
|
||||||
|
|
|
@ -2,24 +2,27 @@
|
||||||
|
|
||||||
local bait = {}
|
local bait = {}
|
||||||
|
|
||||||
--- Catches a hook with `name`. Runs the `cb` when it is thrown
|
--- Catches an event. This function can be used to act on events.
|
||||||
--- @param name string
|
---
|
||||||
--- @param cb function
|
---
|
||||||
function bait.catch(name, cb) end
|
function bait.catch(name, cb) end
|
||||||
|
|
||||||
--- Same as catch, but only runs the `cb` once and then removes the hook
|
--- Catches an event, but only once. This will remove the hook immediately after it runs for the first time.
|
||||||
--- @param name string
|
|
||||||
--- @param cb function
|
|
||||||
function bait.catchOnce(name, cb) end
|
function bait.catchOnce(name, cb) end
|
||||||
|
|
||||||
--- Removes the `catcher` for the event with `name`
|
--- Returns a table of functions that are hooked on an event with the corresponding `name`.
|
||||||
|
function bait.hooks(name) end
|
||||||
|
|
||||||
|
--- Removes the `catcher` for the event with `name`.
|
||||||
--- For this to work, `catcher` has to be the same function used to catch
|
--- For this to work, `catcher` has to be the same function used to catch
|
||||||
--- an event, like one saved to a variable.
|
--- an event, like one saved to a variable.
|
||||||
function bait.release() end
|
---
|
||||||
|
---
|
||||||
|
function bait.release(name, catcher) end
|
||||||
|
|
||||||
--- Throws a hook with `name` with the provided `args`
|
--- Throws a hook with `name` with the provided `args`.
|
||||||
--- @param name string
|
---
|
||||||
--- @vararg any
|
---
|
||||||
function bait.throw(name, ...) end
|
function bait.throw(name, ...args) end
|
||||||
|
|
||||||
return bait
|
return bait
|
||||||
|
|
|
@ -2,13 +2,17 @@
|
||||||
|
|
||||||
local commander = {}
|
local commander = {}
|
||||||
|
|
||||||
--- Deregisters any command registered with `name`
|
--- Removes the named command. Note that this will only remove Commander-registered commands.
|
||||||
--- @param name string
|
|
||||||
function commander.deregister(name) end
|
function commander.deregister(name) end
|
||||||
|
|
||||||
--- Register a command with `name` that runs `cb` when ran
|
--- Adds a new command with the given `name`. When Hilbish has to run a command with a name,
|
||||||
--- @param name string
|
--- it will run the function providing the arguments and sinks.
|
||||||
--- @param cb function
|
---
|
||||||
|
---
|
||||||
function commander.register(name, cb) end
|
function commander.register(name, cb) end
|
||||||
|
|
||||||
|
--- Returns all registered commanders. Returns a list of tables with the following keys:
|
||||||
|
--- - `exec`: The function used to run the commander. Commanders require args and sinks to be passed.
|
||||||
|
function commander.registry() end
|
||||||
|
|
||||||
return commander
|
return commander
|
||||||
|
|
|
@ -2,42 +2,53 @@
|
||||||
|
|
||||||
local fs = {}
|
local fs = {}
|
||||||
|
|
||||||
--- Gives an absolute version of `path`.
|
--- Returns an absolute version of the `path`.
|
||||||
--- @param path string
|
--- This can be used to resolve short paths like `..` to `/home/user`.
|
||||||
function fs.abs(path) end
|
function fs.abs(path) end
|
||||||
|
|
||||||
--- Gives the basename of `path`. For the rules,
|
--- Returns the "basename," or the last part of the provided `path`. If path is empty,
|
||||||
--- see Go's filepath.Base
|
--- `.` will be returned.
|
||||||
function fs.basename() end
|
function fs.basename(path) end
|
||||||
|
|
||||||
--- Changes directory to `dir`
|
--- Changes Hilbish's directory to `dir`.
|
||||||
--- @param dir string
|
|
||||||
function fs.cd(dir) end
|
function fs.cd(dir) end
|
||||||
|
|
||||||
--- Returns the directory part of `path`. For the rules, see Go's
|
--- Returns the directory part of `path`. If a file path like
|
||||||
--- filepath.Dir
|
--- `~/Documents/doc.txt` then this function will return `~/Documents`.
|
||||||
function fs.dir() end
|
function fs.dir(path) end
|
||||||
|
|
||||||
--- Glob all files and directories that match the pattern.
|
--- Match all files based on the provided `pattern`.
|
||||||
--- For the rules, see Go's filepath.Glob
|
--- For the syntax' refer to Go's filepath.Match function: https://pkg.go.dev/path/filepath#Match
|
||||||
function fs.glob() end
|
---
|
||||||
|
---
|
||||||
|
function fs.glob(pattern) end
|
||||||
|
|
||||||
--- Takes paths and joins them together with the OS's
|
--- Takes any list of paths and joins them based on the operating system's path separator.
|
||||||
--- directory separator (forward or backward slash).
|
---
|
||||||
function fs.join() end
|
---
|
||||||
|
function fs.join(...path) end
|
||||||
|
|
||||||
--- Makes a directory called `name`. If `recursive` is true, it will create its parent directories.
|
--- Creates a new directory with the provided `name`.
|
||||||
--- @param name string
|
--- With `recursive`, mkdir will create parent directories.
|
||||||
--- @param recursive boolean
|
---
|
||||||
|
---
|
||||||
function fs.mkdir(name, recursive) end
|
function fs.mkdir(name, recursive) end
|
||||||
|
|
||||||
--- Returns a table of files in `dir`
|
--- Returns a pair of connected files, also known as a pipe.
|
||||||
--- @param dir string
|
--- The type returned is a Lua file, same as returned from `io` functions.
|
||||||
--- @return table
|
function fs.fpipe() end
|
||||||
function fs.readdir(dir) end
|
|
||||||
|
|
||||||
--- Returns info about `path`
|
--- Returns a list of all files and directories in the provided path.
|
||||||
--- @param path string
|
function fs.readdir(path) end
|
||||||
|
|
||||||
|
--- Returns the information about a given `path`.
|
||||||
|
--- The returned table contains the following values:
|
||||||
|
--- name (string) - Name of the path
|
||||||
|
--- size (number) - Size of the path in bytes
|
||||||
|
--- mode (string) - Unix permission mode in an octal format string (with leading 0)
|
||||||
|
--- isDir (boolean) - If the path is a directory
|
||||||
|
---
|
||||||
|
---
|
||||||
function fs.stat(path) end
|
function fs.stat(path) end
|
||||||
|
|
||||||
return fs
|
return fs
|
||||||
|
|
|
@ -2,40 +2,92 @@
|
||||||
|
|
||||||
local hilbish = {}
|
local hilbish = {}
|
||||||
|
|
||||||
--- Sets an alias of `cmd` to `orig`
|
--- This is an alias (ha) for the [hilbish.alias](../#alias) function.
|
||||||
|
--- @param alias string
|
||||||
--- @param cmd string
|
--- @param cmd string
|
||||||
--- @param orig 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.
|
||||||
|
function hilbish.runner.setMode(cb) end
|
||||||
|
|
||||||
|
--- Returns the current input line.
|
||||||
|
function hilbish.editor.getLine() end
|
||||||
|
|
||||||
|
--- Returns the text that is at the register.
|
||||||
|
function hilbish.editor.getVimRegister(register) end
|
||||||
|
|
||||||
|
--- Inserts text into the Hilbish command line.
|
||||||
|
function hilbish.editor.insert(text) end
|
||||||
|
|
||||||
|
--- Reads a keystroke from the user. This is in a format of something like Ctrl-L.
|
||||||
|
function hilbish.editor.getChar() end
|
||||||
|
|
||||||
|
--- Sets the vim register at `register` to hold the passed text.
|
||||||
|
function hilbish.editor.setVimRegister(register, text) end
|
||||||
|
|
||||||
|
--- Return binaries/executables based on the provided parameters.
|
||||||
|
--- This function is meant to be used as a helper in a command completion handler.
|
||||||
|
---
|
||||||
|
---
|
||||||
|
function hilbish.completion.bins(query, ctx, fields) end
|
||||||
|
|
||||||
|
--- Calls a completer function. This is mainly used to call a command completer, which will have a `name`
|
||||||
|
--- in the form of `command.name`, example: `command.git`.
|
||||||
|
--- You can check the Completions doc or `doc completions` for info on the `completionGroups` return value.
|
||||||
|
function hilbish.completion.call(name, query, ctx, fields) end
|
||||||
|
|
||||||
|
--- Returns file matches based on the provided parameters.
|
||||||
|
--- This function is meant to be used as a helper in a command completion handler.
|
||||||
|
function hilbish.completion.files(query, ctx, fields) end
|
||||||
|
|
||||||
|
--- This function contains the general completion handler for Hilbish. This function handles
|
||||||
|
--- completion of everything, which includes calling other command handlers, binaries, and files.
|
||||||
|
--- This function can be overriden to supply a custom handler. Note that alias resolution is required to be done in this function.
|
||||||
|
---
|
||||||
|
---
|
||||||
|
function hilbish.completion.handler(line, pos) end
|
||||||
|
|
||||||
|
--- Sets an alias, with a name of `cmd` to another command.
|
||||||
|
---
|
||||||
|
---
|
||||||
function hilbish.alias(cmd, orig) end
|
function hilbish.alias(cmd, orig) end
|
||||||
|
|
||||||
--- Appends `dir` to $PATH
|
--- Appends the provided dir to the command path (`$PATH`)
|
||||||
--- @param dir string|table
|
---
|
||||||
|
---
|
||||||
function hilbish.appendPath(dir) end
|
function hilbish.appendPath(dir) end
|
||||||
|
|
||||||
--- Registers a completion handler for `scope`.
|
--- Registers a completion handler for the specified scope.
|
||||||
--- A `scope` is currently only expected to be `command.<cmd>`,
|
--- A `scope` is expected to be `command.<cmd>`,
|
||||||
--- replacing <cmd> with the name of the command (for example `command.git`).
|
--- replacing <cmd> with the name of the command (for example `command.git`).
|
||||||
--- `cb` must be a function that returns a table of "completion groups."
|
--- The documentation for completions, under Features/Completions or `doc completions`
|
||||||
--- Check `doc completions` for more information.
|
--- provides more details.
|
||||||
--- @param scope string
|
---
|
||||||
--- @param cb function
|
---
|
||||||
function hilbish.complete(scope, cb) end
|
function hilbish.complete(scope, cb) end
|
||||||
|
|
||||||
--- Returns the current directory of the shell
|
--- Returns the current directory of the shell.
|
||||||
function hilbish.cwd() end
|
function hilbish.cwd() end
|
||||||
|
|
||||||
--- Replaces running hilbish with `cmd`
|
--- Replaces the currently running Hilbish instance with the supplied command.
|
||||||
--- @param cmd string
|
--- This can be used to do an in-place restart.
|
||||||
function hilbish.exec(cmd) end
|
function hilbish.exec(cmd) end
|
||||||
|
|
||||||
--- Puts `fn` in a goroutine
|
--- Puts `fn` in a Goroutine.
|
||||||
--- @param fn function
|
--- This can be used to run any function in another thread at the same time as other Lua code.
|
||||||
|
--- **NOTE: THIS FUNCTION MAY CRASH HILBISH IF OUTSIDE VARIABLES ARE ACCESSED.**
|
||||||
|
--- **This is a limitation of the Lua runtime.**
|
||||||
function hilbish.goro(fn) end
|
function hilbish.goro(fn) end
|
||||||
|
|
||||||
--- Line highlighter handler. This is mainly for syntax highlighting, but in
|
--- Line highlighter handler.
|
||||||
--- reality could set the input of the prompt to *display* anything. The
|
--- This is mainly for syntax highlighting, but in reality could set the input
|
||||||
--- callback is passed the current line and is expected to return a line that
|
--- of the prompt to *display* anything. The callback is passed the current line
|
||||||
--- will be used as the input display.
|
--- and is expected to return a line that will be used as the input display.
|
||||||
--- @param line string
|
--- Note that to set a highlighter, one has to override this function.
|
||||||
|
---
|
||||||
function hilbish.highlighter(line) end
|
function hilbish.highlighter(line) end
|
||||||
|
|
||||||
--- The command line hint handler. It gets called on every key insert to
|
--- The command line hint handler. It gets called on every key insert to
|
||||||
|
@ -43,68 +95,171 @@ function hilbish.highlighter(line) end
|
||||||
--- line and cursor position. It is expected to return a string which is used
|
--- 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,
|
--- as the text for the hint. This is by default a shim. To set hints,
|
||||||
--- override this function with your custom handler.
|
--- override this function with your custom handler.
|
||||||
--- @param line string
|
---
|
||||||
--- @param pos int
|
---
|
||||||
function hilbish.hinter(line, pos) end
|
function hilbish.hinter(line, pos) end
|
||||||
|
|
||||||
--- Sets the input mode for Hilbish's line reader. Accepts either emacs or vim
|
--- Sets the input mode for Hilbish's line reader.
|
||||||
--- @param mode string
|
--- `emacs` is the default. Setting it to `vim` changes behavior of input to be
|
||||||
|
--- Vim-like with modes and Vim keybinds.
|
||||||
function hilbish.inputMode(mode) end
|
function hilbish.inputMode(mode) end
|
||||||
|
|
||||||
--- Runs the `cb` function every `time` milliseconds.
|
--- Runs the `cb` function every specified amount of `time`.
|
||||||
--- Returns a `timer` object (see `doc timers`).
|
--- This creates a timer that ticking immediately.
|
||||||
--- @param cb function
|
|
||||||
--- @param time number
|
|
||||||
--- @return table
|
|
||||||
function hilbish.interval(cb, time) end
|
function hilbish.interval(cb, time) end
|
||||||
|
|
||||||
--- Changes the continued line prompt to `str`
|
--- Changes the text prompt when Hilbish asks for more input.
|
||||||
--- @param str string
|
--- This will show up when text is incomplete, like a missing quote
|
||||||
|
---
|
||||||
|
---
|
||||||
function hilbish.multiprompt(str) end
|
function hilbish.multiprompt(str) end
|
||||||
|
|
||||||
--- Prepends `dir` to $PATH
|
--- Prepends `dir` to $PATH.
|
||||||
--- @param dir string
|
|
||||||
function hilbish.prependPath(dir) end
|
function hilbish.prependPath(dir) end
|
||||||
|
|
||||||
--- Changes the shell prompt to `str`
|
--- Changes the shell prompt to the provided string.
|
||||||
--- There are a few verbs that can be used in the prompt text.
|
--- There are a few verbs that can be used in the prompt text.
|
||||||
--- These will be formatted and replaced with the appropriate values.
|
--- These will be formatted and replaced with the appropriate values.
|
||||||
--- `%d` - Current working directory
|
--- `%d` - Current working directory
|
||||||
--- `%u` - Name of current user
|
--- `%u` - Name of current user
|
||||||
--- `%h` - Hostname of device
|
--- `%h` - Hostname of device
|
||||||
--- @param str string
|
---
|
||||||
--- @param typ string Type of prompt, being left or right. Left by default.
|
|
||||||
function hilbish.prompt(str, typ) end
|
function hilbish.prompt(str, typ) end
|
||||||
|
|
||||||
--- Read input from the user, using Hilbish's line editor/input reader.
|
--- Read input from the user, using Hilbish's line editor/input reader.
|
||||||
--- This is a separate instance from the one Hilbish actually uses.
|
--- This is a separate instance from the one Hilbish actually uses.
|
||||||
--- Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
|
--- Returns `input`, will be nil if Ctrl-D is pressed, or an error occurs.
|
||||||
--- @param prompt string
|
|
||||||
function hilbish.read(prompt) end
|
function hilbish.read(prompt) end
|
||||||
|
|
||||||
--- Runs `cmd` in Hilbish's sh interpreter.
|
--- Runs `cmd` in Hilbish's shell script interpreter.
|
||||||
--- If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
|
--- The `streams` parameter specifies the output and input streams the command should use.
|
||||||
--- 3rd values instead of being outputted to the terminal.
|
--- For example, to write command output to a sink.
|
||||||
--- @param cmd string
|
--- As a table, the caller can directly specify the standard output, error, and input
|
||||||
function hilbish.run(cmd) end
|
--- streams of the command with the table keys `out`, `err`, and `input` respectively.
|
||||||
|
--- As a boolean, it specifies whether the command should use standard output or return its output streams.
|
||||||
|
---
|
||||||
|
function hilbish.run(cmd, streams) end
|
||||||
|
|
||||||
--- Sets the execution/runner mode for interactive Hilbish. This determines whether
|
--- Sets the execution/runner mode for interactive Hilbish.
|
||||||
--- Hilbish wll try to run input as Lua and/or sh or only do one of either.
|
--- 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),
|
--- 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
|
--- sh, and lua. It also accepts a function, to which if it is passed one
|
||||||
--- will call it to execute user input instead.
|
--- will call it to execute user input instead.
|
||||||
--- @param mode string|function
|
--- Read [about runner mode](../features/runner-mode) for more information.
|
||||||
function hilbish.runnerMode(mode) end
|
function hilbish.runnerMode(mode) end
|
||||||
|
|
||||||
--- Runs the `cb` function after `time` in milliseconds
|
--- Executed the `cb` function after a period of `time`.
|
||||||
--- Returns a `timer` object (see `doc timers`).
|
--- This creates a Timer that starts ticking immediately.
|
||||||
--- @param cb function
|
|
||||||
--- @param time number
|
|
||||||
--- @return table
|
|
||||||
function hilbish.timeout(cb, time) end
|
function hilbish.timeout(cb, time) end
|
||||||
|
|
||||||
--- Checks if `name` is a valid command
|
--- Checks if `name` is a valid command.
|
||||||
--- @param binName string
|
--- Will return the path of the binary, or a basename if it's a commander.
|
||||||
function hilbish.which(binName) end
|
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
|
||||||
|
|
||||||
|
--- 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.
|
||||||
|
function hilbish.runner.lua(cmd) end
|
||||||
|
|
||||||
|
--- Sets/toggles the option of automatically flushing output.
|
||||||
|
--- A call with no argument will toggle the value.
|
||||||
|
--- @param auto boolean|nil
|
||||||
|
function hilbish:autoFlush(auto) end
|
||||||
|
|
||||||
|
--- Flush writes all buffered input to the sink.
|
||||||
|
function hilbish:flush() end
|
||||||
|
|
||||||
|
--- Reads a liine of input from the sink.
|
||||||
|
--- @returns string
|
||||||
|
function hilbish:read() end
|
||||||
|
|
||||||
|
--- Reads all input from the sink.
|
||||||
|
--- @returns string
|
||||||
|
function hilbish:readAll() 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
|
||||||
|
|
||||||
|
--- Loads a module at the designated `path`.
|
||||||
|
--- It will throw if any error occurs.
|
||||||
|
function hilbish.module.load(path) end
|
||||||
|
|
||||||
|
--- Runs a command in Hilbish's shell script interpreter.
|
||||||
|
--- This is the equivalent of using `source`.
|
||||||
|
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.
|
||||||
|
function hilbish.aliases.delete(name) end
|
||||||
|
|
||||||
|
--- Get a table of all aliases, with string keys as the alias and the value as the command.
|
||||||
|
---
|
||||||
|
---
|
||||||
|
function hilbish.aliases.list() end
|
||||||
|
|
||||||
|
--- Resolves an alias to its original command. Will thrown an error if the alias doesn't exist.
|
||||||
|
function hilbish.aliases.resolve(alias) end
|
||||||
|
|
||||||
|
--- Creates a new job. This function does not run the job. This function is intended to be
|
||||||
|
--- used by runners, but can also be used to create jobs via Lua. Commanders cannot be ran as jobs.
|
||||||
|
---
|
||||||
|
---
|
||||||
|
function hilbish.jobs.add(cmdstr, args, execPath) end
|
||||||
|
|
||||||
|
--- Returns a table of all job objects.
|
||||||
|
function hilbish.jobs.all() end
|
||||||
|
|
||||||
|
--- Disowns a job. This simply deletes it from the list of jobs without stopping it.
|
||||||
|
function hilbish.jobs.disown(id) end
|
||||||
|
|
||||||
|
--- Get a job object via its ID.
|
||||||
|
--- @param id number
|
||||||
|
--- @returns Job
|
||||||
|
function hilbish.jobs.get(id) end
|
||||||
|
|
||||||
|
--- Returns the last added job to the table.
|
||||||
|
function hilbish.jobs.last() end
|
||||||
|
|
||||||
|
--- Adds a command to the history.
|
||||||
|
function hilbish.history.add(cmd) end
|
||||||
|
|
||||||
|
--- Retrieves all history as a table.
|
||||||
|
function hilbish.history.all() end
|
||||||
|
|
||||||
|
--- Deletes all commands from the history.
|
||||||
|
function hilbish.history.clear() end
|
||||||
|
|
||||||
|
--- Retrieves a command from the history based on the `index`.
|
||||||
|
function hilbish.history.get(index) end
|
||||||
|
|
||||||
|
--- Returns the amount of commands in the history.
|
||||||
|
function hilbish.history.size() end
|
||||||
|
|
||||||
|
--- Creates a timer that runs based on the specified `time`.
|
||||||
|
function hilbish.timers.create(type, time, callback) end
|
||||||
|
|
||||||
|
--- Retrieves a timer via its ID.
|
||||||
|
function hilbish.timers.get(id) end
|
||||||
|
|
||||||
return hilbish
|
return hilbish
|
||||||
|
|
|
@ -5,14 +5,14 @@ local terminal = {}
|
||||||
--- Restores the last saved state of the terminal
|
--- Restores the last saved state of the terminal
|
||||||
function terminal.restoreState() end
|
function terminal.restoreState() end
|
||||||
|
|
||||||
--- Saves the current state of the terminal
|
--- Saves the current state of the terminal.
|
||||||
function terminal.saveState() end
|
function terminal.saveState() end
|
||||||
|
|
||||||
--- Puts the terminal in raw mode
|
--- Puts the terminal into raw mode.
|
||||||
function terminal.setRaw() end
|
function terminal.setRaw() end
|
||||||
|
|
||||||
--- Gets the dimensions of the terminal. Returns a table with `width` and `height`
|
--- Gets the dimensions of the terminal. Returns a table with `width` and `height`
|
||||||
--- Note: this is not the size in relation to the dimensions of the display
|
--- NOTE: The size refers to the amount of columns and rows of text that can fit in the terminal.
|
||||||
function terminal.size() end
|
function terminal.size() end
|
||||||
|
|
||||||
return terminal
|
return terminal
|
||||||
|
|
92
exec.go
92
exec.go
|
@ -29,6 +29,12 @@ var errNotExec = errors.New("not executable")
|
||||||
var errNotFound = errors.New("not found")
|
var errNotFound = errors.New("not found")
|
||||||
var runnerMode rt.Value = rt.StringValue("hybrid")
|
var runnerMode rt.Value = rt.StringValue("hybrid")
|
||||||
|
|
||||||
|
type streams struct {
|
||||||
|
stdout io.Writer
|
||||||
|
stderr io.Writer
|
||||||
|
stdin io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
type execError struct{
|
type execError struct{
|
||||||
typ string
|
typ string
|
||||||
cmd string
|
cmd string
|
||||||
|
@ -97,23 +103,23 @@ func runInput(input string, priv bool) {
|
||||||
if currentRunner.Type() == rt.StringType {
|
if currentRunner.Type() == rt.StringType {
|
||||||
switch currentRunner.AsString() {
|
switch currentRunner.AsString() {
|
||||||
case "hybrid":
|
case "hybrid":
|
||||||
_, _, err = handleLua(cmdString)
|
_, _, err = handleLua(input)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cmdFinish(0, input, priv)
|
cmdFinish(0, input, priv)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
input, exitCode, cont, err = handleSh(cmdString)
|
input, exitCode, cont, err = handleSh(input)
|
||||||
case "hybridRev":
|
case "hybridRev":
|
||||||
_, _, _, err = handleSh(input)
|
_, _, _, err = handleSh(input)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cmdFinish(0, input, priv)
|
cmdFinish(0, input, priv)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
input, exitCode, err = handleLua(cmdString)
|
input, exitCode, err = handleLua(input)
|
||||||
case "lua":
|
case "lua":
|
||||||
input, exitCode, err = handleLua(cmdString)
|
input, exitCode, err = handleLua(input)
|
||||||
case "sh":
|
case "sh":
|
||||||
input, exitCode, cont, err = handleSh(cmdString)
|
input, exitCode, cont, err = handleSh(input)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// can only be a string or function so
|
// can only be a string or function so
|
||||||
|
@ -142,9 +148,9 @@ func runInput(input string, priv bool) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if exErr, ok := isExecError(err); ok {
|
if exErr, ok := isExecError(err); ok {
|
||||||
hooks.Emit("command." + exErr.typ, exErr.cmd)
|
hooks.Emit("command." + exErr.typ, exErr.cmd)
|
||||||
err = exErr.sprint()
|
} else {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
}
|
}
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
}
|
}
|
||||||
cmdFinish(exitCode, input, priv)
|
cmdFinish(exitCode, input, priv)
|
||||||
}
|
}
|
||||||
|
@ -176,6 +182,9 @@ func runLuaRunner(runr rt.Value, userInput string) (input string, exitCode uint8
|
||||||
runnerRet := term.Get(0)
|
runnerRet := term.Get(0)
|
||||||
if runner, ok = runnerRet.TryTable(); !ok {
|
if runner, ok = runnerRet.TryTable(); !ok {
|
||||||
fmt.Fprintln(os.Stderr, "runner did not return a table")
|
fmt.Fprintln(os.Stderr, "runner did not return a table")
|
||||||
|
exitCode = 125
|
||||||
|
input = userInput
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if code, ok := runner.Get(rt.StringValue("exitCode")).TryInt(); ok {
|
if code, ok := runner.Get(rt.StringValue("exitCode")).TryInt(); ok {
|
||||||
|
@ -196,7 +205,8 @@ func runLuaRunner(runr rt.Value, userInput string) (input string, exitCode uint8
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLua(cmdString string) (string, uint8, error) {
|
func handleLua(input string) (string, uint8, error) {
|
||||||
|
cmdString := aliases.Resolve(input)
|
||||||
// First try to load input, essentially compiling to bytecode
|
// First try to load input, essentially compiling to bytecode
|
||||||
chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv()))
|
chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv()))
|
||||||
if err != nil && noexecute {
|
if err != nil && noexecute {
|
||||||
|
@ -233,7 +243,7 @@ func handleSh(cmdString string) (input string, exitCode uint8, cont bool, runErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func execSh(cmdString string) (string, uint8, bool, error) {
|
func execSh(cmdString string) (string, uint8, bool, error) {
|
||||||
_, _, err := execCommand(cmdString, true)
|
_, _, err := execCommand(cmdString, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If input is incomplete, start multiline prompting
|
// If input is incomplete, start multiline prompting
|
||||||
if syntax.IsIncomplete(err) {
|
if syntax.IsIncomplete(err) {
|
||||||
|
@ -254,7 +264,7 @@ func execSh(cmdString string) (string, uint8, bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run command in sh interpreter
|
// Run command in sh interpreter
|
||||||
func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
|
func execCommand(cmd string, strms *streams) (io.Writer, io.Writer, error) {
|
||||||
file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "")
|
file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -262,15 +272,24 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
|
||||||
|
|
||||||
runner, _ := interp.New()
|
runner, _ := interp.New()
|
||||||
|
|
||||||
var stdout io.Writer
|
if strms == nil {
|
||||||
var stderr io.Writer
|
strms = &streams{}
|
||||||
if terminalOut {
|
|
||||||
interp.StdIO(os.Stdin, os.Stdout, os.Stderr)(runner)
|
|
||||||
} else {
|
|
||||||
stdout = new(bytes.Buffer)
|
|
||||||
stderr = new(bytes.Buffer)
|
|
||||||
interp.StdIO(os.Stdin, stdout, stderr)(runner)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strms.stdout == nil {
|
||||||
|
strms.stdout = os.Stdout
|
||||||
|
}
|
||||||
|
|
||||||
|
if strms.stderr == nil {
|
||||||
|
strms.stderr = os.Stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
if strms.stdin == nil {
|
||||||
|
strms.stdin = os.Stdin
|
||||||
|
}
|
||||||
|
|
||||||
|
interp.StdIO(strms.stdin, strms.stdout, strms.stderr)(runner)
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
printer := syntax.NewPrinter()
|
printer := syntax.NewPrinter()
|
||||||
|
|
||||||
|
@ -289,11 +308,11 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
|
||||||
interp.ExecHandler(execHandle(bg))(runner)
|
interp.ExecHandler(execHandle(bg))(runner)
|
||||||
err = runner.Run(context.TODO(), stmt)
|
err = runner.Run(context.TODO(), stmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return stdout, stderr, err
|
return strms.stdout, strms.stderr, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return stdout, stderr, nil
|
return strms.stdout, strms.stderr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func execHandle(bg bool) interp.ExecHandlerFunc {
|
func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
|
@ -323,15 +342,28 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str))
|
luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str))
|
||||||
}
|
}
|
||||||
|
|
||||||
if commands[args[0]] != nil {
|
hc := interp.HandlerCtx(ctx)
|
||||||
|
if cmd := cmds.Commands[args[0]]; cmd != nil {
|
||||||
|
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("input"), rt.UserDataValue(stdin.ud))
|
||||||
|
sinks.Set(rt.StringValue("out"), rt.UserDataValue(stdout.ud))
|
||||||
|
sinks.Set(rt.StringValue("err"), rt.UserDataValue(stderr.ud))
|
||||||
|
|
||||||
t := rt.NewThread(l)
|
t := rt.NewThread(l)
|
||||||
sig := make(chan os.Signal)
|
sig := make(chan os.Signal)
|
||||||
exit := make(chan bool)
|
exit := make(chan bool)
|
||||||
|
|
||||||
|
luaexitcode := rt.IntValue(63)
|
||||||
|
var err error
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
fmt.Println(r)
|
exit <- true
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -340,13 +372,16 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
case <-sig:
|
case <-sig:
|
||||||
t.KillContext()
|
t.KillContext()
|
||||||
return
|
return
|
||||||
case <-exit:
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
luaexitcode, err := rt.Call1(t, rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs))
|
go func() {
|
||||||
exit <- true
|
luaexitcode, err = rt.Call1(t, rt.FunctionValue(cmd), rt.TableValue(luacmdArgs), rt.TableValue(sinks))
|
||||||
|
exit <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-exit
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error())
|
fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error())
|
||||||
return interp.NewExitStatus(1)
|
return interp.NewExitStatus(1)
|
||||||
|
@ -358,7 +393,7 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
exitcode = uint8(code)
|
exitcode = uint8(code)
|
||||||
} else if luaexitcode != rt.NilValue {
|
} else if luaexitcode != rt.NilValue {
|
||||||
// deregister commander
|
// deregister commander
|
||||||
delete(commands, args[0])
|
delete(cmds.Commands, args[0])
|
||||||
fmt.Fprintf(os.Stderr, "Commander did not return number for exit code. %s, you're fired.\n", args[0])
|
fmt.Fprintf(os.Stderr, "Commander did not return number for exit code. %s, you're fired.\n", args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,7 +421,6 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
killTimeout := 2 * time.Second
|
killTimeout := 2 * time.Second
|
||||||
// from here is basically copy-paste of the default exec handler from
|
// from here is basically copy-paste of the default exec handler from
|
||||||
// sh/interp but with our job handling
|
// sh/interp but with our job handling
|
||||||
hc := interp.HandlerCtx(ctx)
|
|
||||||
path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0])
|
path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(hc.Stderr, err)
|
fmt.Fprintln(hc.Stderr, err)
|
||||||
|
@ -574,7 +608,7 @@ func splitInput(input string) ([]string, string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdFinish(code uint8, cmdstr string, private bool) {
|
func cmdFinish(code uint8, cmdstr string, private bool) {
|
||||||
util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code)), "Exit code of last exected command")
|
util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code)))
|
||||||
// using AsValue (to convert to lua type) on an interface which is an int
|
// using AsValue (to convert to lua type) on an interface which is an int
|
||||||
// results in it being unknown in lua .... ????
|
// results in it being unknown in lua .... ????
|
||||||
// so we allow the hook handler to take lua runtime Values
|
// so we allow the hook handler to take lua runtime Values
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build linux darwin
|
//go:build unix
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build windows
|
//go:build windows
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
28
go.mod
28
go.mod
|
@ -1,26 +1,28 @@
|
||||||
module hilbish
|
module hilbish
|
||||||
|
|
||||||
go 1.17
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/arnodel/golua v0.0.0-20220221163911-dfcf252b6f86
|
github.com/arnodel/golua v0.0.0-20230215163904-e0b5347eaaa1
|
||||||
github.com/blackfireio/osinfo v1.0.3
|
github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504
|
||||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9
|
github.com/blackfireio/osinfo v1.0.5
|
||||||
github.com/maxlandon/readline v0.1.0-beta.0.20211027085530-2b76cabb8036
|
github.com/maxlandon/readline v1.0.14
|
||||||
github.com/pborman/getopt v1.1.0
|
github.com/pborman/getopt v1.1.0
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
|
github.com/sahilm/fuzzy v0.1.1
|
||||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171
|
golang.org/x/sys v0.19.0
|
||||||
mvdan.cc/sh/v3 v3.5.1
|
golang.org/x/term v0.19.0
|
||||||
|
mvdan.cc/sh/v3 v3.8.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
||||||
github.com/arnodel/strftime v0.1.6 // indirect
|
github.com/arnodel/strftime v0.1.6 // indirect
|
||||||
github.com/evilsocket/islazy v1.10.6 // indirect
|
github.com/evilsocket/islazy v1.11.0 // indirect
|
||||||
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect
|
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b
|
replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b
|
||||||
|
@ -29,4 +31,4 @@ replace github.com/maxlandon/readline => ./readline
|
||||||
|
|
||||||
replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10
|
replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10
|
||||||
|
|
||||||
replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3
|
replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20240427174124-d239074c1749
|
||||||
|
|
65
go.sum
65
go.sum
|
@ -1,30 +1,21 @@
|
||||||
github.com/Rosettea/golua v0.0.0-20220419183026-6d22d6fec5ac h1:dtXrgjch8PQyf7C90anZUquB5U3dr8AcMGJofeuirrI=
|
github.com/Rosettea/golua v0.0.0-20240427174124-d239074c1749 h1:jIFnWBTsYw8s7RX7H2AOXjDVhWP3ol7OzUVaPN2KnGI=
|
||||||
github.com/Rosettea/golua v0.0.0-20220419183026-6d22d6fec5ac/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
github.com/Rosettea/golua v0.0.0-20240427174124-d239074c1749/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
||||||
github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3 h1:I/wWr40FFLFF9pbT3wLb1FAEZhKb/hUWE+nJ5uHBK2g=
|
|
||||||
github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
|
||||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e h1:P2XupP8SaylWaudD1DqbWtZ3mIa8OsE9635LmR+Q+lg=
|
|
||||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs=
|
|
||||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b h1:s5eDMhBk6H1BgipgLub/gv9qeyBaTuiHM0k3h2/9TSE=
|
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b h1:s5eDMhBk6H1BgipgLub/gv9qeyBaTuiHM0k3h2/9TSE=
|
||||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs=
|
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs=
|
||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||||
github.com/arnodel/edit v0.0.0-20220202110212-dfc8d7a13890/go.mod h1:AcpttpuZBaL9xl8/CX+Em4fBTUbwIkJ66RiAsJlNrBk=
|
|
||||||
github.com/arnodel/strftime v0.1.6 h1:0hc0pUvk8KhEMXE+htyaOUV42zNcf/csIbjzEFCJqsw=
|
github.com/arnodel/strftime v0.1.6 h1:0hc0pUvk8KhEMXE+htyaOUV42zNcf/csIbjzEFCJqsw=
|
||||||
github.com/arnodel/strftime v0.1.6/go.mod h1:5NbK5XqYK8QpRZpqKNt4OlxLtIB8cotkLk4KTKzJfWs=
|
github.com/arnodel/strftime v0.1.6/go.mod h1:5NbK5XqYK8QpRZpqKNt4OlxLtIB8cotkLk4KTKzJfWs=
|
||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504 h1:R1/AOzdMbopSliUTTEHvHbyNmnZ3YxY5GvdhTkpPsSY=
|
||||||
github.com/blackfireio/osinfo v1.0.3 h1:Yk2t2GTPjBcESv6nDSWZKO87bGMQgO+Hi9OoXPpxX8c=
|
github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504/go.mod h1:kHBCvAXJIatTX1pw6tLiOspjGc3MhUDRlog9yrCUS+k=
|
||||||
github.com/blackfireio/osinfo v1.0.3/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA=
|
github.com/blackfireio/osinfo v1.0.5 h1:6hlaWzfcpb87gRmznVf7wSdhysGqLRz9V/xuSdCEXrA=
|
||||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 h1:xz6Nv3zcwO2Lila35hcb0QloCQsc38Al13RNEzWRpX4=
|
github.com/blackfireio/osinfo v1.0.5/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA=
|
||||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9/go.mod h1:2wSM9zJkl1UQEFZgSd68NfCgRz1VL1jzy/RjCg+ULrs=
|
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc=
|
github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc=
|
||||||
github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
github.com/evilsocket/islazy v1.10.6 h1:MFq000a1ByoumoJWlytqg0qon0KlBeUfPsDjY0hK0bo=
|
github.com/evilsocket/islazy v1.11.0 h1:B5w6uuS6ki6iDG+aH/RFeoMb8ijQh/pGabewqp2UeJ0=
|
||||||
github.com/evilsocket/islazy v1.10.6/go.mod h1:OrwQGYg3DuZvXUfmH+KIZDjwTCbrjy48T24TUpGqVVw=
|
github.com/evilsocket/islazy v1.11.0/go.mod h1:muYH4x5MB5YRdkxnrOtrXLIBX6LySj1uFIqys94LKdo=
|
||||||
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/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
||||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
|
@ -32,44 +23,32 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw=
|
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw=
|
||||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0=
|
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0=
|
||||||
github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0=
|
github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0=
|
||||||
github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk=
|
github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I=
|
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I=
|
||||||
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
|
||||||
|
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4=
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
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=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
|
||||||
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=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
|
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
|
||||||
|
|
|
@ -1,6 +1,33 @@
|
||||||
|
// the event emitter
|
||||||
|
/*
|
||||||
|
Bait is the event emitter for Hilbish. Much like Node.js and
|
||||||
|
its `events` system, many actions in Hilbish emit events.
|
||||||
|
Unlike Node.js, Hilbish events are global. So make sure to
|
||||||
|
pick a unique name!
|
||||||
|
|
||||||
|
Usage of the Bait module consists of userstanding
|
||||||
|
event-driven architecture, but it's pretty simple:
|
||||||
|
If you want to act on a certain event, you can `catch` it.
|
||||||
|
You can act on events via callback functions.
|
||||||
|
|
||||||
|
Examples of this are in the Hilbish default config!
|
||||||
|
Consider this part of it:
|
||||||
|
```lua
|
||||||
|
bait.catch('command.exit', function(code)
|
||||||
|
running = false
|
||||||
|
doPrompt(code ~= 0)
|
||||||
|
doNotifyPrompt()
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
What this does is, whenever the `command.exit` event is thrown,
|
||||||
|
this function will set the user prompt.
|
||||||
|
*/
|
||||||
package bait
|
package bait
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"hilbish/util"
|
"hilbish/util"
|
||||||
|
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
|
@ -72,8 +99,12 @@ func (b *Bait) Emit(event string, args ...interface{}) {
|
||||||
}
|
}
|
||||||
_, err := rt.Call1(b.rtm.MainThread(), funcVal, luaArgs...)
|
_, err := rt.Call1(b.rtm.MainThread(), funcVal, luaArgs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// panicking here won't actually cause hilbish to panic and instead will
|
if event != "error" {
|
||||||
// print the error and remove the hook. reference the recoverer function in lua.go
|
b.Emit("error", event, handle.luaCaller, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// if there is an error in an error event handler, panic instead
|
||||||
|
// (calls the go recoverer function)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -187,19 +218,11 @@ func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
"catchOnce": util.LuaExport{b.bcatchOnce, 2, false},
|
"catchOnce": util.LuaExport{b.bcatchOnce, 2, false},
|
||||||
"throw": util.LuaExport{b.bthrow, 1, true},
|
"throw": util.LuaExport{b.bthrow, 1, true},
|
||||||
"release": util.LuaExport{b.brelease, 2, false},
|
"release": util.LuaExport{b.brelease, 2, false},
|
||||||
|
"hooks": util.LuaExport{b.bhooks, 1, false},
|
||||||
}
|
}
|
||||||
mod := rt.NewTable()
|
mod := rt.NewTable()
|
||||||
util.SetExports(rtm, mod, exports)
|
util.SetExports(rtm, mod, exports)
|
||||||
|
|
||||||
util.Document(mod,
|
|
||||||
`Bait is the event emitter for Hilbish. Why name it bait?
|
|
||||||
Because it throws hooks that you can catch (emits events
|
|
||||||
that you can listen to) and because why not, fun naming
|
|
||||||
is fun. This is what you will use if you want to listen
|
|
||||||
in on hooks to know when certain things have happened,
|
|
||||||
like when you've changed directory, a command has
|
|
||||||
failed, etc. To find all available hooks, see doc hooks.`)
|
|
||||||
|
|
||||||
return rt.TableValue(mod), nil
|
return rt.TableValue(mod), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,10 +247,117 @@ func handleHook(t *rt.Thread, c *rt.GoCont, name string, catcher *rt.Closure, ar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// catch(name, cb)
|
||||||
|
// Catches an event. This function can be used to act on events.
|
||||||
|
// #param name string The name of the hook.
|
||||||
|
// #param cb function The function that will be called when the hook is thrown.
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
bait.catch('hilbish.exit', function()
|
||||||
|
print 'Goodbye Hilbish!'
|
||||||
|
end)
|
||||||
|
#example
|
||||||
|
*/
|
||||||
|
func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
name, catcher, err := util.HandleStrCallback(t, c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.OnLua(name, catcher)
|
||||||
|
|
||||||
|
return c.Next(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// catchOnce(name, cb)
|
||||||
|
// Catches an event, but only once. This will remove the hook immediately after it runs for the first time.
|
||||||
|
// #param name string The name of the event
|
||||||
|
// #param cb function The function that will be called when the event is thrown.
|
||||||
|
func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
name, catcher, err := util.HandleStrCallback(t, c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.OnceLua(name, catcher)
|
||||||
|
|
||||||
|
return c.Next(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hooks(name) -> table
|
||||||
|
// Returns a table of functions that are hooked on an event with the corresponding `name`.
|
||||||
|
// #param name string The name of the hook
|
||||||
|
// #returns table<function>
|
||||||
|
func (b *Bait) bhooks(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
evName, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
noHooks := errors.New("no hooks for event " + evName)
|
||||||
|
|
||||||
|
handlers := b.handlers[evName]
|
||||||
|
if handlers == nil {
|
||||||
|
return nil, noHooks
|
||||||
|
}
|
||||||
|
|
||||||
|
luaHandlers := rt.NewTable()
|
||||||
|
for _, handler := range handlers {
|
||||||
|
if handler.typ != luaListener { continue }
|
||||||
|
luaHandlers.Set(rt.IntValue(luaHandlers.Len() + 1), rt.FunctionValue(handler.luaCaller))
|
||||||
|
}
|
||||||
|
|
||||||
|
if luaHandlers.Len() == 0 {
|
||||||
|
return nil, noHooks
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.PushingNext1(t.Runtime, rt.TableValue(luaHandlers)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// release(name, catcher)
|
||||||
|
// Removes the `catcher` for the event with `name`.
|
||||||
|
// For this to work, `catcher` has to be the same function used to catch
|
||||||
|
// an event, like one saved to a variable.
|
||||||
|
// #param name string Name of the event the hook is on
|
||||||
|
// #param catcher function Hook function to remove
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
local hookCallback = function() print 'hi' end
|
||||||
|
|
||||||
|
bait.catch('event', hookCallback)
|
||||||
|
|
||||||
|
-- a little while later....
|
||||||
|
bait.release('event', hookCallback)
|
||||||
|
-- and now hookCallback will no longer be ran for the event.
|
||||||
|
#example
|
||||||
|
*/
|
||||||
|
func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
name, catcher, err := util.HandleStrCallback(t, c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.OffLua(name, catcher)
|
||||||
|
|
||||||
|
return c.Next(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// throw(name, ...args)
|
// throw(name, ...args)
|
||||||
// Throws a hook with `name` with the provided `args`
|
// #param name string The name of the hook.
|
||||||
// --- @param name string
|
// #param args ...any The arguments to pass to the hook.
|
||||||
// --- @vararg any
|
// Throws a hook with `name` with the provided `args`.
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
bait.throw('greeting', 'world')
|
||||||
|
|
||||||
|
-- This can then be listened to via
|
||||||
|
bait.catch('gretting', function(greetTo)
|
||||||
|
print('Hello ' .. greetTo)
|
||||||
|
end)
|
||||||
|
#example
|
||||||
|
*/
|
||||||
func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -244,48 +374,3 @@ func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// catch(name, cb)
|
|
||||||
// Catches a hook with `name`. Runs the `cb` when it is thrown
|
|
||||||
// --- @param name string
|
|
||||||
// --- @param cb function
|
|
||||||
func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
name, catcher, err := util.HandleStrCallback(t, c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b.OnLua(name, catcher)
|
|
||||||
|
|
||||||
return c.Next(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// catchOnce(name, cb)
|
|
||||||
// Same as catch, but only runs the `cb` once and then removes the hook
|
|
||||||
// --- @param name string
|
|
||||||
// --- @param cb function
|
|
||||||
func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
name, catcher, err := util.HandleStrCallback(t, c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b.OnceLua(name, catcher)
|
|
||||||
|
|
||||||
return c.Next(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// release(name, catcher)
|
|
||||||
// Removes the `catcher` for the event with `name`
|
|
||||||
// For this to work, `catcher` has to be the same function used to catch
|
|
||||||
// an event, like one saved to a variable.
|
|
||||||
func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
name, catcher, err := util.HandleStrCallback(t, c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b.OffLua(name, catcher)
|
|
||||||
|
|
||||||
return c.Next(), nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,35 @@
|
||||||
|
// library for custom commands
|
||||||
|
/*
|
||||||
|
Commander is the library which handles Hilbish commands. This makes
|
||||||
|
the user able to add Lua-written commands to their shell without making
|
||||||
|
a separate script in a bin folder. Instead, you may simply use the Commander
|
||||||
|
library in your Hilbish config.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local commander = require 'commander'
|
||||||
|
|
||||||
|
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: `input`, `out`, and `err`.
|
||||||
|
There is an `in` alias to `input`, but it requires using the string accessor syntax (`sinks['in']`)
|
||||||
|
as `in` is also a Lua keyword, so `input` is preferred for use.
|
||||||
|
All of them are a @Sink.
|
||||||
|
In the future, `sinks.in` will be removed.
|
||||||
|
|
||||||
|
- `in` is the standard input.
|
||||||
|
You may use the read functions on this sink to get input from the user.
|
||||||
|
- `out` is standard output.
|
||||||
|
This is usually where command output should go.
|
||||||
|
- `err` is standard error.
|
||||||
|
This sink is for writing errors, as the name would suggest.
|
||||||
|
*/
|
||||||
package commander
|
package commander
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -11,11 +43,13 @@ import (
|
||||||
type Commander struct{
|
type Commander struct{
|
||||||
Events *bait.Bait
|
Events *bait.Bait
|
||||||
Loader packagelib.Loader
|
Loader packagelib.Loader
|
||||||
|
Commands map[string]*rt.Closure
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(rtm *rt.Runtime) Commander {
|
func New(rtm *rt.Runtime) *Commander {
|
||||||
c := Commander{
|
c := &Commander{
|
||||||
Events: bait.New(rtm),
|
Events: bait.New(rtm),
|
||||||
|
Commands: make(map[string]*rt.Closure),
|
||||||
}
|
}
|
||||||
c.Loader = packagelib.Loader{
|
c.Loader = packagelib.Loader{
|
||||||
Load: c.loaderFunc,
|
Load: c.loaderFunc,
|
||||||
|
@ -29,32 +63,45 @@ func (c *Commander) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
exports := map[string]util.LuaExport{
|
exports := map[string]util.LuaExport{
|
||||||
"register": util.LuaExport{c.cregister, 2, false},
|
"register": util.LuaExport{c.cregister, 2, false},
|
||||||
"deregister": util.LuaExport{c.cderegister, 1, false},
|
"deregister": util.LuaExport{c.cderegister, 1, false},
|
||||||
|
"registry": util.LuaExport{c.cregistry, 0, false},
|
||||||
}
|
}
|
||||||
mod := rt.NewTable()
|
mod := rt.NewTable()
|
||||||
util.SetExports(rtm, mod, exports)
|
util.SetExports(rtm, mod, exports)
|
||||||
util.Document(mod, "Commander is Hilbish's custom command library, a way to write commands in Lua.")
|
|
||||||
|
|
||||||
return rt.TableValue(mod), nil
|
return rt.TableValue(mod), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// register(name, cb)
|
// register(name, cb)
|
||||||
// Register a command with `name` that runs `cb` when ran
|
// Adds a new command with the given `name`. When Hilbish has to run a command with a name,
|
||||||
// --- @param name string
|
// it will run the function providing the arguments and sinks.
|
||||||
// --- @param cb function
|
// #param name string Name of the command
|
||||||
|
// #param cb function Callback to handle command invocation
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
-- When you run the command `hello` in the shell, it will print `Hello world`.
|
||||||
|
-- If you run it with, for example, `hello Hilbish`, it will print 'Hello Hilbish'
|
||||||
|
commander.register('hello', function(args, sinks)
|
||||||
|
local name = 'world'
|
||||||
|
if #args > 0 then name = args[1] end
|
||||||
|
|
||||||
|
sinks.out:writeln('Hello ' .. name)
|
||||||
|
end)
|
||||||
|
#example
|
||||||
|
*/
|
||||||
func (c *Commander) cregister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
func (c *Commander) cregister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||||
cmdName, cmd, err := util.HandleStrCallback(t, ct)
|
cmdName, cmd, err := util.HandleStrCallback(t, ct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Events.Emit("commandRegister", cmdName, cmd)
|
c.Commands[cmdName] = cmd
|
||||||
|
|
||||||
return ct.Next(), err
|
return ct.Next(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// deregister(name)
|
// deregister(name)
|
||||||
// Deregisters any command registered with `name`
|
// Removes the named command. Note that this will only remove Commander-registered commands.
|
||||||
// --- @param name string
|
// #param name string Name of the command to remove.
|
||||||
func (c *Commander) cderegister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
func (c *Commander) cderegister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := ct.Check1Arg(); err != nil {
|
if err := ct.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -64,7 +111,23 @@ func (c *Commander) cderegister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Events.Emit("commandDeregister", cmdName)
|
delete(c.Commands, cmdName)
|
||||||
|
|
||||||
return ct.Next(), err
|
return ct.Next(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// registry() -> table
|
||||||
|
// Returns all registered commanders. Returns a list of tables with the following keys:
|
||||||
|
// - `exec`: The function used to run the commander. Commanders require args and sinks to be passed.
|
||||||
|
// #returns table
|
||||||
|
func (c *Commander) cregistry(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||||
|
registryLua := rt.NewTable()
|
||||||
|
for cmdName, cmd := range c.Commands {
|
||||||
|
cmdTbl := rt.NewTable()
|
||||||
|
cmdTbl.Set(rt.StringValue("exec"), rt.FunctionValue(cmd))
|
||||||
|
|
||||||
|
registryLua.Set(rt.StringValue(cmdName), rt.TableValue(cmdTbl))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ct.PushingNext1(t.Runtime, rt.TableValue(registryLua)), nil
|
||||||
|
}
|
||||||
|
|
341
golibs/fs/fs.go
341
golibs/fs/fs.go
|
@ -1,3 +1,10 @@
|
||||||
|
// filesystem interaction and functionality library
|
||||||
|
/*
|
||||||
|
The fs module provides filesystem functions to Hilbish. While Lua's standard
|
||||||
|
library has some I/O functions, they're missing a lot of the basics. The `fs`
|
||||||
|
library offers more functions and will work on any operating system Hilbish does.
|
||||||
|
#field pathSep The operating system's path separator.
|
||||||
|
*/
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -11,6 +18,7 @@ import (
|
||||||
|
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
"github.com/arnodel/golua/lib/packagelib"
|
"github.com/arnodel/golua/lib/packagelib"
|
||||||
|
"github.com/arnodel/golua/lib/iolib"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Loader = packagelib.Loader{
|
var Loader = packagelib.Loader{
|
||||||
|
@ -29,22 +37,56 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
"dir": util.LuaExport{fdir, 1, false},
|
"dir": util.LuaExport{fdir, 1, false},
|
||||||
"glob": util.LuaExport{fglob, 1, false},
|
"glob": util.LuaExport{fglob, 1, false},
|
||||||
"join": util.LuaExport{fjoin, 0, true},
|
"join": util.LuaExport{fjoin, 0, true},
|
||||||
|
"pipe": util.LuaExport{fpipe, 0, false},
|
||||||
}
|
}
|
||||||
mod := rt.NewTable()
|
mod := rt.NewTable()
|
||||||
util.SetExports(rtm, mod, exports)
|
util.SetExports(rtm, mod, exports)
|
||||||
mod.Set(rt.StringValue("pathSep"), rt.StringValue(string(os.PathSeparator)))
|
mod.Set(rt.StringValue("pathSep"), rt.StringValue(string(os.PathSeparator)))
|
||||||
mod.Set(rt.StringValue("pathListSep"), rt.StringValue(string(os.PathListSeparator)))
|
mod.Set(rt.StringValue("pathListSep"), rt.StringValue(string(os.PathListSeparator)))
|
||||||
|
|
||||||
util.Document(mod, `The fs module provides easy and simple access to
|
|
||||||
filesystem functions and other things, and acts an
|
|
||||||
addition to the Lua standard library's I/O and filesystem functions.`)
|
|
||||||
|
|
||||||
return rt.TableValue(mod), nil
|
return rt.TableValue(mod), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// abs(path) -> string
|
||||||
|
// Returns an absolute version of the `path`.
|
||||||
|
// This can be used to resolve short paths like `..` to `/home/user`.
|
||||||
|
// #param path string
|
||||||
|
// #returns string
|
||||||
|
func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
path, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
path = util.ExpandHome(path)
|
||||||
|
|
||||||
|
abspath, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.PushingNext1(t.Runtime, rt.StringValue(abspath)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// basename(path) -> string
|
||||||
|
// Returns the "basename," or the last part of the provided `path`. If path is empty,
|
||||||
|
// `.` will be returned.
|
||||||
|
// #param path string Path to get the base name of.
|
||||||
|
// #returns string
|
||||||
|
func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
path, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.PushingNext(t.Runtime, rt.StringValue(filepath.Base(path))), nil
|
||||||
|
}
|
||||||
|
|
||||||
// cd(dir)
|
// cd(dir)
|
||||||
// Changes directory to `dir`
|
// Changes Hilbish's directory to `dir`.
|
||||||
// --- @param dir string
|
// #param dir string Path to change directory to.
|
||||||
func fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -63,10 +105,103 @@ func fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), err
|
return c.Next(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dir(path) -> string
|
||||||
|
// Returns the directory part of `path`. If a file path like
|
||||||
|
// `~/Documents/doc.txt` then this function will return `~/Documents`.
|
||||||
|
// #param path string Path to get the directory for.
|
||||||
|
// #returns string
|
||||||
|
func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
path, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.PushingNext(t.Runtime, rt.StringValue(filepath.Dir(path))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// glob(pattern) -> matches (table)
|
||||||
|
// Match all files based on the provided `pattern`.
|
||||||
|
// For the syntax' refer to Go's filepath.Match function: https://pkg.go.dev/path/filepath#Match
|
||||||
|
// #param pattern string Pattern to compare files with.
|
||||||
|
// #returns table A list of file names/paths that match.
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
--[[
|
||||||
|
Within a folder that contains the following files:
|
||||||
|
a.txt
|
||||||
|
init.lua
|
||||||
|
code.lua
|
||||||
|
doc.pdf
|
||||||
|
]]--
|
||||||
|
local matches = fs.glob './*.lua'
|
||||||
|
print(matches)
|
||||||
|
-- -> {'init.lua', 'code.lua'}
|
||||||
|
#example
|
||||||
|
*/
|
||||||
|
func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pattern, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
matches, err := filepath.Glob(pattern)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
luaMatches := rt.NewTable()
|
||||||
|
|
||||||
|
for i, match := range matches {
|
||||||
|
luaMatches.Set(rt.IntValue(int64(i + 1)), rt.StringValue(match))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.PushingNext(t.Runtime, rt.TableValue(luaMatches)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// join(...path) -> string
|
||||||
|
// Takes any list of paths and joins them based on the operating system's path separator.
|
||||||
|
// #param path ...string Paths to join together
|
||||||
|
// #returns string The joined path.
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
-- This prints the directory for Hilbish's config!
|
||||||
|
print(fs.join(hilbish.userDir.config, 'hilbish'))
|
||||||
|
-- -> '/home/user/.config/hilbish' on Linux
|
||||||
|
#example
|
||||||
|
*/
|
||||||
|
func fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
strs := make([]string, len(c.Etc()))
|
||||||
|
for i, v := range c.Etc() {
|
||||||
|
if v.Type() != rt.StringType {
|
||||||
|
// +2; go indexes of 0 and first arg from above
|
||||||
|
return nil, fmt.Errorf("bad argument #%d to run (expected string, got %s)", i + 1, v.TypeName())
|
||||||
|
}
|
||||||
|
strs[i] = v.AsString()
|
||||||
|
}
|
||||||
|
|
||||||
|
res := filepath.Join(strs...)
|
||||||
|
|
||||||
|
return c.PushingNext(t.Runtime, rt.StringValue(res)), nil
|
||||||
|
}
|
||||||
|
|
||||||
// mkdir(name, recursive)
|
// mkdir(name, recursive)
|
||||||
// Makes a directory called `name`. If `recursive` is true, it will create its parent directories.
|
// Creates a new directory with the provided `name`.
|
||||||
// --- @param name string
|
// With `recursive`, mkdir will create parent directories.
|
||||||
// --- @param recursive boolean
|
// #param name string Name of the directory
|
||||||
|
// #param recursive boolean Whether to create parent directories for the provided name
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
-- This will create the directory foo, then create the directory bar in the
|
||||||
|
-- foo directory. If recursive is false in this case, it will fail.
|
||||||
|
fs.mkdir('./foo/bar', true)
|
||||||
|
#example
|
||||||
|
*/
|
||||||
func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.CheckNArgs(2); err != nil {
|
if err := c.CheckNArgs(2); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -93,9 +228,74 @@ func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), err
|
return c.Next(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// stat(path)
|
// fpipe() -> File, File
|
||||||
// Returns info about `path`
|
// Returns a pair of connected files, also known as a pipe.
|
||||||
// --- @param path string
|
// The type returned is a Lua file, same as returned from `io` functions.
|
||||||
|
// #returns File
|
||||||
|
// #returns File
|
||||||
|
func fpipe(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
rf, wf, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rfLua := iolib.NewFile(rf, 0)
|
||||||
|
wfLua := iolib.NewFile(wf, 0)
|
||||||
|
|
||||||
|
return c.PushingNext(t.Runtime, rfLua.Value(t.Runtime), wfLua.Value(t.Runtime)), nil
|
||||||
|
}
|
||||||
|
// readdir(path) -> table[string]
|
||||||
|
// Returns a list of all files and directories in the provided path.
|
||||||
|
// #param dir string
|
||||||
|
// #returns table
|
||||||
|
func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dir, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dir = util.ExpandHome(dir)
|
||||||
|
names := rt.NewTable()
|
||||||
|
|
||||||
|
dirEntries, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i, entry := range dirEntries {
|
||||||
|
names.Set(rt.IntValue(int64(i + 1)), rt.StringValue(entry.Name()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// stat(path) -> {}
|
||||||
|
// Returns the information about a given `path`.
|
||||||
|
// The returned table contains the following values:
|
||||||
|
// name (string) - Name of the path
|
||||||
|
// size (number) - Size of the path in bytes
|
||||||
|
// mode (string) - Unix permission mode in an octal format string (with leading 0)
|
||||||
|
// isDir (boolean) - If the path is a directory
|
||||||
|
// #param path string
|
||||||
|
// #returns table
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
local inspect = require 'inspect'
|
||||||
|
|
||||||
|
local stat = fs.stat '~'
|
||||||
|
print(inspect(stat))
|
||||||
|
--[[
|
||||||
|
Would print the following:
|
||||||
|
{
|
||||||
|
isDir = true,
|
||||||
|
mode = "0755",
|
||||||
|
name = "username",
|
||||||
|
size = 12288
|
||||||
|
}
|
||||||
|
]]--
|
||||||
|
#example
|
||||||
|
*/
|
||||||
func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -119,120 +319,3 @@ func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.PushingNext1(t.Runtime, rt.TableValue(statTbl)), nil
|
return c.PushingNext1(t.Runtime, rt.TableValue(statTbl)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readdir(dir)
|
|
||||||
// Returns a table of files in `dir`
|
|
||||||
// --- @param dir string
|
|
||||||
// --- @return table
|
|
||||||
func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
if err := c.Check1Arg(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
dir, err := c.StringArg(0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
dir = util.ExpandHome(dir)
|
|
||||||
names := rt.NewTable()
|
|
||||||
|
|
||||||
dirEntries, err := os.ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for i, entry := range dirEntries {
|
|
||||||
names.Set(rt.IntValue(int64(i + 1)), rt.StringValue(entry.Name()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// abs(path)
|
|
||||||
// Gives an absolute version of `path`.
|
|
||||||
// --- @param path string
|
|
||||||
func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
path, err := c.StringArg(0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
path = util.ExpandHome(path)
|
|
||||||
|
|
||||||
abspath, err := filepath.Abs(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, rt.StringValue(abspath)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// basename(path)
|
|
||||||
// Gives the basename of `path`. For the rules,
|
|
||||||
// see Go's filepath.Base
|
|
||||||
func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
if err := c.Check1Arg(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
path, err := c.StringArg(0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.PushingNext(t.Runtime, rt.StringValue(filepath.Base(path))), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// dir(path)
|
|
||||||
// Returns the directory part of `path`. For the rules, see Go's
|
|
||||||
// filepath.Dir
|
|
||||||
func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
if err := c.Check1Arg(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
path, err := c.StringArg(0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.PushingNext(t.Runtime, rt.StringValue(filepath.Dir(path))), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// glob(pattern)
|
|
||||||
// Glob all files and directories that match the pattern.
|
|
||||||
// For the rules, see Go's filepath.Glob
|
|
||||||
func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
if err := c.Check1Arg(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pattern, err := c.StringArg(0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
matches, err := filepath.Glob(pattern)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
luaMatches := rt.NewTable()
|
|
||||||
|
|
||||||
for i, match := range matches {
|
|
||||||
luaMatches.Set(rt.IntValue(int64(i + 1)), rt.StringValue(match))
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.PushingNext(t.Runtime, rt.TableValue(luaMatches)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// join(paths...)
|
|
||||||
// Takes paths and joins them together with the OS's
|
|
||||||
// directory separator (forward or backward slash).
|
|
||||||
func fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
strs := make([]string, len(c.Etc()))
|
|
||||||
for i, v := range c.Etc() {
|
|
||||||
if v.Type() != rt.StringType {
|
|
||||||
// +2; go indexes of 0 and first arg from above
|
|
||||||
return nil, fmt.Errorf("bad argument #%d to run (expected string, got %s)", i + 1, v.TypeName())
|
|
||||||
}
|
|
||||||
strs[i] = v.AsString()
|
|
||||||
}
|
|
||||||
|
|
||||||
res := filepath.Join(strs...)
|
|
||||||
|
|
||||||
return c.PushingNext(t.Runtime, rt.StringValue(res)), nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// low level terminal library
|
||||||
|
// The terminal library is a simple and lower level library for certain terminal interactions.
|
||||||
package terminal
|
package terminal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -26,14 +28,13 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
|
|
||||||
mod := rt.NewTable()
|
mod := rt.NewTable()
|
||||||
util.SetExports(rtm, mod, exports)
|
util.SetExports(rtm, mod, exports)
|
||||||
util.Document(mod, "The terminal library is a simple and lower level library for certain terminal interactions.")
|
|
||||||
|
|
||||||
return rt.TableValue(mod), nil
|
return rt.TableValue(mod), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// size()
|
// size()
|
||||||
// Gets the dimensions of the terminal. Returns a table with `width` and `height`
|
// Gets the dimensions of the terminal. Returns a table with `width` and `height`
|
||||||
// Note: this is not the size in relation to the dimensions of the display
|
// NOTE: The size refers to the amount of columns and rows of text that can fit in the terminal.
|
||||||
func termsize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func termsize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
w, h, err := term.GetSize(int(os.Stdin.Fd()))
|
w, h, err := term.GetSize(int(os.Stdin.Fd()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -48,7 +49,7 @@ func termsize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveState()
|
// saveState()
|
||||||
// Saves the current state of the terminal
|
// Saves the current state of the terminal.
|
||||||
func termsaveState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func termsaveState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
state, err := term.GetState(int(os.Stdin.Fd()))
|
state, err := term.GetState(int(os.Stdin.Fd()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -71,7 +72,7 @@ func termrestoreState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// setRaw()
|
// setRaw()
|
||||||
// Puts the terminal in raw mode
|
// Puts the terminal into raw mode.
|
||||||
func termsetRaw(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func termsetRaw(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
_, err := term.MakeRaw(int(os.Stdin.Fd()))
|
_, err := term.MakeRaw(int(os.Stdin.Fd()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -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
|
|
@ -73,13 +73,13 @@ func newFileHistory(path string) *fileHistory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
itms := []string{""}
|
|
||||||
lines := strings.Split(string(data), "\n")
|
lines := strings.Split(string(data), "\n")
|
||||||
|
itms := make([]string, len(lines) - 1)
|
||||||
for i, l := range lines {
|
for i, l := range lines {
|
||||||
if i == len(lines) - 1 {
|
if i == len(lines) - 1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
itms = append(itms, l)
|
itms[i] = l
|
||||||
}
|
}
|
||||||
f, err := os.OpenFile(path, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0755)
|
f, err := os.OpenFile(path, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build windows
|
//go:build windows
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
63
job.go
63
job.go
|
@ -18,6 +18,16 @@ import (
|
||||||
var jobs *jobHandler
|
var jobs *jobHandler
|
||||||
var jobMetaKey = rt.StringValue("hshjob")
|
var jobMetaKey = rt.StringValue("hshjob")
|
||||||
|
|
||||||
|
// #type
|
||||||
|
// #interface jobs
|
||||||
|
// #property cmd The user entered command string for the job.
|
||||||
|
// #property running Whether the job is running or not.
|
||||||
|
// #property id The ID of the job in the job table
|
||||||
|
// #property pid The Process ID
|
||||||
|
// #property exitCode The last exit code of the job.
|
||||||
|
// #property stdout The standard output of the job. This just means the normal logs of the process.
|
||||||
|
// #property stderr The standard error stream of the process. This (usually) includes error messages of the job.
|
||||||
|
// The Job type describes a Hilbish job.
|
||||||
type job struct {
|
type job struct {
|
||||||
cmd string
|
cmd string
|
||||||
running bool
|
running bool
|
||||||
|
@ -110,6 +120,10 @@ func (j *job) getProc() *os.Process {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface jobs
|
||||||
|
// #member
|
||||||
|
// start()
|
||||||
|
// Starts running the job.
|
||||||
func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -130,6 +144,10 @@ func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface jobs
|
||||||
|
// #member
|
||||||
|
// stop()
|
||||||
|
// Stops the job from running.
|
||||||
func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -148,6 +166,11 @@ func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface jobs
|
||||||
|
// #member
|
||||||
|
// foreground()
|
||||||
|
// Puts a job in the foreground. This will cause it to run like it was
|
||||||
|
// executed normally and wait for it to complete.
|
||||||
func luaForegroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func luaForegroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -180,6 +203,10 @@ func luaForegroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface jobs
|
||||||
|
// #member
|
||||||
|
// background()
|
||||||
|
// Puts a job in the background. This acts the same as initially running a job.
|
||||||
func luaBackgroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func luaBackgroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -276,6 +303,13 @@ func (j *jobHandler) stopAll() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface jobs
|
||||||
|
// background job management
|
||||||
|
/*
|
||||||
|
Manage interactive jobs in Hilbish via Lua.
|
||||||
|
|
||||||
|
Jobs are the name of background tasks/commands. A job can be started via
|
||||||
|
interactive usage or with the functions defined below for use in external runners. */
|
||||||
func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table {
|
func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table {
|
||||||
jobMethods := rt.NewTable()
|
jobMethods := rt.NewTable()
|
||||||
jFuncs := map[string]util.LuaExport{
|
jFuncs := map[string]util.LuaExport{
|
||||||
|
@ -353,6 +387,11 @@ func jobUserData(j *job) *rt.UserData {
|
||||||
return rt.NewUserData(j, jobMeta.AsTable())
|
return rt.NewUserData(j, jobMeta.AsTable())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface jobs
|
||||||
|
// get(id) -> @Job
|
||||||
|
// Get a job object via its ID.
|
||||||
|
// --- @param id number
|
||||||
|
// --- @returns Job
|
||||||
func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
j.mu.RLock()
|
j.mu.RLock()
|
||||||
defer j.mu.RUnlock()
|
defer j.mu.RUnlock()
|
||||||
|
@ -373,6 +412,18 @@ func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.PushingNext(t.Runtime, rt.UserDataValue(job.ud)), nil
|
return c.PushingNext(t.Runtime, rt.UserDataValue(job.ud)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface jobs
|
||||||
|
// add(cmdstr, args, execPath)
|
||||||
|
// Creates a new job. This function does not run the job. This function is intended to be
|
||||||
|
// used by runners, but can also be used to create jobs via Lua. Commanders cannot be ran as jobs.
|
||||||
|
// #param cmdstr string String that a user would write for the job
|
||||||
|
// #param args table Arguments for the commands. Has to include the name of the command.
|
||||||
|
// #param execPath string Binary to use to run the command. Needs to be an absolute path.
|
||||||
|
/*
|
||||||
|
#example
|
||||||
|
hilbish.jobs.add('go build', {'go', 'build'}, '/usr/bin/go')
|
||||||
|
#example
|
||||||
|
*/
|
||||||
func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.CheckNArgs(3); err != nil {
|
if err := c.CheckNArgs(3); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -402,6 +453,10 @@ func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.PushingNext1(t.Runtime, rt.UserDataValue(jb.ud)), nil
|
return c.PushingNext1(t.Runtime, rt.UserDataValue(jb.ud)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface jobs
|
||||||
|
// all() -> table[@Job]
|
||||||
|
// Returns a table of all job objects.
|
||||||
|
// #returns table[Job]
|
||||||
func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
j.mu.RLock()
|
j.mu.RLock()
|
||||||
defer j.mu.RUnlock()
|
defer j.mu.RUnlock()
|
||||||
|
@ -414,6 +469,10 @@ func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.PushingNext1(t.Runtime, rt.TableValue(jobTbl)), nil
|
return c.PushingNext1(t.Runtime, rt.TableValue(jobTbl)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface jobs
|
||||||
|
// disown(id)
|
||||||
|
// Disowns a job. This simply deletes it from the list of jobs without stopping it.
|
||||||
|
// #param id number
|
||||||
func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -431,6 +490,10 @@ func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #interface jobs
|
||||||
|
// last() -> @Job
|
||||||
|
// Returns the last added job to the table.
|
||||||
|
// #returns Job
|
||||||
func (j *jobHandler) luaLastJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (j *jobHandler) luaLastJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
j.mu.RLock()
|
j.mu.RLock()
|
||||||
defer j.mu.RUnlock()
|
defer j.mu.RUnlock()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build darwin linux
|
//go:build unix
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build windows
|
//go:build windows
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 8467b87dd8d49c68b4100b2d129d5f071544b8cf
|
Subproject commit 34a57c964590f89aa065188a588c7b38aff99c28
|
19
lua.go
19
lua.go
|
@ -23,6 +23,7 @@ func luaInit() {
|
||||||
MessageHandler: debuglib.Traceback,
|
MessageHandler: debuglib.Traceback,
|
||||||
})
|
})
|
||||||
lib.LoadAll(l)
|
lib.LoadAll(l)
|
||||||
|
setupSinkType(l)
|
||||||
|
|
||||||
lib.LoadLibs(l, hilbishLoader)
|
lib.LoadLibs(l, hilbishLoader)
|
||||||
// yes this is stupid, i know
|
// yes this is stupid, i know
|
||||||
|
@ -32,24 +33,12 @@ func luaInit() {
|
||||||
lib.LoadLibs(l, fs.Loader)
|
lib.LoadLibs(l, fs.Loader)
|
||||||
lib.LoadLibs(l, terminal.Loader)
|
lib.LoadLibs(l, terminal.Loader)
|
||||||
|
|
||||||
cmds := commander.New(l)
|
cmds = commander.New(l)
|
||||||
// When a command from Lua is added, register it for use
|
|
||||||
cmds.Events.On("commandRegister", func(args ...interface{}) {
|
|
||||||
cmdName := args[0].(string)
|
|
||||||
cmd := args[1].(*rt.Closure)
|
|
||||||
|
|
||||||
commands[cmdName] = cmd
|
|
||||||
})
|
|
||||||
cmds.Events.On("commandDeregister", func(args ...interface{}) {
|
|
||||||
cmdName := args[0].(string)
|
|
||||||
|
|
||||||
delete(commands, cmdName)
|
|
||||||
})
|
|
||||||
lib.LoadLibs(l, cmds.Loader)
|
lib.LoadLibs(l, cmds.Loader)
|
||||||
|
|
||||||
hooks = bait.New(l)
|
hooks = bait.New(l)
|
||||||
hooks.SetRecoverer(func(event string, handler *bait.Listener, err interface{}) {
|
hooks.SetRecoverer(func(event string, handler *bait.Listener, err interface{}) {
|
||||||
fmt.Println("Error in", event, "event:", err)
|
fmt.Println("Error in `error` hook handler:", err)
|
||||||
hooks.Off(event, handler)
|
hooks.Off(event, handler)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -67,7 +56,7 @@ func luaInit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add more paths that Lua can require from
|
// Add more paths that Lua can require from
|
||||||
err := util.DoString(l, "package.path = package.path .. " + requirePaths)
|
_, err := util.DoString(l, "package.path = package.path .. " + requirePaths)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "Could not add Hilbish require paths! Libraries will be missing. This shouldn't happen.")
|
fmt.Fprintln(os.Stderr, "Could not add Hilbish require paths! Libraries will be missing. This shouldn't happen.")
|
||||||
}
|
}
|
||||||
|
|
39
main.go
39
main.go
|
@ -2,16 +2,20 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"hilbish/util"
|
"hilbish/util"
|
||||||
"hilbish/golibs/bait"
|
"hilbish/golibs/bait"
|
||||||
|
"hilbish/golibs/commander"
|
||||||
|
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
"github.com/pborman/getopt"
|
"github.com/pborman/getopt"
|
||||||
|
@ -23,7 +27,6 @@ var (
|
||||||
l *rt.Runtime
|
l *rt.Runtime
|
||||||
lr *lineReader
|
lr *lineReader
|
||||||
|
|
||||||
commands = map[string]*rt.Closure{}
|
|
||||||
luaCompletions = map[string]*rt.Closure{}
|
luaCompletions = map[string]*rt.Closure{}
|
||||||
|
|
||||||
confDir string
|
confDir string
|
||||||
|
@ -31,6 +34,7 @@ var (
|
||||||
curuser *user.User
|
curuser *user.User
|
||||||
|
|
||||||
hooks *bait.Bait
|
hooks *bait.Bait
|
||||||
|
cmds *commander.Commander
|
||||||
defaultConfPath string
|
defaultConfPath string
|
||||||
defaultHistPath string
|
defaultHistPath string
|
||||||
)
|
)
|
||||||
|
@ -88,7 +92,7 @@ func main() {
|
||||||
interactive = true
|
interactive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileInfo, _ := os.Stdin.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 {
|
if fileInfo, _ := os.Stdin.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 || !term.IsTerminal(int(os.Stdin.Fd())) {
|
||||||
interactive = false
|
interactive = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,18 +110,26 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if *verflag {
|
if *verflag {
|
||||||
fmt.Printf("Hilbish %s\n", getVersion())
|
fmt.Printf("Hilbish %s\nCompiled with %s\n", getVersion(), runtime.Version())
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set $SHELL if the user wants to
|
// Set $SHELL if the user wants to
|
||||||
if *setshflag {
|
if *setshflag {
|
||||||
os.Setenv("SHELL", os.Args[0])
|
os.Setenv("SHELL", "hilbish")
|
||||||
|
|
||||||
|
path, err := exec.LookPath("hilbish")
|
||||||
|
if err == nil {
|
||||||
|
os.Setenv("SHELL", path)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
go handleSignals()
|
|
||||||
lr = newLineReader("", false)
|
lr = newLineReader("", false)
|
||||||
luaInit()
|
luaInit()
|
||||||
|
|
||||||
|
go handleSignals()
|
||||||
|
|
||||||
// If user's config doesn't exixt,
|
// If user's config doesn't exixt,
|
||||||
if _, err := os.Stat(defaultConfPath); os.IsNotExist(err) && *configflag == defaultConfPath {
|
if _, err := os.Stat(defaultConfPath); os.IsNotExist(err) && *configflag == defaultConfPath {
|
||||||
// Read default from current directory
|
// Read default from current directory
|
||||||
|
@ -181,11 +193,18 @@ input:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != readline.CtrlC {
|
if err == readline.CtrlC {
|
||||||
|
fmt.Println("^C")
|
||||||
|
hooks.Emit("hilbish.cancel")
|
||||||
|
} else {
|
||||||
// If we get a completely random error, print
|
// If we get a completely random error, print
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
if errors.Is(err, syscall.ENOTTY) {
|
||||||
|
// what are we even doing here?
|
||||||
|
panic("not a tty")
|
||||||
|
}
|
||||||
|
<-make(chan struct{})
|
||||||
}
|
}
|
||||||
fmt.Println("^C")
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var priv bool
|
var priv bool
|
||||||
|
@ -284,7 +303,7 @@ func removeDupes(slice []string) []string {
|
||||||
|
|
||||||
func contains(s []string, e string) bool {
|
func contains(s []string, e string) bool {
|
||||||
for _, a := range s {
|
for _, a := range s {
|
||||||
if a == e {
|
if strings.ToLower(a) == strings.ToLower(e) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,3 +338,7 @@ func getVersion() string {
|
||||||
|
|
||||||
return v.String()
|
return v.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cut(slice []string, idx int) []string {
|
||||||
|
return append(slice[:idx], slice[idx + 1:]...)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"plugin"
|
||||||
|
|
||||||
|
"hilbish/util"
|
||||||
|
|
||||||
|
rt "github.com/arnodel/golua/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// #interface module
|
||||||
|
// native module loading
|
||||||
|
// #field paths A list of paths to search when loading native modules. This is in the style of Lua search paths and will be used when requiring native modules. Example: `?.so;?/?.so`
|
||||||
|
/*
|
||||||
|
The hilbish.module interface provides a function to load
|
||||||
|
Hilbish plugins/modules. Hilbish modules are Go-written
|
||||||
|
plugins (see https://pkg.go.dev/plugin) that are used to add functionality
|
||||||
|
to Hilbish that cannot be written in Lua for any reason.
|
||||||
|
|
||||||
|
Note that you don't ever need to use the load function that is here as
|
||||||
|
modules can be loaded with a `require` call like Lua C modules, and the
|
||||||
|
search paths can be changed with the `paths` property here.
|
||||||
|
|
||||||
|
To make a valid native module, the Go plugin has to export a Loader function
|
||||||
|
with a signature like so: `func(*rt.Runtime) rt.Value`.
|
||||||
|
|
||||||
|
`rt` in this case refers to the Runtime type at
|
||||||
|
https://pkg.go.dev/github.com/arnodel/golua@master/runtime#Runtime
|
||||||
|
|
||||||
|
Hilbish uses this package as its Lua runtime. You will need to read
|
||||||
|
it to use it for a native plugin.
|
||||||
|
|
||||||
|
Here is some code for an example plugin:
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
rt "github.com/arnodel/golua/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Loader(rtm *rt.Runtime) rt.Value {
|
||||||
|
return rt.StringValue("hello world!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This can be compiled with `go build -buildmode=plugin plugin.go`.
|
||||||
|
If you attempt to require and print the result (`print(require 'plugin')`), it will show "hello world!"
|
||||||
|
*/
|
||||||
|
func moduleLoader(rtm *rt.Runtime) *rt.Table {
|
||||||
|
exports := map[string]util.LuaExport{
|
||||||
|
"load": {moduleLoad, 2, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
mod := rt.NewTable()
|
||||||
|
util.SetExports(rtm, mod, exports)
|
||||||
|
|
||||||
|
return mod
|
||||||
|
}
|
||||||
|
|
||||||
|
// #interface module
|
||||||
|
// load(path)
|
||||||
|
// Loads a module at the designated `path`.
|
||||||
|
// It will throw if any error occurs.
|
||||||
|
// #param path string
|
||||||
|
func moduleLoad(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
if err := c.CheckNArgs(1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := plugin.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := p.Lookup("Loader")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
loader, ok := value.(func(*rt.Runtime) rt.Value)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
val := loader(t.Runtime)
|
||||||
|
|
||||||
|
return c.PushingNext1(t.Runtime, val), nil
|
||||||
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
local commander = require 'commander'
|
local commander = require 'commander'
|
||||||
|
|
||||||
commander.register('bg', function()
|
commander.register('bg', function(_, sinks)
|
||||||
local job = hilbish.jobs.last()
|
local job = hilbish.jobs.last()
|
||||||
if not job then
|
if not job then
|
||||||
print 'bg: no last job'
|
sinks.out:writeln 'bg: no last job'
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local err = job.background()
|
local err = job:background()
|
||||||
if err then
|
if err then
|
||||||
print('bg: ' .. err)
|
sinks.out:writeln('bg: ' .. err)
|
||||||
return 2
|
return 2
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
local chunkSize = 2^13 -- 8K buffer size
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local block = f:read(chunkSize)
|
||||||
|
if not block then break end
|
||||||
|
sinks.out:write(block)
|
||||||
|
end
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
io.flush()
|
||||||
|
return exit
|
||||||
|
end)
|
|
@ -4,32 +4,25 @@ local fs = require 'fs'
|
||||||
local dirs = require 'nature.dirs'
|
local dirs = require 'nature.dirs'
|
||||||
|
|
||||||
dirs.old = hilbish.cwd()
|
dirs.old = hilbish.cwd()
|
||||||
commander.register('cd', function (args)
|
commander.register('cd', function (args, sinks)
|
||||||
if #args > 1 then
|
if #args > 1 then
|
||||||
print("cd: too many arguments")
|
sinks.out:writeln("cd: too many arguments")
|
||||||
return 1
|
return 1
|
||||||
elseif #args > 0 then
|
|
||||||
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
|
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)
|
end)
|
||||||
|
|
|
@ -3,35 +3,38 @@ local fs = require 'fs'
|
||||||
local lunacolors = require 'lunacolors'
|
local lunacolors = require 'lunacolors'
|
||||||
local dirs = require 'nature.dirs'
|
local dirs = require 'nature.dirs'
|
||||||
|
|
||||||
commander.register('cdr', function(args)
|
commander.register('cdr', function(args, sinks)
|
||||||
if not args[1] then
|
if not args[1] then
|
||||||
print(lunacolors.format [[
|
sinks.out:writeln(lunacolors.format [[
|
||||||
cdr: change directory to one which has been recently visied
|
cdr: change directory to one which has been recently visied
|
||||||
|
|
||||||
usage: cdr <index>
|
usage: cdr <index>
|
||||||
|
|
||||||
to get a list of recent directories, use {green}{underline}cdr list{reset}]])
|
to get a list of recent directories, use {green}cdr list{reset}]])
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if args[1] == 'list' then
|
if args[1] == 'list' then
|
||||||
local recentDirs = dirs.recentDirs
|
local recentDirs = dirs.recentDirs
|
||||||
if #recentDirs == 0 then
|
if #recentDirs == 0 then
|
||||||
print 'No directories have been visited.'
|
sinks.out:writeln 'No directories have been visited.'
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
print(table.concat(recentDirs, '\n'))
|
for idx, d in ipairs(dirs.recentDirs) do
|
||||||
|
if d:find(hilbish.home, 1, true) then d = fs.join('~', d:sub(hilbish.home:len() + 1)) end
|
||||||
|
sinks.out:writeln(lunacolors.format(string.format('{cyan}%d{reset} %s', idx, d)))
|
||||||
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local index = tonumber(args[1])
|
local index = tonumber(args[1])
|
||||||
if not index then
|
if not index then
|
||||||
print(string.format('Received %s as index, which isn\'t a number.', index))
|
sinks.out:writeln(string.format('Received %s as index, which isn\'t a number.', index))
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if not dirs.recent(index) then
|
if not dirs.recent(index) then
|
||||||
print(string.format('No recent directory found at index %s.', index))
|
sinks.out:writeln(string.format('No recent directory found at index %s.', index))
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
local ansikit = require 'ansikit'
|
||||||
|
local commander = require 'commander'
|
||||||
|
|
||||||
|
commander.register('clear', function()
|
||||||
|
ansikit.clear(true)
|
||||||
|
ansikit.cursorTo(0, 0)
|
||||||
|
end)
|
|
@ -1,8 +1,8 @@
|
||||||
local commander = require 'commander'
|
local commander = require 'commander'
|
||||||
|
|
||||||
commander.register('disown', function(args)
|
commander.register('disown', function(args, sinks)
|
||||||
if #hilbish.jobs.all() == 0 then
|
if #hilbish.jobs.all() == 0 then
|
||||||
print 'disown: no current job'
|
sinks.out:writeln 'disown: no current job'
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ commander.register('disown', function(args)
|
||||||
if #args < 0 then
|
if #args < 0 then
|
||||||
id = tonumber(args[1])
|
id = tonumber(args[1])
|
||||||
if not id then
|
if not id then
|
||||||
print 'disown: invalid id for job'
|
sinks.out:writeln 'disown: invalid id for job'
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -19,7 +19,7 @@ commander.register('disown', function(args)
|
||||||
|
|
||||||
local ok = pcall(hilbish.jobs.disown, id)
|
local ok = pcall(hilbish.jobs.disown, id)
|
||||||
if not ok then
|
if not ok then
|
||||||
print 'disown: job does not exist'
|
sinks.out:writeln 'disown: job does not exist'
|
||||||
return 2
|
return 2
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue