mirror of https://github.com/Hilbis/Hilbish
Compare commits
88 Commits
7af90eb1f1
...
aae70ff42e
Author | SHA1 | Date |
---|---|---|
sammyette | aae70ff42e | |
sammyette | 5c748daaa3 | |
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 | |
TorchedSammy | 959030f70d | |
TorchedSammy | 59cec0ffa5 | |
TorchedSammy | 1eed4cc7ee | |
TorchedSammy | c13889592f | |
TorchedSammy | 2e192be2e1 | |
TorchedSammy | c96605e79c | |
TorchedSammy | a1ce2ecba6 | |
TorchedSammy | a1410ae7ad | |
TorchedSammy | 20870b9004 | |
TorchedSammy | 3dcd99563a | |
sammy | 2337f9ab60 | |
TorchedSammy | 6ce4fb3973 | |
TorchedSammy | 387d7d2243 | |
TorchedSammy | 7de835fab4 | |
TorchedSammy | 3e0a2d630b | |
TorchedSammy | 09a8b41063 | |
sammy | 349380ae6b | |
TorchedSammy | f7806f5479 | |
TorchedSammy | c8c30e9861 | |
TorchedSammy | 083c266438 | |
TorchedSammy | dd9bdca5e0 | |
TorchedSammy | 9902560061 | |
TorchedSammy | dd9aa4b6ea | |
TorchedSammy | be8bdef9c8 | |
TorchedSammy | e185a32685 | |
TorchedSammy | 2b480e50e6 | |
TorchedSammy | b65acca903 | |
TorchedSammy | 08e2951513 | |
TorchedSammy | 83a2ce38ea | |
TorchedSammy | 60dd5f598a | |
TorchedSammy | 6eea5bce47 | |
TorchedSammy | a106f4aea0 | |
TorchedSammy | 90ed12d551 | |
sammy | e0694c8862 | |
TorchedSammy | d27ce26be0 |
|
@ -0,0 +1,6 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = tab
|
|
@ -25,9 +25,11 @@ jobs:
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '1.17.7'
|
go-version: '1.18.8'
|
||||||
|
- name: Download Task
|
||||||
|
run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
|
||||||
- name: Build
|
- name: Build
|
||||||
run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} make
|
run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} ./bin/task
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
if: matrix.goos == 'windows'
|
if: matrix.goos == 'windows'
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -33,10 +33,13 @@ jobs:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
- name: Download Task
|
||||||
|
run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
|
||||||
- uses: wangyoucao577/go-release-action@v1.25
|
- uses: wangyoucao577/go-release-action@v1.25
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
goos: ${{ matrix.goos }}
|
goos: ${{ matrix.goos }}
|
||||||
goarch: ${{ matrix.goarch }}
|
goarch: ${{ matrix.goarch }}
|
||||||
|
build_command: task
|
||||||
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,31 @@
|
||||||
|
name: Build website
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- website
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup Hugo
|
||||||
|
uses: peaceiris/actions-hugo@v2
|
||||||
|
with:
|
||||||
|
hugo-version: 'latest'
|
||||||
|
extended: true
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: 'cd website && hugo --minify'
|
||||||
|
|
||||||
|
- name: Deploy
|
||||||
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
publish_dir: ./website/public
|
|
@ -9,8 +9,6 @@ local function doPrompt(fail)
|
||||||
))
|
))
|
||||||
end
|
end
|
||||||
|
|
||||||
print(lunacolors.format(hilbish.greeting))
|
|
||||||
|
|
||||||
doPrompt()
|
doPrompt()
|
||||||
|
|
||||||
bait.catch('command.exit', function(code)
|
bait.catch('command.exit', function(code)
|
||||||
|
|
68
CHANGELOG.md
68
CHANGELOG.md
|
@ -1,6 +1,19 @@
|
||||||
# 🎀 Changelog
|
# 🎀 Changelog
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
**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/#/
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Inline hints, akin to fish and the others.
|
- Inline hints, akin to fish and the others.
|
||||||
To make a handler for hint text, you can set the `hilbish.hinter` function.
|
To make a handler for hint text, you can set the `hilbish.hinter` function.
|
||||||
|
@ -52,11 +65,29 @@ having and using multiple runners.
|
||||||
- `fs.basename(path)` gets the basename of path
|
- `fs.basename(path)` gets the basename of path
|
||||||
- `fs.dir(path)` gets the directory part of path
|
- `fs.dir(path)` gets the directory part of path
|
||||||
- `fs.glob(pattern)` globs files and directories based on patterns
|
- `fs.glob(pattern)` globs files and directories based on patterns
|
||||||
|
- `fs.join(dirs...)` joins directories by OS dir separator
|
||||||
- .. and 2 properties
|
- .. and 2 properties
|
||||||
- `fs.pathSep` is the separator for filesystem paths and directories
|
- `fs.pathSep` is the separator for filesystem paths and directories
|
||||||
- `fs.pathListSep` is the separator for $PATH env entries
|
- `fs.pathListSep` is the separator for $PATH env entries
|
||||||
- Lua modules located in `hilbish.userDir.data .. '/hilbish/start'` (like `~/.local/share/hilbish/start/foo/init.lua`)
|
- Lua modules located in `hilbish.userDir.data .. '/hilbish/start'` (like `~/.local/share/hilbish/start/foo/init.lua`)
|
||||||
will be ran on startup
|
will be ran on startup
|
||||||
|
- `hilbish.init` hook, thrown after Hilbish has initialized Lua side
|
||||||
|
- Message of the day on startup (`hilbish.motd`), mainly intended as quick
|
||||||
|
small news pieces for releases. It is printed by default. To disable it,
|
||||||
|
set `hilbish.opts.motd` to false.
|
||||||
|
- `history` opt has been added and is true by default. Setting it to false
|
||||||
|
disables commands being added to history.
|
||||||
|
- `hilbish.rawInput` hook for input from the readline library
|
||||||
|
- Completion of files in quotes
|
||||||
|
- A new and "safer" event emitter has been added. This causes a performance deficit, but avoids a lot of
|
||||||
|
random errors introduced with the new Lua runtime (see [#197])
|
||||||
|
- `bait.release(name, catcher)` removes `handler` for the named `event`
|
||||||
|
- `exec`, `clear` and `cat` builtin commands
|
||||||
|
- `hilbish.cancel` hook
|
||||||
|
- 1st item on history is now inserted when history search menu is opened ([#148])
|
||||||
|
|
||||||
|
[#148]: https://github.com/Rosettea/Hilbish/issues/148
|
||||||
|
[#197]: https://github.com/Rosettea/Hilbish/issues/197
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- **Breaking Change:** Upgraded to Lua 5.4.
|
- **Breaking Change:** Upgraded to Lua 5.4.
|
||||||
|
@ -73,11 +104,17 @@ User input has been added to the return to account for runners wanting to
|
||||||
prompt for continued input, and to add it properly to history. `continue`
|
prompt for continued input, and to add it properly to history. `continue`
|
||||||
got added so that it would be easier for runners to get continued input
|
got added so that it would be easier for runners to get continued input
|
||||||
without having to actually handle it at all.
|
without having to actually handle it at all.
|
||||||
|
|
||||||
- **Breaking Change:** Job objects and timers are now Lua userdata instead
|
- **Breaking Change:** Job objects and timers are now Lua userdata instead
|
||||||
of a table, so their functions require you to call them with a colon instead
|
of a table, so their functions require you to call them with a colon instead
|
||||||
of a dot. (ie. `job.stop()` -> `job:stop()`)
|
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
|
||||||
|
always printed by default. To disable it, set the opt to false.
|
||||||
|
- **Breaking Change:** `command.no-perm` hook has been replaced with `command.not-executable`
|
||||||
|
- History is now fetched from Lua, which means users can override `hilbish.history`
|
||||||
|
methods to make it act how they want.
|
||||||
|
- `guide` has been removed. See the [website](https://rosettea.github.io/Hilbish/)
|
||||||
|
for general tips and documentation
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- If in Vim replace mode, input at the end of the line inserts instead of
|
- If in Vim replace mode, input at the end of the line inserts instead of
|
||||||
|
@ -112,6 +149,33 @@ for explanation.
|
||||||
Lua `job.stop` function.
|
Lua `job.stop` function.
|
||||||
- Jobs are always started in sh exec handler now instead of only successful start.
|
- Jobs are always started in sh exec handler now instead of only successful start.
|
||||||
- SIGTERM is handled properly now, which means stopping jobs and timers.
|
- SIGTERM is handled properly now, which means stopping jobs and timers.
|
||||||
|
- Fix panic on trailing newline on pasted multiline text.
|
||||||
|
- Completions will no longer be refreshed if the prompt refreshes while the
|
||||||
|
menu is open.
|
||||||
|
- Print error on search fail instead of panicking
|
||||||
|
- Windows related fixes:
|
||||||
|
- `hilbish.dataDir` now has tilde (`~`) expanded.
|
||||||
|
- Arrow keys now work on Windows terminals.
|
||||||
|
- Escape codes now work.
|
||||||
|
- Escape percentage symbols in completion entries, so you will no longer see
|
||||||
|
an error of missing format variable
|
||||||
|
- Fix an error with sh syntax in aliases
|
||||||
|
- Prompt now works with east asian characters (CJK)
|
||||||
|
- Set back the prompt to normal after exiting the continue prompt with ctrl-d
|
||||||
|
- 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
|
||||||
|
- 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
|
||||||
|
|
||||||
|
## [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
|
||||||
|
@ -536,6 +600,8 @@ This input for example will prompt for more input to complete:
|
||||||
|
|
||||||
First "stable" release of Hilbish.
|
First "stable" release of Hilbish.
|
||||||
|
|
||||||
|
[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
|
||||||
|
|
30
Makefile
30
Makefile
|
@ -1,30 +0,0 @@
|
||||||
PREFIX ?= /usr
|
|
||||||
BINDIR ?= $(PREFIX)/bin
|
|
||||||
LIBDIR ?= $(PREFIX)/share/hilbish
|
|
||||||
|
|
||||||
MY_GOFLAGS = -ldflags "-s -w"
|
|
||||||
|
|
||||||
all: dev
|
|
||||||
|
|
||||||
dev: MY_GOFLAGS = -ldflags "-s -w -X main.gitCommit=$(shell git rev-parse --short HEAD) -X main.gitBranch=$(shell git rev-parse --abbrev-ref HEAD)"
|
|
||||||
dev: build
|
|
||||||
|
|
||||||
build:
|
|
||||||
go build $(MY_GOFLAGS)
|
|
||||||
|
|
||||||
install:
|
|
||||||
install -v -d "$(DESTDIR)$(BINDIR)/" && install -m 0755 -v hilbish "$(DESTDIR)$(BINDIR)/hilbish"
|
|
||||||
mkdir -p "$(DESTDIR)$(LIBDIR)"
|
|
||||||
cp -r libs docs emmyLuaDocs nature .hilbishrc.lua "$(DESTDIR)$(LIBDIR)"
|
|
||||||
grep -qxF "$(DESTDIR)$(BINDIR)/hilbish" /etc/shells || echo "$(DESTDIR)$(BINDIR)/hilbish" >> /etc/shells
|
|
||||||
|
|
||||||
uninstall:
|
|
||||||
rm -vrf \
|
|
||||||
"$(DESTDIR)$(BINDIR)/hilbish" \
|
|
||||||
"$(DESTDIR)$(LIBDIR)"
|
|
||||||
sed -i '/hilbish/d' /etc/shells
|
|
||||||
|
|
||||||
clean:
|
|
||||||
go clean
|
|
||||||
|
|
||||||
.PHONY: all dev build install uninstall clean
|
|
26
README.md
26
README.md
|
@ -31,7 +31,6 @@ and aims to be infinitely configurable. If something isn't, open an issue!
|
||||||
- [AUR](#AUR)
|
- [AUR](#AUR)
|
||||||
- [Nixpkgs](#Nixpkgs)
|
- [Nixpkgs](#Nixpkgs)
|
||||||
- [Manual Build](#Manual-Build)
|
- [Manual Build](#Manual-Build)
|
||||||
- [Getting Started](#Getting-Started)
|
|
||||||
- [Contributing](#Contributing)
|
- [Contributing](#Contributing)
|
||||||
|
|
||||||
# Screenshots
|
# Screenshots
|
||||||
|
@ -42,6 +41,10 @@ and aims to be infinitely configurable. If something isn't, open an issue!
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
**NOTE:** Hilbish is not guaranteed to work properly on Windows, starting
|
||||||
|
from the 2.0 version. It will still be able to compile, but functionality
|
||||||
|
may be lacking.
|
||||||
|
|
||||||
## Prebuilt binaries
|
## Prebuilt binaries
|
||||||
Go [here](https://nightly.link/Rosettea/Hilbish/workflows/build/master) for
|
Go [here](https://nightly.link/Rosettea/Hilbish/workflows/build/master) for
|
||||||
builds on the master branch.
|
builds on the master branch.
|
||||||
|
@ -66,6 +69,7 @@ If you're new to nix you should probably read up on how to do that [here](https:
|
||||||
## Manual Build
|
## Manual Build
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
- [Go 1.17+](https://go.dev)
|
- [Go 1.17+](https://go.dev)
|
||||||
|
- [Task](https://taskfile.dev/installation/) (**Go on the hyperlink here to see Task's install method for your OS.**)
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
First, clone Hilbish. The recursive is required, as some Lua libraries
|
First, clone Hilbish. The recursive is required, as some Lua libraries
|
||||||
|
@ -78,30 +82,16 @@ go get -d ./...
|
||||||
|
|
||||||
To build, run:
|
To build, run:
|
||||||
```
|
```
|
||||||
make dev
|
task
|
||||||
```
|
```
|
||||||
|
|
||||||
Or, if you want a stable branch, run these commands:
|
Or, if you want a stable branch, run these commands:
|
||||||
```
|
```
|
||||||
git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
|
git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
|
||||||
make build
|
task build
|
||||||
```
|
```
|
||||||
|
|
||||||
After you did all that, run `sudo make install` to install Hilbish globally.
|
After you did all that, run `sudo task install` to install Hilbish globally.
|
||||||
|
|
||||||
# Getting Started
|
|
||||||
At startup, you should see a message which says to run a `guide` command.
|
|
||||||
This guide is a *very* simple and basic step through text of what Hilbish is
|
|
||||||
and where to find documentation.
|
|
||||||
|
|
||||||
Documentation is primarily viewed via the in shell `doc` command.
|
|
||||||
Autogenerated function docs and general docs about other things are included
|
|
||||||
there, so be sure to read it.
|
|
||||||
|
|
||||||
Using Hilbish is the same as using any other Linux shell, with an addition
|
|
||||||
that you can also run Lua. Hilbish can also act as an enhanced Lua REPL
|
|
||||||
via `hilbish.runnerMode 'lua'`. To switch back to normal, use
|
|
||||||
`hilbish.runnerMode 'hybrid'`.
|
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
Any kind of contributions are welcome! Hilbish is very easy to contribute to.
|
Any kind of contributions are welcome! Hilbish is very easy to contribute to.
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# https://taskfile.dev
|
||||||
|
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
vars:
|
||||||
|
PREFIX: '{{default "/usr/local" .PREFIX}}'
|
||||||
|
bindir__: '{{.PREFIX}}/bin'
|
||||||
|
BINDIR: '{{default .bindir__ .BINDIR}}'
|
||||||
|
libdir__: '{{.PREFIX}}/share/hilbish'
|
||||||
|
LIBDIR: '{{default .libdir__ .LIBDIR}}'
|
||||||
|
GOFLAGS: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}}"'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- CGO_ENABLED=0 go build {{.GOFLAGS}}
|
||||||
|
vars:
|
||||||
|
GOFLAGS: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}} -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"'
|
||||||
|
|
||||||
|
build:
|
||||||
|
cmds:
|
||||||
|
- CGO_ENABLED=0 go build {{.GOFLAGS}}
|
||||||
|
|
||||||
|
install:
|
||||||
|
cmds:
|
||||||
|
- install -v -d "{{.DESTDIR}}{{.BINDIR}}/" && install -m 0755 -v hilbish "{{.DESTDIR}}{{.BINDIR}}/hilbish"
|
||||||
|
- mkdir -p "{{.DESTDIR}}{{.LIBDIR}}"
|
||||||
|
- cp -r libs docs emmyLuaDocs nature .hilbishrc.lua {{.DESTDIR}}{{.LIBDIR}}
|
||||||
|
- grep -qxF "{{.DESTDIR}}{{.BINDIR}}/hilbish" /etc/shells || echo "{{.DESTDIR}}{{.BINDIR}}/hilbish" >> /etc/shells
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
cmds:
|
||||||
|
- rm -vrf
|
||||||
|
"{{.DESTDIR}}{{.BINDIR}}/hilbish"
|
||||||
|
"{{.DESTDIR}}{{.LIBDIR}}"
|
||||||
|
- sed -i '/hilbish/d' /etc/shells
|
30
api.go
30
api.go
|
@ -44,7 +44,6 @@ var exports = map[string]util.LuaExport{
|
||||||
"which": {hlwhich, 1, false},
|
"which": {hlwhich, 1, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
var greeting string
|
|
||||||
var hshMod *rt.Table
|
var hshMod *rt.Table
|
||||||
var hilbishLoader = packagelib.Loader{
|
var hilbishLoader = packagelib.Loader{
|
||||||
Load: hilbishLoad,
|
Load: hilbishLoad,
|
||||||
|
@ -103,10 +102,6 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
greeting = `Welcome to {magenta}Hilbish{reset}, {cyan}` + username + `{reset}.
|
|
||||||
The nice lil shell for {blue}Lua{reset} fanatics!
|
|
||||||
Check out the {blue}{bold}guide{reset} command to get started.
|
|
||||||
`
|
|
||||||
util.SetFieldProtected(fakeMod, mod, "ver", rt.StringValue(getVersion()), "Hilbish version")
|
util.SetFieldProtected(fakeMod, mod, "ver", rt.StringValue(getVersion()), "Hilbish version")
|
||||||
util.SetFieldProtected(fakeMod, mod, "user", rt.StringValue(username), "Username of user")
|
util.SetFieldProtected(fakeMod, mod, "user", rt.StringValue(username), "Username of user")
|
||||||
util.SetFieldProtected(fakeMod, mod, "host", rt.StringValue(host), "Host name of the machine")
|
util.SetFieldProtected(fakeMod, mod, "host", rt.StringValue(host), "Host name of the machine")
|
||||||
|
@ -114,7 +109,6 @@ Check out the {blue}{bold}guide{reset} command to get started.
|
||||||
util.SetFieldProtected(fakeMod, mod, "dataDir", rt.StringValue(dataDir), "Directory for Hilbish's data files")
|
util.SetFieldProtected(fakeMod, mod, "dataDir", rt.StringValue(dataDir), "Directory for Hilbish's data files")
|
||||||
util.SetFieldProtected(fakeMod, mod, "interactive", rt.BoolValue(interactive), "If this is an interactive shell")
|
util.SetFieldProtected(fakeMod, mod, "interactive", rt.BoolValue(interactive), "If this is an interactive shell")
|
||||||
util.SetFieldProtected(fakeMod, mod, "login", rt.BoolValue(login), "Whether this is a login shell")
|
util.SetFieldProtected(fakeMod, mod, "login", rt.BoolValue(login), "Whether this is a login shell")
|
||||||
util.SetFieldProtected(fakeMod, mod, "greeting", rt.StringValue(greeting), "Hilbish's welcome message for interactive shells. It has Lunacolors formatting.")
|
|
||||||
util.SetFieldProtected(fakeMod, mod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)")
|
util.SetFieldProtected(fakeMod, mod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)")
|
||||||
util.SetFieldProtected(fakeMod, mod, "exitCode", rt.IntValue(0), "Exit code of last exected command")
|
util.SetFieldProtected(fakeMod, mod, "exitCode", rt.IntValue(0), "Exit code of last exected command")
|
||||||
util.Document(fakeMod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.")
|
util.Document(fakeMod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.")
|
||||||
|
@ -195,7 +189,7 @@ 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), "Current Vim mode of Hilbish (nil if not in Vim mode)")
|
||||||
hooks.Em.Emit("hilbish.vimMode", mode)
|
hooks.Emit("hilbish.vimMode", mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unsetVimMode() {
|
func unsetVimMode() {
|
||||||
|
@ -256,21 +250,27 @@ func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// read(prompt) -> input?
|
// read(prompt?) -> input?
|
||||||
// Read input from the user, using Hilbish's line editor/input reader.
|
// Read input from the user, using Hilbish's line editor/input reader.
|
||||||
// This is a separate instance from the one Hilbish actually uses.
|
// This is a separate instance from the one Hilbish actually uses.
|
||||||
// Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
|
// Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
|
||||||
// --- @param prompt string
|
// --- @param prompt string
|
||||||
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 {
|
||||||
|
|
101
complete.go
101
complete.go
|
@ -11,11 +11,77 @@ import (
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var charEscapeMap = []string{
|
||||||
|
"\"", "\\\"",
|
||||||
|
"'", "\\'",
|
||||||
|
"`", "\\`",
|
||||||
|
" ", "\\ ",
|
||||||
|
"(", "\\(",
|
||||||
|
")", "\\)",
|
||||||
|
"[", "\\[",
|
||||||
|
"]", "\\]",
|
||||||
|
"$", "\\$",
|
||||||
|
"&", "\\&",
|
||||||
|
"*", "\\*",
|
||||||
|
">", "\\>",
|
||||||
|
"<", "\\<",
|
||||||
|
"|", "\\|",
|
||||||
|
}
|
||||||
|
var charEscapeMapInvert = invert(charEscapeMap)
|
||||||
|
var escapeReplaer = strings.NewReplacer(charEscapeMap...)
|
||||||
|
var escapeInvertReplaer = strings.NewReplacer(charEscapeMapInvert...)
|
||||||
|
|
||||||
|
func invert(m []string) []string {
|
||||||
|
newM := make([]string, len(charEscapeMap))
|
||||||
|
for i := range m {
|
||||||
|
if (i + 1) % 2 == 0 {
|
||||||
|
newM[i] = m[i - 1]
|
||||||
|
newM[i - 1] = m[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newM
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitForFile(str string) []string {
|
||||||
|
split := []string{}
|
||||||
|
sb := &strings.Builder{}
|
||||||
|
quoted := false
|
||||||
|
|
||||||
|
for i, r := range str {
|
||||||
|
if r == '"' {
|
||||||
|
quoted = !quoted
|
||||||
|
sb.WriteRune(r)
|
||||||
|
} else if r == ' ' && str[i - 1] == '\\' {
|
||||||
|
sb.WriteRune(r)
|
||||||
|
} else if !quoted && r == ' ' {
|
||||||
|
split = append(split, sb.String())
|
||||||
|
sb.Reset()
|
||||||
|
} else {
|
||||||
|
sb.WriteRune(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(str, " ") {
|
||||||
|
split = append(split, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if sb.Len() > 0 {
|
||||||
|
split = append(split, sb.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return split
|
||||||
|
}
|
||||||
|
|
||||||
func fileComplete(query, ctx string, fields []string) ([]string, string) {
|
func fileComplete(query, ctx string, fields []string) ([]string, string) {
|
||||||
return matchPath(query)
|
q := splitForFile(ctx)
|
||||||
|
|
||||||
|
return matchPath(q[len(q) - 1])
|
||||||
}
|
}
|
||||||
|
|
||||||
func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
||||||
|
q := splitForFile(ctx)
|
||||||
|
query = q[len(q) - 1]
|
||||||
|
|
||||||
var completions []string
|
var completions []string
|
||||||
|
|
||||||
prefixes := []string{"./", "../", "/", "~/"}
|
prefixes := []string{"./", "../", "/", "~/"}
|
||||||
|
@ -25,7 +91,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)
|
||||||
|
@ -37,7 +103,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
|
||||||
|
@ -68,9 +133,12 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchPath(query string) ([]string, string) {
|
func matchPath(query string) ([]string, string) {
|
||||||
|
oldQuery := query
|
||||||
|
query = strings.TrimPrefix(query, "\"")
|
||||||
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 "."
|
||||||
|
@ -87,34 +155,21 @@ func matchPath(query string) ([]string, string) {
|
||||||
if file.IsDir() {
|
if file.IsDir() {
|
||||||
entry = entry + string(os.PathSeparator)
|
entry = entry + string(os.PathSeparator)
|
||||||
}
|
}
|
||||||
entry = escapeFilename(entry)
|
if !strings.HasPrefix(oldQuery, "\"") {
|
||||||
|
entry = escapeFilename(entry)
|
||||||
|
}
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func completionLoader(rtm *rt.Runtime) *rt.Table {
|
func completionLoader(rtm *rt.Runtime) *rt.Table {
|
||||||
|
|
|
@ -2,5 +2,11 @@ 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
|
catchOnce(name, cb) > Same as catch, but only runs the `cb` once and then removes the hook
|
||||||
|
|
||||||
|
hooks(name) -> {cb, cb...} > Returns a table with hooks on the event with `name`.
|
||||||
|
|
||||||
|
release(name, catcher) > Removes the `catcher` for the event with `name`
|
||||||
|
For this to work, `catcher` has to be the same function used to catch
|
||||||
|
an event, like one saved to a variable.
|
||||||
|
|
||||||
throw(name, ...args) > Throws a hook with `name` with the provided `args`
|
throw(name, ...args) > Throws a hook with `name` with the provided `args`
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,9 @@ filepath.Dir
|
||||||
glob(pattern) > Glob all files and directories that match the pattern.
|
glob(pattern) > Glob all files and directories that match the pattern.
|
||||||
For the rules, see Go's filepath.Glob
|
For the rules, see Go's filepath.Glob
|
||||||
|
|
||||||
|
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.
|
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`
|
readdir(dir) > Returns a table of files in `dir`
|
||||||
|
|
|
@ -41,7 +41,7 @@ These will be formatted and replaced with the appropriate values.
|
||||||
`%u` - Name of current user
|
`%u` - Name of current user
|
||||||
`%h` - Hostname of device
|
`%h` - Hostname of device
|
||||||
|
|
||||||
read(prompt) -> input? > Read input from the user, using Hilbish's line editor/input reader.
|
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.
|
This is a separate instance from the one Hilbish actually uses.
|
||||||
Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
|
Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,5 @@
|
||||||
|
|
||||||
+ `hilbish.vimAction` -> actionName, args > Sent when the user does a "vim action," being something
|
+ `hilbish.vimAction` -> actionName, args > Sent when the user does a "vim action," being something
|
||||||
like yanking or pasting text. See `doc vim-mode actions` for more info.
|
like yanking or pasting text. See `doc vim-mode actions` for more info.
|
||||||
|
|
||||||
|
+ `hilbish.cancel` > Sent when the user cancels their input with Ctrl-C.
|
||||||
|
|
|
@ -6,3 +6,8 @@ Here is the format for a doc for a hook:
|
||||||
|
|
||||||
`<args>` just means the arguments of the hook. If a hook doc has the format
|
`<args>` just means the arguments of the hook. If a hook doc has the format
|
||||||
of `arg...`, it means the hook can take/recieve any number of `arg`.
|
of `arg...`, it means the hook can take/recieve any number of `arg`.
|
||||||
|
|
||||||
|
+ error -> eventName, handler, err > Emitted when there is an error in
|
||||||
|
an event handler. The `eventName` is the name of the event the handler
|
||||||
|
is for, the `handler` is the callback function, and `err` is the error
|
||||||
|
message.
|
||||||
|
|
|
@ -11,6 +11,7 @@ func editorLoader(rtm *rt.Runtime) *rt.Table {
|
||||||
"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},
|
||||||
}
|
}
|
||||||
|
|
||||||
mod := rt.NewTable()
|
mod := rt.NewTable()
|
||||||
|
@ -68,3 +69,9 @@ func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
|
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func editorGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
buf := lr.rl.GetLine()
|
||||||
|
|
||||||
|
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,18 @@ function bait.catch(name, cb) end
|
||||||
--- @param cb function
|
--- @param cb function
|
||||||
function bait.catchOnce(name, cb) end
|
function bait.catchOnce(name, cb) end
|
||||||
|
|
||||||
|
--- Returns a table with hooks on the event with `name`.
|
||||||
|
--- @param name string
|
||||||
|
--- @returns table
|
||||||
|
function bait.hooks(name) end
|
||||||
|
|
||||||
|
--- Removes the `catcher` for the event with `name`
|
||||||
|
--- For this to work, `catcher` has to be the same function used to catch
|
||||||
|
--- an event, like one saved to a variable.
|
||||||
|
--- @param name string
|
||||||
|
--- @param catcher function
|
||||||
|
function bait.release(name, catcher) end
|
||||||
|
|
||||||
--- Throws a hook with `name` with the provided `args`
|
--- Throws a hook with `name` with the provided `args`
|
||||||
--- @param name string
|
--- @param name string
|
||||||
--- @vararg any
|
--- @vararg any
|
||||||
|
|
|
@ -22,6 +22,10 @@ function fs.dir() end
|
||||||
--- For the rules, see Go's filepath.Glob
|
--- For the rules, see Go's filepath.Glob
|
||||||
function fs.glob() end
|
function fs.glob() end
|
||||||
|
|
||||||
|
--- Takes paths and joins them together with the OS's
|
||||||
|
--- directory separator (forward or backward slash).
|
||||||
|
function fs.join() end
|
||||||
|
|
||||||
--- Makes a directory called `name`. If `recursive` is true, it will create its parent directories.
|
--- Makes a directory called `name`. If `recursive` is true, it will create its parent directories.
|
||||||
--- @param name string
|
--- @param name string
|
||||||
--- @param recursive boolean
|
--- @param recursive boolean
|
||||||
|
|
36
exec.go
36
exec.go
|
@ -85,7 +85,7 @@ func isExecError(err error) (execError, bool) {
|
||||||
func runInput(input string, priv bool) {
|
func runInput(input string, priv bool) {
|
||||||
running = true
|
running = true
|
||||||
cmdString := aliases.Resolve(input)
|
cmdString := aliases.Resolve(input)
|
||||||
hooks.Em.Emit("command.preexec", input, cmdString)
|
hooks.Emit("command.preexec", input, cmdString)
|
||||||
|
|
||||||
rerun:
|
rerun:
|
||||||
var exitCode uint8
|
var exitCode uint8
|
||||||
|
@ -96,7 +96,7 @@ 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
|
||||||
|
@ -108,9 +108,9 @@ func runInput(input string, priv bool) {
|
||||||
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(input)
|
input, exitCode, cont, err = handleSh(input)
|
||||||
}
|
}
|
||||||
|
@ -140,10 +140,10 @@ func runInput(input string, priv bool) {
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if exErr, ok := isExecError(err); ok {
|
if exErr, ok := isExecError(err); ok {
|
||||||
hooks.Em.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)
|
||||||
}
|
}
|
||||||
|
@ -152,6 +152,7 @@ func reprompt(input string) (string, error) {
|
||||||
for {
|
for {
|
||||||
in, err := continuePrompt(strings.TrimSuffix(input, "\\"))
|
in, err := continuePrompt(strings.TrimSuffix(input, "\\"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
lr.SetPrompt(fmtPrompt(prompt))
|
||||||
return input, err
|
return input, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +195,8 @@ func runLuaRunner(runr rt.Value, userInput string) (input string, exitCode uint8
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLua(cmdString string) (string, uint8, error) {
|
func handleLua(input string) (string, uint8, error) {
|
||||||
|
cmdString := aliases.Resolve(input)
|
||||||
// First try to load input, essentially compiling to bytecode
|
// First try to load input, essentially compiling to bytecode
|
||||||
chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv()))
|
chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv()))
|
||||||
if err != nil && noexecute {
|
if err != nil && noexecute {
|
||||||
|
@ -220,7 +222,17 @@ func handleLua(cmdString string) (string, uint8, error) {
|
||||||
return cmdString, 125, err
|
return cmdString, 125, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSh(cmdString string) (string, uint8, bool, error) {
|
func handleSh(cmdString string) (input string, exitCode uint8, cont bool, runErr error) {
|
||||||
|
shRunner := hshMod.Get(rt.StringValue("runner")).AsTable().Get(rt.StringValue("sh"))
|
||||||
|
var err error
|
||||||
|
input, exitCode, cont, runErr, err = runLuaRunner(shRunner, cmdString)
|
||||||
|
if err != nil {
|
||||||
|
runErr = err
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func execSh(cmdString string) (string, uint8, bool, error) {
|
||||||
_, _, err := execCommand(cmdString, true)
|
_, _, err := execCommand(cmdString, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If input is incomplete, start multiline prompting
|
// If input is incomplete, start multiline prompting
|
||||||
|
@ -540,13 +552,9 @@ func splitInput(input string) ([]string, string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdFinish(code uint8, cmdstr string, private bool) {
|
func cmdFinish(code uint8, cmdstr string, private bool) {
|
||||||
// if input has space at the beginning, dont put in history
|
|
||||||
if interactive && !private {
|
|
||||||
handleHistory(cmdstr)
|
|
||||||
}
|
|
||||||
util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code)), "Exit code of last exected command")
|
util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code)), "Exit code of last exected command")
|
||||||
// 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
|
||||||
hooks.Em.Emit("command.exit", rt.IntValue(int64(code)), cmdstr)
|
hooks.Emit("command.exit", rt.IntValue(int64(code)), cmdstr, private)
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -29,4 +29,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-20220621002945-b05143999437
|
replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -4,6 +4,8 @@ github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3 h1:I/wWr40FFLFF9pbT
|
||||||
github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
||||||
github.com/Rosettea/golua v0.0.0-20220621002945-b05143999437 h1:6lWu4YVLeKuZ8jR9xwHONhkHBsrIbw5dpfG1gtOVw0A=
|
github.com/Rosettea/golua v0.0.0-20220621002945-b05143999437 h1:6lWu4YVLeKuZ8jR9xwHONhkHBsrIbw5dpfG1gtOVw0A=
|
||||||
github.com/Rosettea/golua v0.0.0-20220621002945-b05143999437/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
github.com/Rosettea/golua v0.0.0-20220621002945-b05143999437/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
||||||
|
github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345 h1:QNYjYDogUSiNUkffbhFSrSCtpZhofeiVYGFN2FI4wSs=
|
||||||
|
github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
||||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e h1:P2XupP8SaylWaudD1DqbWtZ3mIa8OsE9635LmR+Q+lg=
|
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.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=
|
||||||
|
|
|
@ -1,27 +1,43 @@
|
||||||
package bait
|
package bait
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"errors"
|
||||||
|
|
||||||
"hilbish/util"
|
"hilbish/util"
|
||||||
|
|
||||||
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/chuckpreslar/emission"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Bait struct{
|
type listenerType int
|
||||||
Em *emission.Emitter
|
const (
|
||||||
Loader packagelib.Loader
|
goListener listenerType = iota
|
||||||
|
luaListener
|
||||||
|
)
|
||||||
|
|
||||||
|
// Recoverer is a function which is called when a panic occurs in an event.
|
||||||
|
type Recoverer func(event string, handler *Listener, err interface{})
|
||||||
|
|
||||||
|
// Listener is a struct that holds the handler for an event.
|
||||||
|
type Listener struct{
|
||||||
|
typ listenerType
|
||||||
|
once bool
|
||||||
|
caller func(...interface{})
|
||||||
|
luaCaller *rt.Closure
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() Bait {
|
type Bait struct{
|
||||||
emitter := emission.NewEmitter()
|
Loader packagelib.Loader
|
||||||
emitter.RecoverWith(func(hookname, hookfunc interface{}, err error) {
|
recoverer Recoverer
|
||||||
emitter.Off(hookname, hookfunc)
|
handlers map[string][]*Listener
|
||||||
fmt.Println(err)
|
rtm *rt.Runtime
|
||||||
})
|
}
|
||||||
b := Bait{
|
|
||||||
Em: emitter,
|
// New creates a new Bait instance.
|
||||||
|
func New(rtm *rt.Runtime) *Bait {
|
||||||
|
b := &Bait{
|
||||||
|
handlers: make(map[string][]*Listener),
|
||||||
|
rtm: rtm,
|
||||||
}
|
}
|
||||||
b.Loader = packagelib.Loader{
|
b.Loader = packagelib.Loader{
|
||||||
Load: b.loaderFunc,
|
Load: b.loaderFunc,
|
||||||
|
@ -31,11 +47,153 @@ func New() Bait {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emit throws an event.
|
||||||
|
func (b *Bait) Emit(event string, args ...interface{}) {
|
||||||
|
handles := b.handlers[event]
|
||||||
|
if handles == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, handle := range handles {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
b.callRecoverer(event, handle, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if handle.typ == luaListener {
|
||||||
|
funcVal := rt.FunctionValue(handle.luaCaller)
|
||||||
|
var luaArgs []rt.Value
|
||||||
|
for _, arg := range args {
|
||||||
|
var luarg rt.Value
|
||||||
|
switch arg.(type) {
|
||||||
|
case rt.Value: luarg = arg.(rt.Value)
|
||||||
|
default: luarg = rt.AsValue(arg)
|
||||||
|
}
|
||||||
|
luaArgs = append(luaArgs, luarg)
|
||||||
|
}
|
||||||
|
_, err := rt.Call1(b.rtm.MainThread(), funcVal, luaArgs...)
|
||||||
|
if err != nil {
|
||||||
|
if event != "error" {
|
||||||
|
b.Emit("error", event, handle.luaCaller, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// if there is an error in an error event handler, panic instead
|
||||||
|
// (calls the go recoverer function)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handle.caller(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if handle.once {
|
||||||
|
b.removeListener(event, idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On adds a Go function handler for an event.
|
||||||
|
func (b *Bait) On(event string, handler func(...interface{})) *Listener {
|
||||||
|
listener := &Listener{
|
||||||
|
typ: goListener,
|
||||||
|
caller: handler,
|
||||||
|
}
|
||||||
|
|
||||||
|
b.addListener(event, listener)
|
||||||
|
return listener
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnLua adds a Lua function handler for an event.
|
||||||
|
func (b *Bait) OnLua(event string, handler *rt.Closure) *Listener {
|
||||||
|
listener :=&Listener{
|
||||||
|
typ: luaListener,
|
||||||
|
luaCaller: handler,
|
||||||
|
}
|
||||||
|
b.addListener(event, listener)
|
||||||
|
|
||||||
|
return listener
|
||||||
|
}
|
||||||
|
|
||||||
|
// Off removes a Go function handler for an event.
|
||||||
|
func (b *Bait) Off(event string, listener *Listener) {
|
||||||
|
handles := b.handlers[event]
|
||||||
|
|
||||||
|
for i, handle := range handles {
|
||||||
|
if handle == listener {
|
||||||
|
b.removeListener(event, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OffLua removes a Lua function handler for an event.
|
||||||
|
func (b *Bait) OffLua(event string, handler *rt.Closure) {
|
||||||
|
handles := b.handlers[event]
|
||||||
|
|
||||||
|
for i, handle := range handles {
|
||||||
|
if handle.luaCaller == handler {
|
||||||
|
b.removeListener(event, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once adds a Go function listener for an event that only runs once.
|
||||||
|
func (b *Bait) Once(event string, handler func(...interface{})) *Listener {
|
||||||
|
listener := &Listener{
|
||||||
|
typ: goListener,
|
||||||
|
once: true,
|
||||||
|
caller: handler,
|
||||||
|
}
|
||||||
|
b.addListener(event, listener)
|
||||||
|
|
||||||
|
return listener
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnceLua adds a Lua function listener for an event that only runs once.
|
||||||
|
func (b *Bait) OnceLua(event string, handler *rt.Closure) *Listener {
|
||||||
|
listener := &Listener{
|
||||||
|
typ: luaListener,
|
||||||
|
once: true,
|
||||||
|
luaCaller: handler,
|
||||||
|
}
|
||||||
|
b.addListener(event, listener)
|
||||||
|
|
||||||
|
return listener
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRecoverer sets the function to be executed when a panic occurs in an event.
|
||||||
|
func (b *Bait) SetRecoverer(recoverer Recoverer) {
|
||||||
|
b.recoverer = recoverer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bait) addListener(event string, listener *Listener) {
|
||||||
|
if b.handlers[event] == nil {
|
||||||
|
b.handlers[event] = []*Listener{}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.handlers[event] = append(b.handlers[event], listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (b *Bait) removeListener(event string, idx int) {
|
||||||
|
b.handlers[event][idx] = b.handlers[event][len(b.handlers[event]) - 1]
|
||||||
|
|
||||||
|
b.handlers[event] = b.handlers[event][:len(b.handlers[event]) - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bait) callRecoverer(event string, handler *Listener, err interface{}) {
|
||||||
|
if b.recoverer == nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
b.recoverer(event, handler, err)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
exports := map[string]util.LuaExport{
|
exports := map[string]util.LuaExport{
|
||||||
"catch": util.LuaExport{b.bcatch, 2, false},
|
"catch": util.LuaExport{b.bcatch, 2, false},
|
||||||
"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},
|
||||||
|
"hooks": util.LuaExport{b.bhooks, 1, false},
|
||||||
}
|
}
|
||||||
mod := rt.NewTable()
|
mod := rt.NewTable()
|
||||||
util.SetExports(rtm, mod, exports)
|
util.SetExports(rtm, mod, exports)
|
||||||
|
@ -89,7 +247,7 @@ func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
for i, v := range c.Etc() {
|
for i, v := range c.Etc() {
|
||||||
ifaceSlice[i] = v
|
ifaceSlice[i] = v
|
||||||
}
|
}
|
||||||
b.Em.Emit(name, ifaceSlice...)
|
b.Emit(name, ifaceSlice...)
|
||||||
|
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
@ -104,9 +262,7 @@ func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.Em.On(name, func(args ...interface{}) {
|
b.OnLua(name, catcher)
|
||||||
handleHook(t, c, name, catcher, args...)
|
|
||||||
})
|
|
||||||
|
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
@ -121,9 +277,56 @@ func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.Em.Once(name, func(args ...interface{}) {
|
b.OnceLua(name, catcher)
|
||||||
handleHook(t, c, name, catcher, args...)
|
|
||||||
})
|
|
||||||
|
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// release(name, catcher)
|
||||||
|
// Removes the `catcher` for the event with `name`
|
||||||
|
// For this to work, `catcher` has to be the same function used to catch
|
||||||
|
// an event, like one saved to a variable.
|
||||||
|
// --- @param name string
|
||||||
|
// --- @param catcher function
|
||||||
|
func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
name, catcher, err := util.HandleStrCallback(t, c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.OffLua(name, catcher)
|
||||||
|
|
||||||
|
return c.Next(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hooks(name) -> {cb, cb...}
|
||||||
|
// Returns a table with hooks on the event with `name`.
|
||||||
|
// --- @param name string
|
||||||
|
// --- @returns table
|
||||||
|
func (b *Bait) bhooks(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
evName, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
noHooks := errors.New("no hooks for event " + evName)
|
||||||
|
|
||||||
|
handlers := b.handlers[evName]
|
||||||
|
if handlers == nil {
|
||||||
|
return nil, noHooks
|
||||||
|
}
|
||||||
|
|
||||||
|
luaHandlers := rt.NewTable()
|
||||||
|
for _, handler := range handlers {
|
||||||
|
if handler.typ != luaListener { continue }
|
||||||
|
luaHandlers.Set(rt.IntValue(luaHandlers.Len() + 1), rt.FunctionValue(handler.luaCaller))
|
||||||
|
}
|
||||||
|
|
||||||
|
if luaHandlers.Len() == 0 {
|
||||||
|
return nil, noHooks
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.PushingNext1(t.Runtime, rt.TableValue(luaHandlers)), nil
|
||||||
|
}
|
||||||
|
|
|
@ -2,20 +2,20 @@ package commander
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"hilbish/util"
|
"hilbish/util"
|
||||||
|
"hilbish/golibs/bait"
|
||||||
|
|
||||||
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/chuckpreslar/emission"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Commander struct{
|
type Commander struct{
|
||||||
Events *emission.Emitter
|
Events *bait.Bait
|
||||||
Loader packagelib.Loader
|
Loader packagelib.Loader
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() Commander {
|
func New(rtm *rt.Runtime) Commander {
|
||||||
c := Commander{
|
c := Commander{
|
||||||
Events: emission.NewEmitter(),
|
Events: bait.New(rtm),
|
||||||
}
|
}
|
||||||
c.Loader = packagelib.Loader{
|
c.Loader = packagelib.Loader{
|
||||||
Load: c.loaderFunc,
|
Load: c.loaderFunc,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"os"
|
"os"
|
||||||
|
@ -27,6 +28,7 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
"basename": util.LuaExport{fbasename, 1, false},
|
"basename": util.LuaExport{fbasename, 1, false},
|
||||||
"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},
|
||||||
}
|
}
|
||||||
mod := rt.NewTable()
|
mod := rt.NewTable()
|
||||||
util.SetExports(rtm, mod, exports)
|
util.SetExports(rtm, mod, exports)
|
||||||
|
@ -216,3 +218,21 @@ func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
|
||||||
return c.PushingNext(t.Runtime, rt.TableValue(luaMatches)), nil
|
return c.PushingNext(t.Runtime, rt.TableValue(luaMatches)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// join(paths...)
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
60
history.go
60
history.go
|
@ -4,36 +4,84 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type luaHistory struct {}
|
||||||
|
|
||||||
|
func (h *luaHistory) Write(line string) (int, error) {
|
||||||
|
histWrite := hshMod.Get(rt.StringValue("history")).AsTable().Get(rt.StringValue("add"))
|
||||||
|
ln, err := rt.Call1(l.MainThread(), histWrite, rt.StringValue(line))
|
||||||
|
|
||||||
|
var num int64
|
||||||
|
if ln.Type() == rt.IntType {
|
||||||
|
num = ln.AsInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(num), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *luaHistory) GetLine(idx int) (string, error) {
|
||||||
|
histGet := hshMod.Get(rt.StringValue("history")).AsTable().Get(rt.StringValue("get"))
|
||||||
|
lcmd, err := rt.Call1(l.MainThread(), histGet, rt.IntValue(int64(idx)))
|
||||||
|
|
||||||
|
var cmd string
|
||||||
|
if lcmd.Type() == rt.StringType {
|
||||||
|
cmd = lcmd.AsString()
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *luaHistory) Len() int {
|
||||||
|
histSize := hshMod.Get(rt.StringValue("history")).AsTable().Get(rt.StringValue("size"))
|
||||||
|
ln, _ := rt.Call1(l.MainThread(), histSize)
|
||||||
|
|
||||||
|
var num int64
|
||||||
|
if ln.Type() == rt.IntType {
|
||||||
|
num = ln.AsInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(num)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *luaHistory) Dump() interface{} {
|
||||||
|
// hilbish.history interface already has all function, this isnt used in readline
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type fileHistory struct {
|
type fileHistory struct {
|
||||||
items []string
|
items []string
|
||||||
f *os.File
|
f *os.File
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFileHistory() *fileHistory {
|
func newFileHistory(path string) *fileHistory {
|
||||||
err := os.MkdirAll(defaultHistDir, 0755)
|
dir := filepath.Dir(path)
|
||||||
|
|
||||||
|
err := os.MkdirAll(dir, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := os.ReadFile(defaultHistPath)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(defaultHistPath, 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 {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,13 @@ package main
|
||||||
import "golang.org/x/sys/windows"
|
import "golang.org/x/sys/windows"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var mode uint32
|
// vt output (escape codes)
|
||||||
windows.GetConsoleMode(windows.Stdout, &mode)
|
var outMode uint32
|
||||||
windows.SetConsoleMode(windows.Stdout, mode | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
windows.GetConsoleMode(windows.Stdout, &outMode)
|
||||||
|
windows.SetConsoleMode(windows.Stdout, outMode | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
||||||
|
|
||||||
|
// vt input
|
||||||
|
var inMode uint32
|
||||||
|
windows.GetConsoleMode(windows.Stdin, &inMode)
|
||||||
|
windows.SetConsoleMode(windows.Stdin, inMode | windows.ENABLE_VIRTUAL_TERMINAL_INPUT)
|
||||||
}
|
}
|
||||||
|
|
6
job.go
6
job.go
|
@ -67,7 +67,7 @@ func (j *job) start() error {
|
||||||
j.pid = proc.Pid
|
j.pid = proc.Pid
|
||||||
j.running = true
|
j.running = true
|
||||||
|
|
||||||
hooks.Em.Emit("job.start", rt.UserDataValue(j.ud))
|
hooks.Emit("job.start", rt.UserDataValue(j.ud))
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ func (j *job) stop() {
|
||||||
|
|
||||||
func (j *job) finish() {
|
func (j *job) finish() {
|
||||||
j.running = false
|
j.running = false
|
||||||
hooks.Em.Emit("job.done", rt.UserDataValue(j.ud))
|
hooks.Emit("job.done", rt.UserDataValue(j.ud))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *job) wait() {
|
func (j *job) wait() {
|
||||||
|
@ -236,7 +236,7 @@ func (j *jobHandler) add(cmd string, args []string, path string) *job {
|
||||||
jb.ud = jobUserData(jb)
|
jb.ud = jobUserData(jb)
|
||||||
|
|
||||||
j.jobs[j.latestID] = jb
|
j.jobs[j.latestID] = jb
|
||||||
hooks.Em.Emit("job.add", rt.UserDataValue(jb.ud))
|
hooks.Emit("job.add", rt.UserDataValue(jb.ud))
|
||||||
|
|
||||||
return jb
|
return jb
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit d60cd77c73875b5bb55e5a2fdc30bae01a7ac499
|
Subproject commit 34a57c964590f89aa065188a588c7b38aff99c28
|
28
lua.go
28
lua.go
|
@ -12,12 +12,16 @@ import (
|
||||||
|
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
"github.com/arnodel/golua/lib"
|
"github.com/arnodel/golua/lib"
|
||||||
|
"github.com/arnodel/golua/lib/debuglib"
|
||||||
)
|
)
|
||||||
|
|
||||||
var minimalconf = `hilbish.prompt '& '`
|
var minimalconf = `hilbish.prompt '& '`
|
||||||
|
|
||||||
func luaInit() {
|
func luaInit() {
|
||||||
l = rt.New(os.Stdout)
|
l = rt.New(os.Stdout)
|
||||||
|
l.PushContext(rt.RuntimeContextDef{
|
||||||
|
MessageHandler: debuglib.Traceback,
|
||||||
|
})
|
||||||
lib.LoadAll(l)
|
lib.LoadAll(l)
|
||||||
|
|
||||||
lib.LoadLibs(l, hilbishLoader)
|
lib.LoadLibs(l, hilbishLoader)
|
||||||
|
@ -28,26 +32,40 @@ 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()
|
cmds := commander.New(l)
|
||||||
// When a command from Lua is added, register it for use
|
// When a command from Lua is added, register it for use
|
||||||
cmds.Events.On("commandRegister", func(cmdName string, cmd *rt.Closure) {
|
cmds.Events.On("commandRegister", func(args ...interface{}) {
|
||||||
|
cmdName := args[0].(string)
|
||||||
|
cmd := args[1].(*rt.Closure)
|
||||||
|
|
||||||
commands[cmdName] = cmd
|
commands[cmdName] = cmd
|
||||||
})
|
})
|
||||||
cmds.Events.On("commandDeregister", func(cmdName string) {
|
cmds.Events.On("commandDeregister", func(args ...interface{}) {
|
||||||
|
cmdName := args[0].(string)
|
||||||
|
|
||||||
delete(commands, cmdName)
|
delete(commands, cmdName)
|
||||||
})
|
})
|
||||||
lib.LoadLibs(l, cmds.Loader)
|
lib.LoadLibs(l, cmds.Loader)
|
||||||
|
|
||||||
hooks = bait.New()
|
hooks = bait.New(l)
|
||||||
|
hooks.SetRecoverer(func(event string, handler *bait.Listener, err interface{}) {
|
||||||
|
fmt.Println("Error in `error` hook handler:", err)
|
||||||
|
hooks.Off(event, handler)
|
||||||
|
})
|
||||||
|
|
||||||
lib.LoadLibs(l, hooks.Loader)
|
lib.LoadLibs(l, hooks.Loader)
|
||||||
|
|
||||||
// Add Ctrl-C handler
|
// Add Ctrl-C handler
|
||||||
hooks.Em.On("signal.sigint", func() {
|
hooks.On("signal.sigint", func(...interface{}) {
|
||||||
if !interactive {
|
if !interactive {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
lr.rl.RawInputCallback = func(r []rune) {
|
||||||
|
hooks.Emit("hilbish.rawInput", string(r))
|
||||||
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
|
|
23
main.go
23
main.go
|
@ -30,7 +30,7 @@ var (
|
||||||
userDataDir string
|
userDataDir string
|
||||||
curuser *user.User
|
curuser *user.User
|
||||||
|
|
||||||
hooks bait.Bait
|
hooks *bait.Bait
|
||||||
defaultConfPath string
|
defaultConfPath string
|
||||||
defaultHistPath string
|
defaultHistPath string
|
||||||
)
|
)
|
||||||
|
@ -116,8 +116,8 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
go handleSignals()
|
go handleSignals()
|
||||||
luaInit()
|
|
||||||
lr = newLineReader("", false)
|
lr = newLineReader("", false)
|
||||||
|
luaInit()
|
||||||
// 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
|
||||||
|
@ -138,6 +138,7 @@ func main() {
|
||||||
} else {
|
} else {
|
||||||
runConfig(*configflag)
|
runConfig(*configflag)
|
||||||
}
|
}
|
||||||
|
hooks.Emit("hilbish.init")
|
||||||
|
|
||||||
if fileInfo, _ := os.Stdin.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 {
|
if fileInfo, _ := os.Stdin.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 {
|
||||||
scanner := bufio.NewScanner(bufio.NewReader(os.Stdin))
|
scanner := bufio.NewScanner(bufio.NewReader(os.Stdin))
|
||||||
|
@ -176,15 +177,18 @@ input:
|
||||||
|
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
// Exit if user presses ^D (ctrl + d)
|
// Exit if user presses ^D (ctrl + d)
|
||||||
hooks.Em.Emit("hilbish.exit")
|
hooks.Emit("hilbish.exit")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != readline.CtrlC {
|
if err == readline.CtrlC {
|
||||||
|
fmt.Println("^C")
|
||||||
|
hooks.Emit("hilbish.cancel")
|
||||||
|
} else {
|
||||||
// If we get a completely random error, print
|
// If we get a completely random error, print
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
}
|
}
|
||||||
fmt.Println("^C")
|
// TODO: Halt if any other error occurs
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var priv bool
|
var priv bool
|
||||||
|
@ -195,7 +199,7 @@ input:
|
||||||
input = strings.TrimSpace(input)
|
input = strings.TrimSpace(input)
|
||||||
if len(input) == 0 {
|
if len(input) == 0 {
|
||||||
running = true
|
running = true
|
||||||
hooks.Em.Emit("command.exit", 0)
|
hooks.Emit("command.exit", 0)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +230,7 @@ input:
|
||||||
}
|
}
|
||||||
|
|
||||||
func continuePrompt(prev string) (string, error) {
|
func continuePrompt(prev string) (string, error) {
|
||||||
hooks.Em.Emit("multiline", nil)
|
hooks.Emit("multiline", nil)
|
||||||
lr.SetPrompt(multilinePrompt)
|
lr.SetPrompt(multilinePrompt)
|
||||||
cont, err := lr.Read()
|
cont, err := lr.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -268,11 +272,6 @@ func fmtPrompt(prompt string) string {
|
||||||
return nprompt
|
return nprompt
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleHistory(cmd string) {
|
|
||||||
lr.AddHistory(cmd)
|
|
||||||
// TODO: load history again (history shared between sessions like this ye)
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeDupes(slice []string) []string {
|
func removeDupes(slice []string) []string {
|
||||||
all := make(map[string]bool)
|
all := make(map[string]bool)
|
||||||
newSlice := []string{}
|
newSlice := []string{}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
local commander = require 'commander'
|
||||||
|
local fs = require 'fs'
|
||||||
|
|
||||||
|
commander.register('cat', function(args)
|
||||||
|
local exit = 0
|
||||||
|
|
||||||
|
if #args == 0 then
|
||||||
|
print [[
|
||||||
|
usage: cat [file]...]]
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, fName in ipairs(args) do
|
||||||
|
local f = io.open(fName)
|
||||||
|
if f == nil then
|
||||||
|
exit = 1
|
||||||
|
print(string.format('cat: %s: no such file or directory', fName))
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
io.write(f:read '*a')
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
io.flush()
|
||||||
|
return exit
|
||||||
|
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)
|
|
@ -0,0 +1,5 @@
|
||||||
|
local commander = require 'commander'
|
||||||
|
|
||||||
|
commander.register('exec', function(args)
|
||||||
|
hilbish.exec(args[1])
|
||||||
|
end)
|
|
@ -1,54 +0,0 @@
|
||||||
local ansikit = require 'ansikit'
|
|
||||||
local commander = require 'commander'
|
|
||||||
|
|
||||||
local helpTexts = {
|
|
||||||
[[
|
|
||||||
Hello there! Welcome to Hilbish, the comfy and nice little shell for
|
|
||||||
Lua users and fans. Hilbish is configured with Lua, and its
|
|
||||||
scripts are also in Lua. It also runs both Lua and shell script when
|
|
||||||
interactive (aka normal usage).
|
|
||||||
]],
|
|
||||||
[[
|
|
||||||
What does that mean for you, the user? It means that if you prefer to
|
|
||||||
use Lua for scripting instead of shell script but still have ordinary
|
|
||||||
shell usage for interactive use.
|
|
||||||
]],
|
|
||||||
[[
|
|
||||||
If this is your first time using Hilbish and Lua, check out the
|
|
||||||
Programming in Lua book here: https://www.lua.org/pil
|
|
||||||
After (or if you already know Lua) check out the doc command.
|
|
||||||
It is an in shell tool for documentation about Hilbish provided
|
|
||||||
functions and modules.
|
|
||||||
]],
|
|
||||||
[[
|
|
||||||
If you've updated from a pre-1.0 version (0.7.1 as an example)
|
|
||||||
you'll want to move your config from ~/.hilbishrc.lua to
|
|
||||||
]] ..
|
|
||||||
hilbish.userDir.config .. '/hilbish/init.lua' ..
|
|
||||||
[[
|
|
||||||
|
|
||||||
and also change all global functions (prompt, alias) to be
|
|
||||||
in the hilbish module (hilbish.prompt, hilbish.alias as examples).
|
|
||||||
|
|
||||||
And if this is your first time (most likely), you can copy a config
|
|
||||||
from ]] .. hilbish.dataDir,
|
|
||||||
[[
|
|
||||||
Since 1.0 is a big release, you'll want to check the changelog
|
|
||||||
at https://github.com/Rosettea/Hilbish/releases/tag/v1.0.0
|
|
||||||
to find more breaking changes.
|
|
||||||
]]
|
|
||||||
}
|
|
||||||
commander.register('guide', function()
|
|
||||||
ansikit.clear()
|
|
||||||
ansikit.cursorTo(0, 0)
|
|
||||||
for _, text in ipairs(helpTexts) do
|
|
||||||
print(text)
|
|
||||||
local out = hilbish.read('Hit enter to continue ')
|
|
||||||
ansikit.clear()
|
|
||||||
ansikit.cursorTo(0, 0)
|
|
||||||
if not out then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print 'Hope you enjoy using Hilbish!'
|
|
||||||
end)
|
|
|
@ -1,9 +1,19 @@
|
||||||
-- Add command builtins
|
local fs = require 'fs'
|
||||||
require 'nature.commands.cd'
|
|
||||||
require 'nature.commands.cdr'
|
-- explanation: this specific function gives to us info about
|
||||||
require 'nature.commands.doc'
|
-- the currently running source. this includes a path to the
|
||||||
require 'nature.commands.exit'
|
-- source file (info.source)
|
||||||
require 'nature.commands.guide'
|
-- we will use that to automatically load all commands by reading
|
||||||
require 'nature.commands.disown'
|
-- all the files in this dir and just requiring it.
|
||||||
require 'nature.commands.fg'
|
local info = debug.getinfo(1)
|
||||||
require 'nature.commands.bg'
|
local commandDir = fs.dir(info.source)
|
||||||
|
if commandDir == '.' then return end
|
||||||
|
|
||||||
|
local commands = fs.readdir(commandDir)
|
||||||
|
for _, command in ipairs(commands) do
|
||||||
|
local name = command:gsub('%.lua', '') -- chop off extension
|
||||||
|
if name ~= 'init' then
|
||||||
|
-- skip this file (for obvious reasons)
|
||||||
|
require('nature.commands.' .. name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -24,7 +24,7 @@ function hilbish.completion.handler(line, pos)
|
||||||
return {compGroup}, pfx
|
return {compGroup}, pfx
|
||||||
else
|
else
|
||||||
local ok, compGroups, pfx = pcall(hilbish.completion.call,
|
local ok, compGroups, pfx = pcall(hilbish.completion.call,
|
||||||
'command.' .. #fields[1], query, ctx, fields)
|
'command.' .. fields[1], query, ctx, fields)
|
||||||
if ok then
|
if ok then
|
||||||
return compGroups, pfx
|
return compGroups, pfx
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
-- Prelude initializes everything else for our shell
|
-- Prelude initializes everything else for our shell
|
||||||
local _ = require 'succulent' -- Function additions
|
local _ = require 'succulent' -- Function additions
|
||||||
|
local bait = require 'bait'
|
||||||
local fs = require 'fs'
|
local fs = require 'fs'
|
||||||
|
|
||||||
package.path = package.path .. ';' .. hilbish.dataDir .. '/?/init.lua'
|
package.path = package.path .. ';' .. hilbish.dataDir .. '/?/init.lua'
|
||||||
|
@ -28,7 +29,9 @@ do
|
||||||
return got_virt
|
return got_virt
|
||||||
end
|
end
|
||||||
|
|
||||||
virt_G[key] = os.getenv(key)
|
if type(key) == 'string' then
|
||||||
|
virt_G[key] = os.getenv(key)
|
||||||
|
end
|
||||||
return virt_G[key]
|
return virt_G[key]
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
@ -54,7 +57,6 @@ do
|
||||||
if ok then
|
if ok then
|
||||||
for _, module in ipairs(modules) do
|
for _, module in ipairs(modules) do
|
||||||
local entry = package.searchpath(module, startSearchPath)
|
local entry = package.searchpath(module, startSearchPath)
|
||||||
print(entry)
|
|
||||||
if entry then
|
if entry then
|
||||||
dofile(entry)
|
dofile(entry)
|
||||||
end
|
end
|
||||||
|
@ -63,3 +65,15 @@ do
|
||||||
|
|
||||||
package.path = package.path .. ';' .. startSearchPath
|
package.path = package.path .. ';' .. startSearchPath
|
||||||
end
|
end
|
||||||
|
|
||||||
|
bait.catch('error', function(event, handler, err)
|
||||||
|
bait.release(event, handler)
|
||||||
|
end)
|
||||||
|
|
||||||
|
bait.catch('command.not-found', function(cmd)
|
||||||
|
print(string.format('hilbish: %s not found', cmd))
|
||||||
|
end)
|
||||||
|
|
||||||
|
bait.catch('command.not-executable', function(cmd)
|
||||||
|
print(string.format('hilbish: %s: not executable', cmd))
|
||||||
|
end)
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
local fs = require 'fs'
|
local fs = require 'fs'
|
||||||
|
|
||||||
function cdHandle(inp)
|
local oldShRunner = hilbish.runner.sh
|
||||||
local res = hilbish.runner.lua(inp)
|
function hilbish.runner.sh(input)
|
||||||
|
local res = oldShRunner(input)
|
||||||
if not res.err then
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
res = hilbish.runner.sh(inp)
|
|
||||||
|
|
||||||
if res.exit ~= 0 and hilbish.opts.autocd then
|
if res.exit ~= 0 and hilbish.opts.autocd then
|
||||||
local ok, stat = pcall(fs.stat, res.input)
|
local ok, stat = pcall(fs.stat, res.input)
|
||||||
|
@ -21,5 +16,3 @@ function cdHandle(inp)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
end
|
end
|
||||||
|
|
||||||
hilbish.runner.setMode(cdHandle)
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
local bait = require 'bait'
|
||||||
|
local lunacolors = require 'lunacolors'
|
||||||
|
|
||||||
|
bait.catch('hilbish.init', function()
|
||||||
|
if hilbish.interactive and type(hilbish.opts.greeting) == 'string' then
|
||||||
|
print(lunacolors.format(hilbish.opts.greeting))
|
||||||
|
end
|
||||||
|
end)
|
|
@ -0,0 +1,6 @@
|
||||||
|
local bait = require 'bait'
|
||||||
|
|
||||||
|
bait.catch('command.exit', function(_, cmd, priv)
|
||||||
|
if not cmd then return end
|
||||||
|
if not priv and hilbish.opts.history then hilbish.history.add(cmd) end
|
||||||
|
end)
|
|
@ -20,7 +20,12 @@ local function setupOpt(name, default)
|
||||||
end
|
end
|
||||||
|
|
||||||
local defaultOpts = {
|
local defaultOpts = {
|
||||||
autocd = false
|
autocd = false,
|
||||||
|
history = true,
|
||||||
|
greeting = string.format([[Welcome to {magenta}Hilbish{reset}, {cyan}%s{reset}.
|
||||||
|
The nice lil shell for {blue}Lua{reset} fanatics!
|
||||||
|
]], hilbish.user),
|
||||||
|
motd = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for optsName, default in pairs(defaultOpts) do
|
for optsName, default in pairs(defaultOpts) do
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
local bait = require 'bait'
|
||||||
|
local lunacolors = require 'lunacolors'
|
||||||
|
|
||||||
|
hilbish.motd = [[
|
||||||
|
Hilbish 2.0 is a {red}major{reset} update! If your config doesn't work
|
||||||
|
anymore, that will definitely be why! A MOTD, very message, much day.
|
||||||
|
]]
|
||||||
|
|
||||||
|
bait.catch('hilbish.init', function()
|
||||||
|
if hilbish.interactive and hilbish.opts.motd then
|
||||||
|
print(lunacolors.format(hilbish.motd))
|
||||||
|
end
|
||||||
|
end)
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"github.com/rivo/uniseg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// initGrid - Grid display details. Called each time we want to be sure to have
|
// initGrid - Grid display details. Called each time we want to be sure to have
|
||||||
|
@ -13,8 +14,8 @@ func (g *CompletionGroup) initGrid(rl *Instance) {
|
||||||
// Compute size of each completion item box
|
// Compute size of each completion item box
|
||||||
tcMaxLength := 1
|
tcMaxLength := 1
|
||||||
for i := range g.Suggestions {
|
for i := range g.Suggestions {
|
||||||
if len(g.Suggestions[i]) > tcMaxLength {
|
if uniseg.GraphemeClusterCount(g.Suggestions[i]) > tcMaxLength {
|
||||||
tcMaxLength = len([]rune(g.Suggestions[i]))
|
tcMaxLength = uniseg.GraphemeClusterCount(g.Suggestions[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,11 +100,11 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) {
|
||||||
|
|
||||||
// If group title, print it and adjust offset.
|
// If group title, print it and adjust offset.
|
||||||
if g.Name != "" {
|
if g.Name != "" {
|
||||||
comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, g.Name, RESET)
|
comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, fmtEscape(g.Name), RESET)
|
||||||
rl.tcUsedY++
|
rl.tcUsedY++
|
||||||
}
|
}
|
||||||
|
|
||||||
cellWidth := strconv.Itoa((GetTermWidth() / g.tcMaxX) - 2)
|
cellWidth := strconv.Itoa((GetTermWidth() / g.tcMaxX) - 4)
|
||||||
x := 0
|
x := 0
|
||||||
y := 1
|
y := 1
|
||||||
|
|
||||||
|
@ -124,7 +125,15 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) {
|
||||||
comp += seqInvert
|
comp += seqInvert
|
||||||
}
|
}
|
||||||
|
|
||||||
comp += fmt.Sprintf("%-"+cellWidth+"s %s", g.Suggestions[i], seqReset)
|
sugg := g.Suggestions[i]
|
||||||
|
if len(sugg) > GetTermWidth() {
|
||||||
|
sugg = sugg[:GetTermWidth() - 4] + "..."
|
||||||
|
}
|
||||||
|
formatStr := "%-"+cellWidth+"s%s "
|
||||||
|
if g.tcMaxX == 1 {
|
||||||
|
formatStr = "%s%s"
|
||||||
|
}
|
||||||
|
comp += fmt.Sprintf(formatStr, fmtEscape(sugg), seqReset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always add a newline to the group if the end if not punctuated with one
|
// Always add a newline to the group if the end if not punctuated with one
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package readline
|
package readline
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
// CompletionGroup - A group/category of items offered to completion, with its own
|
// CompletionGroup - A group/category of items offered to completion, with its own
|
||||||
// name, descriptions and completion display format/type.
|
// name, descriptions and completion display format/type.
|
||||||
// The output, if there are multiple groups available for a given completion input,
|
// The output, if there are multiple groups available for a given completion input,
|
||||||
|
@ -285,3 +287,7 @@ func (g *CompletionGroup) goLastCell() {
|
||||||
g.tcPosX = 0
|
g.tcPosX = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fmtEscape(s string) string {
|
||||||
|
return strings.Replace(s, "%", "%%", -1)
|
||||||
|
}
|
||||||
|
|
|
@ -206,12 +206,12 @@ func (g *CompletionGroup) writeList(rl *Instance) (comp string) {
|
||||||
if len(item) > maxLength {
|
if len(item) > maxLength {
|
||||||
item = item[:maxLength-3] + "..."
|
item = item[:maxLength-3] + "..."
|
||||||
}
|
}
|
||||||
sugg := fmt.Sprintf("\r%s%-"+cellWidth+"s", highlight(y, 0), item)
|
sugg := fmt.Sprintf("\r%s%-"+cellWidth+"s", highlight(y, 0), fmtEscape(item))
|
||||||
|
|
||||||
// Alt suggestion
|
// Alt suggestion
|
||||||
alt, ok := g.Aliases[item]
|
alt, ok := g.Aliases[item]
|
||||||
if ok {
|
if ok {
|
||||||
alt = fmt.Sprintf(" %s%"+cellWidthAlt+"s", highlight(y, 1), alt)
|
alt = fmt.Sprintf(" %s%"+cellWidthAlt+"s", highlight(y, 1), fmtEscape(alt))
|
||||||
} else {
|
} else {
|
||||||
// Else, make an empty cell
|
// Else, make an empty cell
|
||||||
alt = strings.Repeat(" ", maxLengthAlt+1) // + 2 to keep account of spaces
|
alt = strings.Repeat(" ", maxLengthAlt+1) // + 2 to keep account of spaces
|
||||||
|
|
|
@ -76,7 +76,7 @@ func (g *CompletionGroup) writeMap(rl *Instance) (comp string) {
|
||||||
|
|
||||||
if g.Name != "" {
|
if g.Name != "" {
|
||||||
// Print group title (changes with line returns depending on type)
|
// Print group title (changes with line returns depending on type)
|
||||||
comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, g.Name, RESET)
|
comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, fmtEscape(g.Name), RESET)
|
||||||
rl.tcUsedY++
|
rl.tcUsedY++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ func (g *CompletionGroup) writeMap(rl *Instance) (comp string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
comp += fmt.Sprintf("\r%-"+cellWidth+"s %s %-"+itemWidth+"s %s\n",
|
comp += fmt.Sprintf("\r%-"+cellWidth+"s %s %-"+itemWidth+"s %s\n",
|
||||||
description, highlight(y), item, seqReset)
|
description, highlight(y), fmtEscape(item), seqReset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the equivalent of this group's size to final screen clearing
|
// Add the equivalent of this group's size to final screen clearing
|
||||||
|
|
|
@ -123,23 +123,20 @@ func (rl *Instance) walkHistory(i int) {
|
||||||
|
|
||||||
// When we are exiting the current line buffer to move around
|
// When we are exiting the current line buffer to move around
|
||||||
// the history, we make buffer the current line
|
// the history, we make buffer the current line
|
||||||
if rl.histPos == 0 && (rl.histPos+i) == 1 {
|
if rl.histOffset == 0 && rl.histOffset + i == 1 {
|
||||||
rl.lineBuf = string(rl.line)
|
rl.lineBuf = string(rl.line)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch rl.histPos + i {
|
rl.histOffset += i
|
||||||
case 0, history.Len() + 1:
|
if rl.histOffset == 0 {
|
||||||
rl.histPos = 0
|
|
||||||
rl.line = []rune(rl.lineBuf)
|
rl.line = []rune(rl.lineBuf)
|
||||||
rl.pos = len(rl.lineBuf)
|
rl.pos = len(rl.lineBuf)
|
||||||
return
|
} else if rl.histOffset <= -1 {
|
||||||
case -1:
|
rl.histOffset = 0
|
||||||
rl.histPos = 0
|
} else {
|
||||||
rl.lineBuf = string(rl.line)
|
|
||||||
default:
|
|
||||||
dedup = true
|
dedup = true
|
||||||
old = string(rl.line)
|
old = string(rl.line)
|
||||||
new, err = history.GetLine(history.Len() - rl.histPos - 1)
|
new, err = history.GetLine(history.Len() - rl.histOffset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rl.resetHelpers()
|
rl.resetHelpers()
|
||||||
print("\r\n" + err.Error() + "\r\n")
|
print("\r\n" + err.Error() + "\r\n")
|
||||||
|
@ -148,7 +145,6 @@ func (rl *Instance) walkHistory(i int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rl.clearLine()
|
rl.clearLine()
|
||||||
rl.histPos += i
|
|
||||||
rl.line = []rune(new)
|
rl.line = []rune(new)
|
||||||
rl.pos = len(rl.line)
|
rl.pos = len(rl.line)
|
||||||
if rl.pos > 0 {
|
if rl.pos > 0 {
|
||||||
|
|
|
@ -134,6 +134,7 @@ type Instance struct {
|
||||||
// history operating params
|
// history operating params
|
||||||
lineBuf string
|
lineBuf string
|
||||||
histPos int
|
histPos int
|
||||||
|
histOffset int
|
||||||
histNavIdx int // Used for quick history navigation.
|
histNavIdx int // Used for quick history navigation.
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -198,6 +199,8 @@ type Instance struct {
|
||||||
|
|
||||||
ViModeCallback func(ViMode)
|
ViModeCallback func(ViMode)
|
||||||
ViActionCallback func(ViAction, []string)
|
ViActionCallback func(ViAction, []string)
|
||||||
|
|
||||||
|
RawInputCallback func([]rune) // called on all input
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInstance is used to create a readline instance and initialise it with sane defaults.
|
// NewInstance is used to create a readline instance and initialise it with sane defaults.
|
||||||
|
|
|
@ -18,7 +18,7 @@ func (rl *Instance) updateLine(line []rune) {
|
||||||
|
|
||||||
// getLine - In many places we need the current line input. We either return the real line,
|
// getLine - In many places we need the current line input. We either return the real line,
|
||||||
// or the one that includes the current completion candidate, if there is any.
|
// or the one that includes the current completion candidate, if there is any.
|
||||||
func (rl *Instance) getLine() []rune {
|
func (rl *Instance) GetLine() []rune {
|
||||||
if len(rl.currentComp) > 0 {
|
if len(rl.currentComp) > 0 {
|
||||||
return rl.lineComp
|
return rl.lineComp
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
ansi "github.com/acarl005/stripansi"
|
ansi "github.com/acarl005/stripansi"
|
||||||
"github.com/rivo/uniseg"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetPrompt will define the readline prompt string.
|
// SetPrompt will define the readline prompt string.
|
||||||
|
@ -209,7 +208,7 @@ func (rl *Instance) colorizeVimPrompt(p []rune) (cp []rune) {
|
||||||
// getting its real-printed length.
|
// getting its real-printed length.
|
||||||
func getRealLength(s string) (l int) {
|
func getRealLength(s string) (l int) {
|
||||||
stripped := ansi.Strip(s)
|
stripped := ansi.Strip(s)
|
||||||
return uniseg.GraphemeClusterCount(stripped)
|
return getWidth([]rune(stripped))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rl *Instance) echoRightPrompt() {
|
func (rl *Instance) echoRightPrompt() {
|
||||||
|
|
|
@ -49,7 +49,7 @@ func (rl *Instance) Readline() (string, error) {
|
||||||
|
|
||||||
// History Init
|
// History Init
|
||||||
// We need this set to the last command, so that we can access it quickly
|
// We need this set to the last command, so that we can access it quickly
|
||||||
rl.histPos = 0
|
rl.histOffset = 0
|
||||||
rl.viUndoHistory = []undoItem{{line: "", pos: 0}}
|
rl.viUndoHistory = []undoItem{{line: "", pos: 0}}
|
||||||
|
|
||||||
// Multisplit
|
// Multisplit
|
||||||
|
@ -94,6 +94,9 @@ func (rl *Instance) Readline() (string, error) {
|
||||||
|
|
||||||
rl.skipStdinRead = false
|
rl.skipStdinRead = false
|
||||||
r := []rune(string(b))
|
r := []rune(string(b))
|
||||||
|
if rl.RawInputCallback != nil {
|
||||||
|
rl.RawInputCallback(r[:i])
|
||||||
|
}
|
||||||
|
|
||||||
if isMultiline(r[:i]) || len(rl.multiline) > 0 {
|
if isMultiline(r[:i]) || len(rl.multiline) > 0 {
|
||||||
rl.multiline = append(rl.multiline, b[:i]...)
|
rl.multiline = append(rl.multiline, b[:i]...)
|
||||||
|
@ -235,7 +238,9 @@ func (rl *Instance) Readline() (string, error) {
|
||||||
|
|
||||||
// Normal completion search does only refresh the search pattern and the comps
|
// Normal completion search does only refresh the search pattern and the comps
|
||||||
if rl.modeTabFind || rl.modeAutoFind {
|
if rl.modeTabFind || rl.modeAutoFind {
|
||||||
|
rl.resetVirtualComp(false)
|
||||||
rl.backspaceTabFind()
|
rl.backspaceTabFind()
|
||||||
|
rl.renderHelpers()
|
||||||
rl.viUndoSkipAppend = true
|
rl.viUndoSkipAppend = true
|
||||||
} else {
|
} else {
|
||||||
// Always cancel any virtual completion
|
// Always cancel any virtual completion
|
||||||
|
@ -328,6 +333,8 @@ func (rl *Instance) Readline() (string, error) {
|
||||||
|
|
||||||
rl.modeTabFind = true
|
rl.modeTabFind = true
|
||||||
rl.updateTabFind([]rune{})
|
rl.updateTabFind([]rune{})
|
||||||
|
rl.updateVirtualComp()
|
||||||
|
rl.renderHelpers()
|
||||||
rl.viUndoSkipAppend = true
|
rl.viUndoSkipAppend = true
|
||||||
|
|
||||||
// Tab Completion & Completion Search ---------------------------------------------------------------
|
// Tab Completion & Completion Search ---------------------------------------------------------------
|
||||||
|
@ -481,7 +488,10 @@ func (rl *Instance) Readline() (string, error) {
|
||||||
if string(r[:i]) != seqShiftTab &&
|
if string(r[:i]) != seqShiftTab &&
|
||||||
string(r[:i]) != seqForwards && string(r[:i]) != seqBackwards &&
|
string(r[:i]) != seqForwards && string(r[:i]) != seqBackwards &&
|
||||||
string(r[:i]) != seqUp && string(r[:i]) != seqDown {
|
string(r[:i]) != seqUp && string(r[:i]) != seqDown {
|
||||||
rl.resetVirtualComp(false)
|
// basically only applies except on 1st ctrl r open
|
||||||
|
// so if we have not explicitly selected something
|
||||||
|
// (tabCompletionSelect is false) drop virtual completion
|
||||||
|
rl.resetVirtualComp(!rl.tabCompletionSelect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,7 +524,9 @@ func (rl *Instance) Readline() (string, error) {
|
||||||
if rl.modeAutoFind || rl.modeTabFind {
|
if rl.modeAutoFind || rl.modeTabFind {
|
||||||
rl.resetVirtualComp(false)
|
rl.resetVirtualComp(false)
|
||||||
rl.updateTabFind(r[:i])
|
rl.updateTabFind(r[:i])
|
||||||
|
rl.renderHelpers()
|
||||||
rl.viUndoSkipAppend = true
|
rl.viUndoSkipAppend = true
|
||||||
|
continue
|
||||||
} else {
|
} else {
|
||||||
rl.resetVirtualComp(false)
|
rl.resetVirtualComp(false)
|
||||||
rl.editorInput(r[:i])
|
rl.editorInput(r[:i])
|
||||||
|
@ -534,6 +546,10 @@ func (rl *Instance) Readline() (string, error) {
|
||||||
// entry readline is currently configured for and then update the line entries
|
// entry readline is currently configured for and then update the line entries
|
||||||
// accordingly.
|
// accordingly.
|
||||||
func (rl *Instance) editorInput(r []rune) {
|
func (rl *Instance) editorInput(r []rune) {
|
||||||
|
if len(r) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch rl.modeViMode {
|
switch rl.modeViMode {
|
||||||
case VimKeys:
|
case VimKeys:
|
||||||
rl.vi(r[0])
|
rl.vi(r[0])
|
||||||
|
@ -601,6 +617,7 @@ func (rl *Instance) escapeSeq(r []rune) {
|
||||||
case string(charEscape):
|
case string(charEscape):
|
||||||
switch {
|
switch {
|
||||||
case rl.modeAutoFind:
|
case rl.modeAutoFind:
|
||||||
|
rl.resetVirtualComp(true)
|
||||||
rl.resetTabFind()
|
rl.resetTabFind()
|
||||||
rl.clearHelpers()
|
rl.clearHelpers()
|
||||||
rl.resetTabCompletion()
|
rl.resetTabCompletion()
|
||||||
|
@ -608,6 +625,7 @@ func (rl *Instance) escapeSeq(r []rune) {
|
||||||
rl.renderHelpers()
|
rl.renderHelpers()
|
||||||
|
|
||||||
case rl.modeTabFind:
|
case rl.modeTabFind:
|
||||||
|
rl.resetVirtualComp(true)
|
||||||
rl.resetTabFind()
|
rl.resetTabFind()
|
||||||
rl.resetTabCompletion()
|
rl.resetTabCompletion()
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package readline
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
"github.com/rivo/uniseg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// insertCandidateVirtual - When a completion candidate is selected, we insert it virtually in the input line:
|
// insertCandidateVirtual - When a completion candidate is selected, we insert it virtually in the input line:
|
||||||
|
@ -249,10 +250,10 @@ func (rl *Instance) viJumpEVirtual(tokeniser func([]rune, int) ([]string, int, i
|
||||||
return
|
return
|
||||||
case pos >= len(word)-1:
|
case pos >= len(word)-1:
|
||||||
word = rTrimWhiteSpace(split[index+1])
|
word = rTrimWhiteSpace(split[index+1])
|
||||||
adjust = len(split[index]) - pos
|
adjust = uniseg.GraphemeClusterCount(split[index]) - pos
|
||||||
adjust += len(word) - 1
|
adjust += uniseg.GraphemeClusterCount(word) - 1
|
||||||
default:
|
default:
|
||||||
adjust = len(word) - pos - 1
|
adjust = uniseg.GraphemeClusterCount(word) - pos - 1
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ func delayedSyntaxTimer(rl *Instance, i int64) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// We pass either the current line or the one with the current completion.
|
// We pass either the current line or the one with the current completion.
|
||||||
newLine := rl.DelayedSyntaxWorker(rl.getLine())
|
newLine := rl.DelayedSyntaxWorker(rl.GetLine())
|
||||||
var sLine string
|
var sLine string
|
||||||
count := atomic.LoadInt64(&rl.delayedSyntaxCount)
|
count := atomic.LoadInt64(&rl.delayedSyntaxCount)
|
||||||
if count != i {
|
if count != i {
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package readline
|
package readline
|
||||||
|
|
||||||
import "golang.org/x/text/width"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/text/width"
|
||||||
|
)
|
||||||
|
|
||||||
// updateHelpers is a key part of the whole refresh process:
|
// updateHelpers is a key part of the whole refresh process:
|
||||||
// it should coordinate reprinting the input line, any Infos and completions
|
// it should coordinate reprinting the input line, any Infos and completions
|
||||||
|
@ -52,19 +56,19 @@ func (rl *Instance) updateReferences() {
|
||||||
rl.posY = 0
|
rl.posY = 0
|
||||||
rl.fullY = 0
|
rl.fullY = 0
|
||||||
|
|
||||||
var fullLine, cPosLine int
|
var curLine []rune
|
||||||
if len(rl.currentComp) > 0 {
|
if len(rl.currentComp) > 0 {
|
||||||
fullLine = getWidth(rl.lineComp)
|
curLine = rl.lineComp
|
||||||
cPosLine = getWidth(rl.lineComp[:rl.pos])
|
|
||||||
} else {
|
} else {
|
||||||
fullLine = getWidth(rl.line)
|
curLine = rl.line
|
||||||
cPosLine = getWidth(rl.line[:rl.pos])
|
|
||||||
}
|
}
|
||||||
|
fullLine := getWidth(curLine)
|
||||||
|
cPosLine := getWidth(curLine[:rl.pos])
|
||||||
|
|
||||||
// We need the X offset of the whole line
|
// We need the X offset of the whole line
|
||||||
toEndLine := rl.promptLen + fullLine
|
toEndLine := rl.promptLen + fullLine
|
||||||
fullOffset := toEndLine / GetTermWidth()
|
fullOffset := toEndLine / GetTermWidth()
|
||||||
rl.fullY = fullOffset
|
rl.fullY = fullOffset + strings.Count(string(curLine), "\n")
|
||||||
fullRest := toEndLine % GetTermWidth()
|
fullRest := toEndLine % GetTermWidth()
|
||||||
rl.fullX = fullRest
|
rl.fullX = fullRest
|
||||||
|
|
||||||
|
|
|
@ -245,7 +245,7 @@ func (rl *Instance) vi(r rune) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep the previous cursor position
|
// Keep the previous cursor position
|
||||||
prev := rl.pos
|
//prev := rl.pos
|
||||||
|
|
||||||
new, err := rl.StartEditorWithBuffer(multiline, "")
|
new, err := rl.StartEditorWithBuffer(multiline, "")
|
||||||
if err != nil || len(new) == 0 || string(new) == string(multiline) {
|
if err != nil || len(new) == 0 || string(new) == string(multiline) {
|
||||||
|
@ -257,11 +257,11 @@ func (rl *Instance) vi(r rune) {
|
||||||
// Clean the shell and put the new buffer, with adjusted pos if needed.
|
// Clean the shell and put the new buffer, with adjusted pos if needed.
|
||||||
rl.clearLine()
|
rl.clearLine()
|
||||||
rl.line = new
|
rl.line = new
|
||||||
if prev > len(rl.line) {
|
rl.pos = len(rl.line)
|
||||||
rl.pos = len(rl.line) - 1
|
/*if prev > len(rl.line) {
|
||||||
} else {
|
} else {
|
||||||
rl.pos = prev
|
rl.pos = prev
|
||||||
}
|
}*/
|
||||||
|
|
||||||
case 'w':
|
case 'w':
|
||||||
// If we were not yanking
|
// If we were not yanking
|
||||||
|
|
32
rl.go
32
rl.go
|
@ -13,18 +13,22 @@ import (
|
||||||
|
|
||||||
type lineReader struct {
|
type lineReader struct {
|
||||||
rl *readline.Instance
|
rl *readline.Instance
|
||||||
|
fileHist *fileHistory
|
||||||
}
|
}
|
||||||
var fileHist *fileHistory
|
|
||||||
var hinter *rt.Closure
|
var hinter *rt.Closure
|
||||||
var highlighter *rt.Closure
|
var highlighter *rt.Closure
|
||||||
|
|
||||||
func newLineReader(prompt string, noHist bool) *lineReader {
|
func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
rl := readline.NewInstance()
|
rl := readline.NewInstance()
|
||||||
|
lr := &lineReader{
|
||||||
|
rl: rl,
|
||||||
|
}
|
||||||
|
|
||||||
// we don't mind hilbish.read rl instances having completion,
|
// we don't mind hilbish.read rl instances having completion,
|
||||||
// but it cant have shared history
|
// but it cant have shared history
|
||||||
if !noHist {
|
if !noHist {
|
||||||
fileHist = newFileHistory()
|
lr.fileHist = newFileHistory(defaultHistPath)
|
||||||
rl.SetHistoryCtrlR("History", fileHist)
|
rl.SetHistoryCtrlR("History", &luaHistory{})
|
||||||
rl.HistoryAutoWrite = false
|
rl.HistoryAutoWrite = false
|
||||||
}
|
}
|
||||||
rl.ShowVimMode = false
|
rl.ShowVimMode = false
|
||||||
|
@ -44,14 +48,14 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
case readline.VimActionPaste: actionStr = "paste"
|
case readline.VimActionPaste: actionStr = "paste"
|
||||||
case readline.VimActionYank: actionStr = "yank"
|
case readline.VimActionYank: actionStr = "yank"
|
||||||
}
|
}
|
||||||
hooks.Em.Emit("hilbish.vimAction", actionStr, args)
|
hooks.Emit("hilbish.vimAction", actionStr, args)
|
||||||
}
|
}
|
||||||
rl.HintText = func(line []rune, pos int) []rune {
|
rl.HintText = func(line []rune, pos int) []rune {
|
||||||
if hinter == nil {
|
if hinter == nil {
|
||||||
return []rune{}
|
return []rune{}
|
||||||
}
|
}
|
||||||
|
|
||||||
retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(highlighter),
|
retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(hinter),
|
||||||
rt.StringValue(string(line)), rt.IntValue(int64(pos)))
|
rt.StringValue(string(line)), rt.IntValue(int64(pos)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
@ -171,13 +175,11 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
return pfx, compGroups
|
return pfx, compGroups
|
||||||
}
|
}
|
||||||
|
|
||||||
return &lineReader{
|
return lr
|
||||||
rl,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) Read() (string, error) {
|
func (lr *lineReader) Read() (string, error) {
|
||||||
hooks.Em.Emit("command.precmd", nil)
|
hooks.Emit("command.precmd", nil)
|
||||||
s, err := lr.rl.Readline()
|
s, err := lr.rl.Readline()
|
||||||
// this is so dumb
|
// this is so dumb
|
||||||
if err == readline.EOF {
|
if err == readline.EOF {
|
||||||
|
@ -212,7 +214,7 @@ func (lr *lineReader) SetRightPrompt(p string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) AddHistory(cmd string) {
|
func (lr *lineReader) AddHistory(cmd string) {
|
||||||
fileHist.Write(cmd)
|
lr.fileHist.Write(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) ClearInput() {
|
func (lr *lineReader) ClearInput() {
|
||||||
|
@ -253,7 +255,7 @@ func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) luaSize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (lr *lineReader) luaSize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.PushingNext1(t.Runtime, rt.IntValue(int64(fileHist.Len()))), nil
|
return c.PushingNext1(t.Runtime, rt.IntValue(int64(lr.fileHist.Len()))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
@ -265,17 +267,17 @@ func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd, _ := fileHist.GetLine(int(idx))
|
cmd, _ := lr.fileHist.GetLine(int(idx))
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
|
return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
tbl := rt.NewTable()
|
tbl := rt.NewTable()
|
||||||
size := fileHist.Len()
|
size := lr.fileHist.Len()
|
||||||
|
|
||||||
for i := 1; i < size; i++ {
|
for i := 1; i < size; i++ {
|
||||||
cmd, _ := fileHist.GetLine(i)
|
cmd, _ := lr.fileHist.GetLine(i)
|
||||||
tbl.Set(rt.IntValue(int64(i)), rt.StringValue(cmd))
|
tbl.Set(rt.IntValue(int64(i)), rt.StringValue(cmd))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,6 +285,6 @@ func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
fileHist.clear()
|
lr.fileHist.clear()
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,13 +28,13 @@ func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
input, exitCode, cont, err := handleSh(cmd)
|
_, exitCode, cont, err := execSh(aliases.Resolve(cmd))
|
||||||
var luaErr rt.Value = rt.NilValue
|
var luaErr rt.Value = rt.NilValue
|
||||||
if err != nil {
|
if err != nil {
|
||||||
luaErr = rt.StringValue(err.Error())
|
luaErr = rt.StringValue(err.Error())
|
||||||
}
|
}
|
||||||
runnerRet := rt.NewTable()
|
runnerRet := rt.NewTable()
|
||||||
runnerRet.Set(rt.StringValue("input"), rt.StringValue(input))
|
runnerRet.Set(rt.StringValue("input"), rt.StringValue(cmd))
|
||||||
runnerRet.Set(rt.StringValue("exitCode"), rt.IntValue(int64(exitCode)))
|
runnerRet.Set(rt.StringValue("exitCode"), rt.IntValue(int64(exitCode)))
|
||||||
runnerRet.Set(rt.StringValue("continue"), rt.BoolValue(cont))
|
runnerRet.Set(rt.StringValue("continue"), rt.BoolValue(cont))
|
||||||
runnerRet.Set(rt.StringValue("err"), luaErr)
|
runnerRet.Set(rt.StringValue("err"), luaErr)
|
||||||
|
|
|
@ -15,11 +15,11 @@ func handleSignals() {
|
||||||
|
|
||||||
for s := range c {
|
for s := range c {
|
||||||
switch s {
|
switch s {
|
||||||
case os.Interrupt: hooks.Em.Emit("signal.sigint")
|
case os.Interrupt: hooks.Emit("signal.sigint")
|
||||||
case syscall.SIGTERM: exit(0)
|
case syscall.SIGTERM: exit(0)
|
||||||
case syscall.SIGWINCH: hooks.Em.Emit("signal.resize")
|
case syscall.SIGWINCH: hooks.Emit("signal.resize")
|
||||||
case syscall.SIGUSR1: hooks.Em.Emit("signal.sigusr1")
|
case syscall.SIGUSR1: hooks.Emit("signal.sigusr1")
|
||||||
case syscall.SIGUSR2: hooks.Em.Emit("signal.sigusr2")
|
case syscall.SIGUSR2: hooks.Emit("signal.sigusr2")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ func handleSignals() {
|
||||||
for s := range c {
|
for s := range c {
|
||||||
switch s {
|
switch s {
|
||||||
case os.Interrupt:
|
case os.Interrupt:
|
||||||
hooks.Em.Emit("signal.sigint")
|
hooks.Emit("signal.sigint")
|
||||||
if !running && interactive {
|
if !running && interactive {
|
||||||
lr.ClearInput()
|
lr.ClearInput()
|
||||||
}
|
}
|
||||||
|
|
2
vars.go
2
vars.go
|
@ -11,7 +11,7 @@ var (
|
||||||
|
|
||||||
// Version info
|
// Version info
|
||||||
var (
|
var (
|
||||||
ver = "v2.0.0"
|
ver = "v2.0.0-rc1"
|
||||||
releaseName = "Hibiscus"
|
releaseName = "Hibiscus"
|
||||||
gitCommit string
|
gitCommit string
|
||||||
gitBranch string
|
gitBranch string
|
||||||
|
|
|
@ -14,7 +14,7 @@ var (
|
||||||
.. hilbish.userDir.config .. '/hilbish/?/init.lua;'
|
.. hilbish.userDir.config .. '/hilbish/?/init.lua;'
|
||||||
.. hilbish.userDir.config .. '/hilbish/?/?.lua;'
|
.. hilbish.userDir.config .. '/hilbish/?/?.lua;'
|
||||||
.. hilbish.userDir.config .. '/hilbish/?.lua'`
|
.. hilbish.userDir.config .. '/hilbish/?.lua'`
|
||||||
dataDir = "/usr/share/hilbish"
|
dataDir = "/usr/local/share/hilbish"
|
||||||
preloadPath = dataDir + "/nature/init.lua"
|
preloadPath = dataDir + "/nature/init.lua"
|
||||||
sampleConfPath = dataDir + "/.hilbishrc.lua" // Path to default/sample config
|
sampleConfPath = dataDir + "/.hilbishrc.lua" // Path to default/sample config
|
||||||
defaultConfDir = ""
|
defaultConfDir = ""
|
||||||
|
|
|
@ -2,14 +2,16 @@
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "hilbish/util"
|
||||||
|
|
||||||
// String vars that are free to be changed at compile time
|
// String vars that are free to be changed at compile time
|
||||||
var (
|
var (
|
||||||
requirePaths = commonRequirePaths + `.. ';'
|
requirePaths = commonRequirePaths + `.. ';'
|
||||||
.. hilbish.userDir.config .. '\\Hilbish\\libs\\?\\init.lua;'
|
.. hilbish.userDir.config .. '\\Hilbish\\libs\\?\\init.lua;'
|
||||||
.. hilbish.userDir.config .. '\\Hilbish\\libs\\?\\?.lua;'
|
.. hilbish.userDir.config .. '\\Hilbish\\libs\\?\\?.lua;'
|
||||||
.. hilbish.userDir.config .. '\\Hilbish\\libs\\?.lua;'`
|
.. hilbish.userDir.config .. '\\Hilbish\\libs\\?.lua;'`
|
||||||
dataDir = "~\\Appdata\\Roaming\\Hilbish" // ~ and \ gonna cry?
|
dataDir = util.ExpandHome("~\\Appdata\\Roaming\\Hilbish") // ~ and \ gonna cry?
|
||||||
preloadPath = dataDir + "\\nature\\init.lua"
|
preloadPath = dataDir + "\\nature\\init.lua"
|
||||||
sampleConfPath = dataDir + "\\hilbishrc.lua" // Path to default/sample config
|
sampleConfPath = dataDir + "\\.hilbishrc.lua" // Path to default/sample config
|
||||||
defaultConfDir = ""
|
defaultConfDir = ""
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: "{{ replace .Name "-" " " | title }}"
|
||||||
|
date: {{ .Date }}
|
||||||
|
draft: true
|
||||||
|
---
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
baseURL = 'https://rosettea.github.io/Hilbish/'
|
||||||
|
languageCode = 'en-us'
|
||||||
|
title = 'Hilbish'
|
||||||
|
theme = 'hsh'
|
||||||
|
enableGitInfo = true
|
||||||
|
|
||||||
|
[menu]
|
||||||
|
[[menu.nav]]
|
||||||
|
identifier = 'home'
|
||||||
|
name = 'Home'
|
||||||
|
pageref = '/'
|
||||||
|
weight = 1
|
||||||
|
[[menu.nav]]
|
||||||
|
identifier = 'install'
|
||||||
|
name = 'Install'
|
||||||
|
pageref = '/install'
|
||||||
|
weight = 2
|
||||||
|
[[menu.nav]]
|
||||||
|
identifier = 'docs'
|
||||||
|
name = 'Docs'
|
||||||
|
pageref = '/docs'
|
||||||
|
weight = 3
|
||||||
|
|
||||||
|
[markup.goldmark.renderer]
|
||||||
|
unsafe = true
|
|
@ -0,0 +1,134 @@
|
||||||
|
---
|
||||||
|
description: 'Something Unique. Hilbish is the new interactive shell for Lua fans. Extensible, scriptable, configurable: All in Lua.'
|
||||||
|
---
|
||||||
|
|
||||||
|
[//]: <>
|
||||||
|
|
||||||
|
<!-- hugo (prob goldmark) is funny; the html wont work if its the first thing -->
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="fw-light">Something Unique.</h1>
|
||||||
|
<p>
|
||||||
|
<strong>Hilbish</strong> is the new interactive shell for Lua fans.<br>
|
||||||
|
Extensible, scriptable, configurable: All in Lua.
|
||||||
|
</p>
|
||||||
|
<a href="install" class="btn btn-primary">Install</a>
|
||||||
|
<a href="https://github.com/Rosettea/Hilbish" class="btn btn-secondary" target="_blank">Github</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="row row-cols-1 row-cols-md-2 g-4">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card border-light mb-3">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<a href="https://safe.kashima.moe/6njmopm47u1x.png">
|
||||||
|
<img src="https://safe.kashima.moe/6njmopm47u1x.png" class="img-fluid rounded-start">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h5 class="card-header">Simple and Easy Scripting</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="card-text">
|
||||||
|
Hilbish is configured and scripted in the Lua programming language.
|
||||||
|
This removes all the old, ugly things about Shell script and introduces
|
||||||
|
everything good about Lua, including other languages (Moonscript & Fennel).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="card border-light mb-3">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<a href="https://safe.kashima.moe/jkndbi636lzj.png">
|
||||||
|
<img src="https://safe.kashima.moe/jkndbi636lzj.png" class="img-fluid rounded-start">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h5 class="card-header">History and Completion Menus</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="card-text">
|
||||||
|
Hilbish provides the user with proper menus for completions,
|
||||||
|
history searching. Want to see your previous commands? Hit Ctrl-R.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="card border-light mb-3">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<a href="https://safe.kashima.moe/6yfeooamzro4.png">
|
||||||
|
<img src="https://safe.kashima.moe/6yfeooamzro4.png" class="img-fluid rounded-start">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h5 class="card-header">Tons of Features, and More to Come</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="card-text">
|
||||||
|
Hilbish offers a bunch of features to make your interactive
|
||||||
|
shell experience rich. Things like syntax highlighting and hinting
|
||||||
|
available via the Lua API.
|
||||||
|
</p>
|
||||||
|
<p class="card-small text-muted">* Command hints shown in photo are not default.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- uncomment, replace top when editor interface can be replaced (and replace the images) -->
|
||||||
|
<!--
|
||||||
|
<div class="col">
|
||||||
|
<div class="card border-light mb-3">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<a href="https://safe.kashima.moe/6yfeooamzro4.png">
|
||||||
|
<img src="https://safe.kashima.moe/6yfeooamzro4.png" class="img-fluid rounded-start">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h5 class="card-header">Highly Extensible</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="card-text">
|
||||||
|
Hilbish can be turned into an all new shell if wanted. One of our
|
||||||
|
main goals is that most (if not all) interfaces can be replaced.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h1 class="fw-light">Why not just Lua?</h1>
|
||||||
|
<p>
|
||||||
|
Hilbish is your interactive shell as well as a just a Lua interpreter
|
||||||
|
and enhanced REPL.<br>
|
||||||
|
</p>
|
||||||
|
<ul class="list-group" style="max-width: 64em;">
|
||||||
|
<li class="list-group-item"><i class="fa-solid fa-battery-full"></i> Batteries included Lua runtime that's also your user shell!</li>
|
||||||
|
<li class="list-group-item"><i class="fa-solid fa-network-wired"></i> Hilbish is easily cross platform. It has OS agnostic interfaces for easy cross platform Lua code.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h1 class="fw-light">Try It Today!</h1>
|
||||||
|
<p>
|
||||||
|
Hilbish is known to run on the 3 major platforms (Windows, MacOS, Linux)
|
||||||
|
but likely builds on other Unixes!
|
||||||
|
<br>
|
||||||
|
Windows doesn't work as well as it should, so if you're a Windows user,
|
||||||
|
<a href="https://github.com/Rosettea/Hilbish/discussions/165">say something</a>!
|
||||||
|
<ul class="list-group" style="max-width: 64em;">
|
||||||
|
<li class="list-group-item"><i class="fa-solid fa-cloud-arrow-down"></i> <a href="/Hilbish/install" style="text-decoration: none;"><strong>Download</strong></a> the binary</li>
|
||||||
|
<li class="list-group-item"><i class="fa-solid fa-screwdriver-wrench"></i> <a href="https://github.com/Rosettea/Hilbish#manual-build" style="text-decoration: none;"><strong>Build</strong></a> from source</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
|
@ -0,0 +1,19 @@
|
||||||
|
---
|
||||||
|
title: Introduction
|
||||||
|
layout: doc
|
||||||
|
weight: -1
|
||||||
|
menu: docs
|
||||||
|
---
|
||||||
|
|
||||||
|
Here lies the documentation for Hilbish, the hyper extensible Lua shell.
|
||||||
|
Hilbish provides you with a few quality of life features and useful
|
||||||
|
functions to ensure you can make the shell fully yours.
|
||||||
|
|
||||||
|
These features include:
|
||||||
|
- Completion and history search menus
|
||||||
|
- Hinting and syntax highlighting (scripted by user)
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
Steps on installing Hilbish will be at the Install page in the navigation bar
|
||||||
|
at the top. This also included getting development builds from the GitHub
|
||||||
|
repository.
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
title: Frequently Asked Questions
|
||||||
|
layout: doc
|
||||||
|
weight: -20
|
||||||
|
menu: docs
|
||||||
|
---
|
||||||
|
|
||||||
|
# Is Hilbish POSIX compliant?
|
||||||
|
No, it is not. POSIX compliance is a non-goal. Perhaps in the future,
|
||||||
|
someone would be able to write a native plugin to support shell scripting
|
||||||
|
(which would be against it's main goal, but ....)
|
||||||
|
|
||||||
|
# Windows Support?
|
||||||
|
It compiles for Windows (CI ensures it does), but otherwise it is not
|
||||||
|
directly supported. If you'd like to improve this situation,
|
||||||
|
checkout [the discussion](https://github.com/Rosettea/Hilbish/discussions/165).
|
||||||
|
|
||||||
|
# Where is the API documentation?
|
||||||
|
The builtin `doc` command supplies all documentation of Hilbish provided
|
||||||
|
APIs. This will be on the website in the near future.
|
||||||
|
|
||||||
|
# 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.
|
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
title: Features
|
||||||
|
layout: doc
|
||||||
|
weight: -40
|
||||||
|
menu: docs
|
||||||
|
---
|
||||||
|
|
||||||
|
Hilbish has a wide range of features to enhance the user's experience and
|
||||||
|
is always adding new ones. If there is something missing here or something
|
||||||
|
you would like to see, please [start a discussion](https://github.com/Rosettea/Hilbish/discussions)
|
||||||
|
or comment on any existing ones which match your request.
|
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
title: Runner Mode
|
||||||
|
description: Customize the interactive script/command runner.
|
||||||
|
layout: doc
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "Features"
|
||||||
|
---
|
||||||
|
|
||||||
|
Hilbish allows you to change how interactive text can be interpreted.
|
||||||
|
This is mainly due to the fact that the default method Hilbish uses
|
||||||
|
is that it runs Lua first and then falls back to shell script.
|
||||||
|
|
||||||
|
In some cases, someone might want to switch to just shell script to avoid
|
||||||
|
it while interactive but still have a Lua config, or go full Lua to use
|
||||||
|
Hilbish as a REPL. This also allows users to add alternative languages,
|
||||||
|
instead of either like Fennel.
|
|
@ -0,0 +1,59 @@
|
||||||
|
---
|
||||||
|
title: Getting Started
|
||||||
|
layout: doc
|
||||||
|
weight: -10
|
||||||
|
menu: docs
|
||||||
|
---
|
||||||
|
|
||||||
|
To start Hilbish, open a terminal. If Hilbish has been installed and is not the
|
||||||
|
default shell, you can simply run `hilbish` to start it. This will launch
|
||||||
|
a normal interactive session.
|
||||||
|
To exit, you can either run the `exit` command or hit Ctrl+D.
|
||||||
|
|
||||||
|
# Setting as Default
|
||||||
|
## Login shell
|
||||||
|
There are a few ways to make Hilbish your default shell. A simple way is
|
||||||
|
to make it your user/login shell.
|
||||||
|
|
||||||
|
{{< warning `It is not recommended to set Hilbish as your login shell. That is expected to be a
|
||||||
|
POSIX compliant shell, which Hilbish is not. At most, there will just be a
|
||||||
|
few variables missing in your environment` >}}
|
||||||
|
|
||||||
|
To do that, simply run `chsh -s /usr/bin/hilbish`.
|
||||||
|
Some distros (namely Fedora) might have `lchsh` instead, which is used like `lchsh <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.
|
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
title: Install
|
||||||
|
description: Steps on how to install Hilbish on all the OSes and distros supported.
|
||||||
|
layout: page
|
||||||
|
---
|
||||||
|
|
||||||
|
## Official Binaries
|
||||||
|
The best way to get Hilbish is to get a build directly from GitHub.
|
||||||
|
At any time, there are 2 versions of Hilbish recommended for download:
|
||||||
|
the latest stable release, and development builds from the master branch.
|
||||||
|
|
||||||
|
You can download both at any time, but note that the development builds may
|
||||||
|
have breaking changes.
|
||||||
|
|
||||||
|
For the latest **stable release**, check here: https://github.com/Rosettea/Hilbish/releases/latest
|
||||||
|
For a **development build**: https://nightly.link/Rosettea/Hilbish/workflows/build/master
|
||||||
|
|
||||||
|
## Package Repositories
|
||||||
|
### Arch Linux (AUR)
|
||||||
|
Hilbish is on the AUR. Setup an AUR helper, and install.
|
||||||
|
Example with yay:
|
||||||
|
|
||||||
|
```
|
||||||
|
yay -S hilbish
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, from master branch:
|
||||||
|
```
|
||||||
|
yay -S hilbish-git
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alpine Linux
|
||||||
|
Hilbish is currentlty in the testing/edge repository for Alpine.
|
||||||
|
Follow the steps [here](https://wiki.alpinelinux.org/wiki/Enable_Community_Repository)
|
||||||
|
(Using testing repositories) and install:
|
||||||
|
```
|
||||||
|
apk add hilbish
|
||||||
|
```
|
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Rosettea
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,2 @@
|
||||||
|
+++
|
||||||
|
+++
|
|
@ -0,0 +1,7 @@
|
||||||
|
{{ define "main"}}
|
||||||
|
<main id="main">
|
||||||
|
<div>
|
||||||
|
<h1><a href="{{ "/" | relURL }}">Go Home</a></h1>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{{ end }}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<h{{ (add .Level 1) }} id="{{ .Anchor | safeURL }}">
|
||||||
|
{{ .Text | safeHTML }}
|
||||||
|
</h{{ (add .Level 1) }}>
|
||||||
|
{{ if eq .Text ""}}
|
||||||
|
<hr>
|
||||||
|
{{ end }}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}{{ if eq (substr .Destination 0 4) "http" }} target="_blank" rel="noopener"{{ end }}>
|
||||||
|
{{ .Text | safeHTML }}
|
||||||
|
</a>
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
{{- partial "head.html" . -}}
|
||||||
|
<body class="d-flex flex-column min-vh-100" style="overflow-x: hidden;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||||
|
<symbol id="check-circle-fill" fill="currentColor" viewBox="0 0 16 16">
|
||||||
|
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="info-fill" fill="currentColor" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="exclamation-triangle-fill" fill="currentColor" viewBox="0 0 16 16">
|
||||||
|
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/>
|
||||||
|
</symbol>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
{{- partial "header.html" . -}}
|
||||||
|
{{- block "main" . }}{{- end }}
|
||||||
|
{{- partial "footer.html" . -}}
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,53 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
<div class="container py-3 row">
|
||||||
|
<div class="container" style="width: 240px;">
|
||||||
|
<div class="p-3 col">
|
||||||
|
<ul class="nav nav-pills mb-auto">
|
||||||
|
{{ $currentPage := . }}
|
||||||
|
{{ range .Site.Menus.docs.ByWeight.Reverse }}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="{{ .URL }}" class="nav-link">
|
||||||
|
<strong>{{ .Title }}</strong>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{ if .Children }}
|
||||||
|
<ul style="list-style: none;">
|
||||||
|
{{ range .Children }}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="{{ .URL }}" class="nav-link">
|
||||||
|
{{ .Title }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-3 col">
|
||||||
|
<div>
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
<p><em>
|
||||||
|
{{ $date := .Date.UTC.Format "Jan 2, 2006" }}
|
||||||
|
{{ $lastmod := .Lastmod.UTC.Format "Jan 2, 2006" }}
|
||||||
|
{{ if and (ne $lastmod $date) (gt .Lastmod .Date) }}
|
||||||
|
Last updated {{ $lastmod }}<br>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if .Description }}
|
||||||
|
{{ .Description }}<br>
|
||||||
|
{{ end}}
|
||||||
|
</em></p>
|
||||||
|
{{.Content}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer mt-auto">
|
||||||
|
<p class="card-small text-muted">
|
||||||
|
Want to help improve this page? <a href="https://github.com/Rosettea/Hilbish/issues/new/choose">Create an issue.</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
<main>
|
||||||
|
<div class="container mt-2">
|
||||||
|
{{.Content}}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{{ end }}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
<main>
|
||||||
|
<div class="container mt-2">
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
{{.Content}}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{{ end }}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
<main style="max-width: 80em; margin: auto;">
|
||||||
|
{{.Content}}
|
||||||
|
</main>
|
||||||
|
{{ end }}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<footer class="footer mt-auto mt-auto py-3 bg-light row">
|
||||||
|
<div class="col mb-3">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col mb-3">
|
||||||
|
<a href="/Hilbish" class="d-flex align-items-center mb-3 link-dark text-decoration-none">
|
||||||
|
<img src="/Hilbish/hilbish-flower.png" alt="" height="48" class="d-inline-block align-text-top">
|
||||||
|
</a>
|
||||||
|
<p class="text-muted">
|
||||||
|
Rosettea © 2022
|
||||||
|
<br>
|
||||||
|
Made with <i class="fa-solid fa-heart" style="color: #f6345b;"></i>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col mb-3"></div>
|
||||||
|
<div class="col mb-3"></div>
|
||||||
|
<div class="col mb-3"></div>
|
||||||
|
|
||||||
|
<div class="col mb-3">
|
||||||
|
<h5>Hilbish</h5>
|
||||||
|
<ul class="nav flex-column">
|
||||||
|
<li class="nav-item mb-2"><a href="/Hilbish" class="nav-link p-0 text-muted">Home</a></li>
|
||||||
|
<li class="nav-item mb-2"><a href="/Hilbish/docs/faq" class="nav-link p-0 text-muted">FAQ</a></li>
|
||||||
|
<li class="nav-item mb-2"><a href="https://github.com/Rosettea/Hilbish" class="nav-link p-0 text-muted">Source</a></li>
|
||||||
|
<li class="nav-item mb-2"><a href="https://github.com/Rosettea/Hilbish/releases" class="nav-link p-0 text-muted">Releases</a></li>
|
||||||
|
<li class="nav-item mb-2"><a href="/Hilbish/docs" class="nav-link p-0 text-muted">Documentation</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col mb-3"></div>
|
||||||
|
</footer>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<head>
|
||||||
|
{{ $title := print .Title " — " .Site.Title }}
|
||||||
|
{{ if .IsHome }}{{ $title = .Site.Title }}{{ end }}
|
||||||
|
<title>{{ $title }}</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no"/>
|
||||||
|
|
||||||
|
<meta name="theme-color" content="#ff89dd">
|
||||||
|
<meta content="/Hilbish/hilbish-flower.png" property="og:image" />
|
||||||
|
|
||||||
|
<meta property="og:site_name" content="Hilbish" />
|
||||||
|
<meta content="{{ $title }}" property="og:title" />
|
||||||
|
<meta content="{{if .Description}}{{ .Description }}{{ else }}{{ .Summary }}{{ end }}" property="og:description" />
|
||||||
|
<meta content="{{if .Description}}{{ .Description }}{{ else }}{{ .Summary }}{{ end }}" name="description" />
|
||||||
|
|
||||||
|
<meta name="revisit-after" content="2 days">
|
||||||
|
<meta name="keywords" content="Lua, Hilbish, Linux, Shell">
|
||||||
|
|
||||||
|
<meta property="og:locale" content="en_GB" />
|
||||||
|
<link rel="canonical" href="https://rosettea.github.io/Hilbish/" />
|
||||||
|
<meta property="og:url" content="https://rosettea.github.io/Hilbish/" />
|
||||||
|
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
</head>
|
|
@ -0,0 +1,25 @@
|
||||||
|
<header>
|
||||||
|
<nav class="navbar navbar-expand-md sticky-top bg-light">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="/Hilbish">
|
||||||
|
<img src="/Hilbish/hilbish-flower.png" alt="" height="24" class="d-inline-block align-text-top">
|
||||||
|
Hilbish
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
|
{{ $currentPage := . }}
|
||||||
|
{{ range .Site.Menus.nav }}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="{{ .URL }}" class="nav-link {{ if $currentPage.IsMenuCurrent "nav" . }}active{{ end }}">
|
||||||
|
{{ .Name }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<div class="alert alert-warning d-flex align-items-center" role="alert">
|
||||||
|
<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Warning:"><use xlink:href="#exclamation-triangle-fill"/></svg>
|
||||||
|
<div>
|
||||||
|
{{ .Get 0 }}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,21 @@
|
||||||
|
# theme.toml template for a Hugo theme
|
||||||
|
# See https://github.com/gohugoio/hugoThemes#themetoml for an example
|
||||||
|
|
||||||
|
name = "Hsh"
|
||||||
|
license = "MIT"
|
||||||
|
licenselink = "https://github.com/yourname/yourtheme/blob/master/LICENSE"
|
||||||
|
description = ""
|
||||||
|
homepage = "http://example.com/"
|
||||||
|
tags = []
|
||||||
|
features = []
|
||||||
|
min_version = "0.41.0"
|
||||||
|
|
||||||
|
[author]
|
||||||
|
name = ""
|
||||||
|
homepage = ""
|
||||||
|
|
||||||
|
# If porting an existing theme
|
||||||
|
[original]
|
||||||
|
name = ""
|
||||||
|
homepage = ""
|
||||||
|
repo = ""
|
Loading…
Reference in New Issue