mirror of https://github.com/Hilbis/Hilbish
Compare commits
No commits in common. "1e2c88f4a40b97a00c38d9208d56855019086bc2" and "bd693a4057c5435d3bdddc2c0444253b3524475c" have entirely different histories.
1e2c88f4a4
...
bd693a4057
|
@ -26,10 +26,8 @@ jobs:
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '1.17.7'
|
go-version: '1.17.7'
|
||||||
- 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 }} ./bin/task
|
run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} make
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
if: matrix.goos == 'windows'
|
if: matrix.goos == 'windows'
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -9,6 +9,8 @@ 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)
|
||||||
|
|
37
CHANGELOG.md
37
CHANGELOG.md
|
@ -1,10 +1,6 @@
|
||||||
# 🎀 Changelog
|
# 🎀 Changelog
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
**NOTE:** Hilbish now uses [Task] insead of Make for builds.
|
|
||||||
|
|
||||||
[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.
|
||||||
|
@ -56,25 +52,11 @@ 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`
|
|
||||||
|
|
||||||
[#197]: https://github.com/Rosettea/Hilbish/issues/197
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- **Breaking Change:** Upgraded to Lua 5.4.
|
- **Breaking Change:** Upgraded to Lua 5.4.
|
||||||
|
@ -90,17 +72,12 @@ It can (at the moment) have 4 variables:
|
||||||
User input has been added to the return to account for runners wanting to
|
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.
|
|
||||||
- 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
|
||||||
|
@ -135,16 +112,6 @@ 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
|
|
||||||
|
|
||||||
## [1.2.0] - 2022-03-17
|
## [1.2.0] - 2022-03-17
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
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
|
21
README.md
21
README.md
|
@ -66,7 +66,6 @@ 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/#/)
|
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
First, clone Hilbish. The recursive is required, as some Lua libraries
|
First, clone Hilbish. The recursive is required, as some Lua libraries
|
||||||
|
@ -79,16 +78,30 @@ go get -d ./...
|
||||||
|
|
||||||
To build, run:
|
To build, run:
|
||||||
```
|
```
|
||||||
task
|
make dev
|
||||||
```
|
```
|
||||||
|
|
||||||
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`)
|
||||||
task build
|
make build
|
||||||
```
|
```
|
||||||
|
|
||||||
After you did all that, run `sudo task install` to install Hilbish globally.
|
After you did all that, run `sudo make 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.
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
# https://taskfile.dev
|
|
||||||
|
|
||||||
version: '3'
|
|
||||||
|
|
||||||
vars:
|
|
||||||
PREFIX: '{{default "/usr" .PREFIX}}'
|
|
||||||
bindir__: '{{.PREFIX}}/bin'
|
|
||||||
BINDIR: '{{default .bindir__ .BINDIR}}'
|
|
||||||
libdir__: '{{.PREFIX}}/share/hilbish'
|
|
||||||
LIBDIR: '{{default .libdir__ .LIBDIR}}'
|
|
||||||
GOFLAGS: '-ldflags "-s -w"'
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
default:
|
|
||||||
cmds:
|
|
||||||
- go build {{.GOFLAGS}}
|
|
||||||
vars:
|
|
||||||
GOFLAGS: '-ldflags "-s -w -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"'
|
|
||||||
|
|
||||||
build:
|
|
||||||
cmds:
|
|
||||||
- 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
|
|
8
api.go
8
api.go
|
@ -44,6 +44,7 @@ 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,
|
||||||
|
@ -102,6 +103,10 @@ 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")
|
||||||
|
@ -109,6 +114,7 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
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.")
|
||||||
|
@ -193,7 +199,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.Emit("hilbish.vimMode", mode)
|
hooks.Em.Emit("hilbish.vimMode", mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unsetVimMode() {
|
func unsetVimMode() {
|
||||||
|
|
37
complete.go
37
complete.go
|
@ -11,37 +11,8 @@ import (
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func splitQuote(str string) []string {
|
|
||||||
split := []string{}
|
|
||||||
sb := &strings.Builder{}
|
|
||||||
quoted := false
|
|
||||||
|
|
||||||
for _, r := range str {
|
|
||||||
if r == '"' {
|
|
||||||
quoted = !quoted
|
|
||||||
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) {
|
||||||
q := splitQuote(ctx)
|
return matchPath(query)
|
||||||
|
|
||||||
return matchPath(q[len(q) - 1])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
||||||
|
@ -97,8 +68,6 @@ 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
|
||||||
|
|
||||||
|
@ -118,9 +87,7 @@ func matchPath(query string) ([]string, string) {
|
||||||
if file.IsDir() {
|
if file.IsDir() {
|
||||||
entry = entry + string(os.PathSeparator)
|
entry = entry + string(os.PathSeparator)
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(oldQuery, "\"") {
|
entry = escapeFilename(entry)
|
||||||
entry = escapeFilename(entry)
|
|
||||||
}
|
|
||||||
entries = append(entries, entry)
|
entries = append(entries, entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,5 @@ 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
|
||||||
|
|
||||||
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,9 +11,6 @@ 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`
|
||||||
|
|
|
@ -11,7 +11,6 @@ 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()
|
||||||
|
@ -69,9 +68,3 @@ 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,11 +12,6 @@ function bait.catch(name, cb) end
|
||||||
--- @param cb function
|
--- @param cb function
|
||||||
function bait.catchOnce(name, cb) end
|
function bait.catchOnce(name, cb) end
|
||||||
|
|
||||||
--- Removes the `catcher` for the event with `name`
|
|
||||||
--- For this to work, `catcher` has to be the same function used to catch
|
|
||||||
--- an event, like one saved to a variable.
|
|
||||||
function bait.release() end
|
|
||||||
|
|
||||||
--- 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,10 +22,6 @@ 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
|
||||||
|
|
27
exec.go
27
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.Emit("command.preexec", input, cmdString)
|
hooks.Em.Emit("command.preexec", input, cmdString)
|
||||||
|
|
||||||
rerun:
|
rerun:
|
||||||
var exitCode uint8
|
var exitCode uint8
|
||||||
|
@ -101,7 +101,7 @@ func runInput(input string, priv bool) {
|
||||||
cmdFinish(0, input, priv)
|
cmdFinish(0, input, priv)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
input, exitCode, cont, err = handleSh(cmdString)
|
input, exitCode, cont, err = handleSh(input)
|
||||||
case "hybridRev":
|
case "hybridRev":
|
||||||
_, _, _, err = handleSh(input)
|
_, _, _, err = handleSh(input)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -112,7 +112,7 @@ func runInput(input string, priv bool) {
|
||||||
case "lua":
|
case "lua":
|
||||||
input, exitCode, err = handleLua(cmdString)
|
input, exitCode, err = handleLua(cmdString)
|
||||||
case "sh":
|
case "sh":
|
||||||
input, exitCode, cont, err = handleSh(cmdString)
|
input, exitCode, cont, err = handleSh(input)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// can only be a string or function so
|
// can only be a string or function so
|
||||||
|
@ -140,7 +140,7 @@ func runInput(input string, priv bool) {
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if exErr, ok := isExecError(err); ok {
|
if exErr, ok := isExecError(err); ok {
|
||||||
hooks.Emit("command." + exErr.typ, exErr.cmd)
|
hooks.Em.Emit("command." + exErr.typ, exErr.cmd)
|
||||||
err = exErr.sprint()
|
err = exErr.sprint()
|
||||||
}
|
}
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
@ -152,7 +152,6 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,17 +220,7 @@ func handleLua(cmdString string) (string, uint8, error) {
|
||||||
return cmdString, 125, err
|
return cmdString, 125, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSh(cmdString string) (input string, exitCode uint8, cont bool, runErr error) {
|
func handleSh(cmdString string) (string, uint8, bool, 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
|
||||||
|
@ -551,9 +540,13 @@ 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.Emit("command.exit", rt.IntValue(int64(code)), cmdstr, private)
|
hooks.Em.Emit("command.exit", rt.IntValue(int64(code)), cmdstr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,27 @@
|
||||||
package bait
|
package bait
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"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 listenerType int
|
|
||||||
const (
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bait struct{
|
type Bait struct{
|
||||||
|
Em *emission.Emitter
|
||||||
Loader packagelib.Loader
|
Loader packagelib.Loader
|
||||||
recoverer Recoverer
|
|
||||||
handlers map[string][]*Listener
|
|
||||||
rtm *rt.Runtime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Bait instance.
|
func New() Bait {
|
||||||
func New(rtm *rt.Runtime) *Bait {
|
emitter := emission.NewEmitter()
|
||||||
b := &Bait{
|
emitter.RecoverWith(func(hookname, hookfunc interface{}, err error) {
|
||||||
handlers: make(map[string][]*Listener),
|
emitter.Off(hookname, hookfunc)
|
||||||
rtm: rtm,
|
fmt.Println(err)
|
||||||
|
})
|
||||||
|
b := Bait{
|
||||||
|
Em: emitter,
|
||||||
}
|
}
|
||||||
b.Loader = packagelib.Loader{
|
b.Loader = packagelib.Loader{
|
||||||
Load: b.loaderFunc,
|
Load: b.loaderFunc,
|
||||||
|
@ -45,148 +31,11 @@ func New(rtm *rt.Runtime) *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 {
|
|
||||||
// panicking here won't actually cause hilbish to panic and instead will
|
|
||||||
// print the error and remove the hook. reference the recoverer function in lua.go
|
|
||||||
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},
|
|
||||||
}
|
}
|
||||||
mod := rt.NewTable()
|
mod := rt.NewTable()
|
||||||
util.SetExports(rtm, mod, exports)
|
util.SetExports(rtm, mod, exports)
|
||||||
|
@ -240,7 +89,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.Emit(name, ifaceSlice...)
|
b.Em.Emit(name, ifaceSlice...)
|
||||||
|
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
@ -255,7 +104,9 @@ func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.OnLua(name, catcher)
|
b.Em.On(name, func(args ...interface{}) {
|
||||||
|
handleHook(t, c, name, catcher, args...)
|
||||||
|
})
|
||||||
|
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
@ -270,22 +121,9 @@ func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.OnceLua(name, catcher)
|
b.Em.Once(name, func(args ...interface{}) {
|
||||||
|
handleHook(t, c, name, catcher, args...)
|
||||||
return c.Next(), nil
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// release(name, catcher)
|
|
||||||
// Removes the `catcher` for the event with `name`
|
|
||||||
// For this to work, `catcher` has to be the same function used to catch
|
|
||||||
// an event, like one saved to a variable.
|
|
||||||
func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
name, catcher, err := util.HandleStrCallback(t, c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b.OffLua(name, catcher)
|
|
||||||
|
|
||||||
return c.Next(), nil
|
return c.Next(), 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 *bait.Bait
|
Events *emission.Emitter
|
||||||
Loader packagelib.Loader
|
Loader packagelib.Loader
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(rtm *rt.Runtime) Commander {
|
func New() Commander {
|
||||||
c := Commander{
|
c := Commander{
|
||||||
Events: bait.New(rtm),
|
Events: emission.NewEmitter(),
|
||||||
}
|
}
|
||||||
c.Loader = packagelib.Loader{
|
c.Loader = packagelib.Loader{
|
||||||
Load: c.loaderFunc,
|
Load: c.loaderFunc,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"os"
|
"os"
|
||||||
|
@ -28,7 +27,6 @@ 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)
|
||||||
|
@ -218,21 +216,3 @@ 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
|
|
||||||
}
|
|
||||||
|
|
56
history.go
56
history.go
|
@ -4,69 +4,21 @@ 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(path string) *fileHistory {
|
func newFileHistory() *fileHistory {
|
||||||
dir := filepath.Dir(path)
|
err := os.MkdirAll(defaultHistDir, 0755)
|
||||||
|
|
||||||
err := os.MkdirAll(dir, 0755)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(defaultHistPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -81,7 +33,7 @@ func newFileHistory(path string) *fileHistory {
|
||||||
}
|
}
|
||||||
itms = append(itms, l)
|
itms = append(itms, l)
|
||||||
}
|
}
|
||||||
f, err := os.OpenFile(path, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0755)
|
f, err := os.OpenFile(defaultHistPath, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,7 @@ package main
|
||||||
import "golang.org/x/sys/windows"
|
import "golang.org/x/sys/windows"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// vt output (escape codes)
|
var mode uint32
|
||||||
var outMode uint32
|
windows.GetConsoleMode(windows.Stdout, &mode)
|
||||||
windows.GetConsoleMode(windows.Stdout, &outMode)
|
windows.SetConsoleMode(windows.Stdout, mode | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
||||||
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.Emit("job.start", rt.UserDataValue(j.ud))
|
hooks.Em.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.Emit("job.done", rt.UserDataValue(j.ud))
|
hooks.Em.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.Emit("job.add", rt.UserDataValue(jb.ud))
|
hooks.Em.Emit("job.add", rt.UserDataValue(jb.ud))
|
||||||
|
|
||||||
return jb
|
return jb
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 8467b87dd8d49c68b4100b2d129d5f071544b8cf
|
Subproject commit d60cd77c73875b5bb55e5a2fdc30bae01a7ac499
|
28
lua.go
28
lua.go
|
@ -12,16 +12,12 @@ 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)
|
||||||
|
@ -32,40 +28,26 @@ func luaInit() {
|
||||||
lib.LoadLibs(l, fs.Loader)
|
lib.LoadLibs(l, fs.Loader)
|
||||||
lib.LoadLibs(l, terminal.Loader)
|
lib.LoadLibs(l, terminal.Loader)
|
||||||
|
|
||||||
cmds := commander.New(l)
|
cmds := commander.New()
|
||||||
// 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(args ...interface{}) {
|
cmds.Events.On("commandRegister", func(cmdName string, cmd *rt.Closure) {
|
||||||
cmdName := args[0].(string)
|
|
||||||
cmd := args[1].(*rt.Closure)
|
|
||||||
|
|
||||||
commands[cmdName] = cmd
|
commands[cmdName] = cmd
|
||||||
})
|
})
|
||||||
cmds.Events.On("commandDeregister", func(args ...interface{}) {
|
cmds.Events.On("commandDeregister", func(cmdName string) {
|
||||||
cmdName := args[0].(string)
|
|
||||||
|
|
||||||
delete(commands, cmdName)
|
delete(commands, cmdName)
|
||||||
})
|
})
|
||||||
lib.LoadLibs(l, cmds.Loader)
|
lib.LoadLibs(l, cmds.Loader)
|
||||||
|
|
||||||
hooks = bait.New(l)
|
hooks = bait.New()
|
||||||
hooks.SetRecoverer(func(event string, handler *bait.Listener, err interface{}) {
|
|
||||||
fmt.Println("Error in", event, "event:", err)
|
|
||||||
hooks.Off(event, handler)
|
|
||||||
})
|
|
||||||
|
|
||||||
lib.LoadLibs(l, hooks.Loader)
|
lib.LoadLibs(l, hooks.Loader)
|
||||||
|
|
||||||
// Add Ctrl-C handler
|
// Add Ctrl-C handler
|
||||||
hooks.On("signal.sigint", func(...interface{}) {
|
hooks.Em.On("signal.sigint", func() {
|
||||||
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 {
|
||||||
|
|
16
main.go
16
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()
|
||||||
lr = newLineReader("", false)
|
|
||||||
luaInit()
|
luaInit()
|
||||||
|
lr = newLineReader("", false)
|
||||||
// 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,7 +138,6 @@ 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))
|
||||||
|
@ -177,7 +176,7 @@ input:
|
||||||
|
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
// Exit if user presses ^D (ctrl + d)
|
// Exit if user presses ^D (ctrl + d)
|
||||||
hooks.Emit("hilbish.exit")
|
hooks.Em.Emit("hilbish.exit")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -196,7 +195,7 @@ input:
|
||||||
input = strings.TrimSpace(input)
|
input = strings.TrimSpace(input)
|
||||||
if len(input) == 0 {
|
if len(input) == 0 {
|
||||||
running = true
|
running = true
|
||||||
hooks.Emit("command.exit", 0)
|
hooks.Em.Emit("command.exit", 0)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +226,7 @@ input:
|
||||||
}
|
}
|
||||||
|
|
||||||
func continuePrompt(prev string) (string, error) {
|
func continuePrompt(prev string) (string, error) {
|
||||||
hooks.Emit("multiline", nil)
|
hooks.Em.Emit("multiline", nil)
|
||||||
lr.SetPrompt(multilinePrompt)
|
lr.SetPrompt(multilinePrompt)
|
||||||
cont, err := lr.Read()
|
cont, err := lr.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -269,6 +268,11 @@ 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,54 @@
|
||||||
|
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,19 +1,9 @@
|
||||||
local fs = require 'fs'
|
-- Add command builtins
|
||||||
|
require 'nature.commands.cd'
|
||||||
-- explanation: this specific function gives to us info about
|
require 'nature.commands.cdr'
|
||||||
-- the currently running source. this includes a path to the
|
require 'nature.commands.doc'
|
||||||
-- source file (info.source)
|
require 'nature.commands.exit'
|
||||||
-- we will use that to automatically load all commands by reading
|
require 'nature.commands.guide'
|
||||||
-- all the files in this dir and just requiring it.
|
require 'nature.commands.disown'
|
||||||
local info = debug.getinfo(1)
|
require 'nature.commands.fg'
|
||||||
local commandDir = fs.dir(info.source)
|
require 'nature.commands.bg'
|
||||||
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
|
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
local fs = require 'fs'
|
local fs = require 'fs'
|
||||||
|
|
||||||
local oldShRunner = hilbish.runner.sh
|
function cdHandle(inp)
|
||||||
function hilbish.runner.sh(input)
|
local res = hilbish.runner.lua(inp)
|
||||||
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)
|
||||||
|
@ -16,3 +21,5 @@ function hilbish.runner.sh(input)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
hilbish.runner.setMode(cdHandle)
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
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)
|
|
|
@ -1,5 +0,0 @@
|
||||||
local bait = require 'bait'
|
|
||||||
|
|
||||||
bait.catch('command.exit', function(_, cmd, priv)
|
|
||||||
if not priv and hilbish.opts.history then hilbish.history.add(cmd) end
|
|
||||||
end)
|
|
|
@ -20,12 +20,7 @@ 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
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
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)
|
|
|
@ -99,7 +99,7 @@ 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, fmtEscape(g.Name), RESET)
|
comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, g.Name, RESET)
|
||||||
rl.tcUsedY++
|
rl.tcUsedY++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) {
|
||||||
comp += seqInvert
|
comp += seqInvert
|
||||||
}
|
}
|
||||||
|
|
||||||
comp += fmt.Sprintf("%-"+cellWidth+"s %s", fmtEscape(g.Suggestions[i]), seqReset)
|
comp += fmt.Sprintf("%-"+cellWidth+"s %s", g.Suggestions[i], 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,7 +1,5 @@
|
||||||
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,
|
||||||
|
@ -287,7 +285,3 @@ 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), fmtEscape(item))
|
sugg := fmt.Sprintf("\r%s%-"+cellWidth+"s", highlight(y, 0), 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), fmtEscape(alt))
|
alt = fmt.Sprintf(" %s%"+cellWidthAlt+"s", highlight(y, 1), 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, fmtEscape(g.Name), RESET)
|
comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, 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), fmtEscape(item), seqReset)
|
description, highlight(y), 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
|
||||||
|
|
|
@ -198,8 +198,6 @@ 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,6 +4,7 @@ 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.
|
||||||
|
@ -208,7 +209,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 getWidth([]rune(stripped))
|
return uniseg.GraphemeClusterCount(stripped)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rl *Instance) echoRightPrompt() {
|
func (rl *Instance) echoRightPrompt() {
|
||||||
|
|
|
@ -94,9 +94,6 @@ 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]...)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
32
rl.go
32
rl.go
|
@ -13,22 +13,18 @@ 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 {
|
||||||
lr.fileHist = newFileHistory(defaultHistPath)
|
fileHist = newFileHistory()
|
||||||
rl.SetHistoryCtrlR("History", &luaHistory{})
|
rl.SetHistoryCtrlR("History", fileHist)
|
||||||
rl.HistoryAutoWrite = false
|
rl.HistoryAutoWrite = false
|
||||||
}
|
}
|
||||||
rl.ShowVimMode = false
|
rl.ShowVimMode = false
|
||||||
|
@ -48,14 +44,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.Emit("hilbish.vimAction", actionStr, args)
|
hooks.Em.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(hinter),
|
retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(highlighter),
|
||||||
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)
|
||||||
|
@ -175,11 +171,13 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
return pfx, compGroups
|
return pfx, compGroups
|
||||||
}
|
}
|
||||||
|
|
||||||
return lr
|
return &lineReader{
|
||||||
|
rl,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) Read() (string, error) {
|
func (lr *lineReader) Read() (string, error) {
|
||||||
hooks.Emit("command.precmd", nil)
|
hooks.Em.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 {
|
||||||
|
@ -214,7 +212,7 @@ func (lr *lineReader) SetRightPrompt(p string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) AddHistory(cmd string) {
|
func (lr *lineReader) AddHistory(cmd string) {
|
||||||
lr.fileHist.Write(cmd)
|
fileHist.Write(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) ClearInput() {
|
func (lr *lineReader) ClearInput() {
|
||||||
|
@ -255,7 +253,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(lr.fileHist.Len()))), nil
|
return c.PushingNext1(t.Runtime, rt.IntValue(int64(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) {
|
||||||
|
@ -267,17 +265,17 @@ func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd, _ := lr.fileHist.GetLine(int(idx))
|
cmd, _ := 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 := lr.fileHist.Len()
|
size := fileHist.Len()
|
||||||
|
|
||||||
for i := 1; i < size; i++ {
|
for i := 1; i < size; i++ {
|
||||||
cmd, _ := lr.fileHist.GetLine(i)
|
cmd, _ := fileHist.GetLine(i)
|
||||||
tbl.Set(rt.IntValue(int64(i)), rt.StringValue(cmd))
|
tbl.Set(rt.IntValue(int64(i)), rt.StringValue(cmd))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,6 +283,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) {
|
||||||
lr.fileHist.clear()
|
fileHist.clear()
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
input, exitCode, cont, err := execSh(cmd)
|
input, exitCode, cont, err := handleSh(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())
|
||||||
|
|
|
@ -15,11 +15,11 @@ func handleSignals() {
|
||||||
|
|
||||||
for s := range c {
|
for s := range c {
|
||||||
switch s {
|
switch s {
|
||||||
case os.Interrupt: hooks.Emit("signal.sigint")
|
case os.Interrupt: hooks.Em.Emit("signal.sigint")
|
||||||
case syscall.SIGTERM: exit(0)
|
case syscall.SIGTERM: exit(0)
|
||||||
case syscall.SIGWINCH: hooks.Emit("signal.resize")
|
case syscall.SIGWINCH: hooks.Em.Emit("signal.resize")
|
||||||
case syscall.SIGUSR1: hooks.Emit("signal.sigusr1")
|
case syscall.SIGUSR1: hooks.Em.Emit("signal.sigusr1")
|
||||||
case syscall.SIGUSR2: hooks.Emit("signal.sigusr2")
|
case syscall.SIGUSR2: hooks.Em.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.Emit("signal.sigint")
|
hooks.Em.Emit("signal.sigint")
|
||||||
if !running && interactive {
|
if !running && interactive {
|
||||||
lr.ClearInput()
|
lr.ClearInput()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,14 @@
|
||||||
|
|
||||||
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 = util.ExpandHome("~\\Appdata\\Roaming\\Hilbish") // ~ and \ gonna cry?
|
dataDir = "~\\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 = ""
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue