mirror of https://github.com/Hilbis/Hilbish
chore: update branch
commit
398702cafa
|
@ -1,8 +1,12 @@
|
||||||
name: Build
|
name: Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
- push
|
push:
|
||||||
- pull_request
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -19,18 +23,18 @@ jobs:
|
||||||
goos: windows
|
goos: windows
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.18.8'
|
go-version: '1.22.2'
|
||||||
- name: Download Task
|
- name: Download Task
|
||||||
run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
|
run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
|
||||||
- name: Build
|
- name: Build
|
||||||
run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} ./bin/task
|
run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} ./bin/task
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v4
|
||||||
if: matrix.goos == 'windows'
|
if: matrix.goos == 'windows'
|
||||||
with:
|
with:
|
||||||
name: hilbish-${{ matrix.goos }}-${{ matrix.goarch }}
|
name: hilbish-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||||
|
@ -44,7 +48,7 @@ jobs:
|
||||||
libs
|
libs
|
||||||
docs
|
docs
|
||||||
emmyLuaDocs
|
emmyLuaDocs
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v4
|
||||||
if: matrix.goos != 'windows'
|
if: matrix.goos != 'windows'
|
||||||
with:
|
with:
|
||||||
name: hilbish-${{ matrix.goos }}-${{ matrix.goarch }}
|
name: hilbish-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||||
|
|
|
@ -26,7 +26,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
|
|
|
@ -9,8 +9,8 @@ jobs:
|
||||||
gen:
|
gen:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v2
|
- uses: actions/setup-go@v5
|
||||||
- name: Run docgen
|
- name: Run docgen
|
||||||
run: go run cmd/docgen/docgen.go
|
run: go run cmd/docgen/docgen.go
|
||||||
- name: Commit new docs
|
- name: Commit new docs
|
||||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
||||||
create-release:
|
create-release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- uses: taiki-e/create-gh-release-action@v1
|
- uses: taiki-e/create-gh-release-action@v1
|
||||||
with:
|
with:
|
||||||
title: Hilbish $tag
|
title: Hilbish $tag
|
||||||
|
@ -30,7 +30,7 @@ jobs:
|
||||||
- goarch: arm64
|
- goarch: arm64
|
||||||
goos: windows
|
goos: windows
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
|
@ -1,27 +1,34 @@
|
||||||
name: Build website
|
name: Build website
|
||||||
|
|
||||||
on:
|
on:
|
||||||
- push
|
push:
|
||||||
- pull_request
|
branches:
|
||||||
|
- master
|
||||||
|
tags:
|
||||||
|
- v[0-9]+.*
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup Hugo
|
- name: Setup Hugo
|
||||||
uses: peaceiris/actions-hugo@v2
|
uses: peaceiris/actions-hugo@v3
|
||||||
with:
|
with:
|
||||||
hugo-version: 'latest'
|
hugo-version: '0.111.3'
|
||||||
extended: true
|
extended: true
|
||||||
|
|
||||||
- name: Set branch name
|
- name: Set branch name
|
||||||
id: branch
|
id: branch
|
||||||
run: echo "BRANCH_NAME=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> "$GITHUB_ENV"
|
run: echo "BRANCH_NAME=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/*/}}" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Fix base URL
|
- name: Fix base URL
|
||||||
if: env.BRANCH_NAME != 'master' && github.repository_owner == 'Rosettea'
|
if: env.BRANCH_NAME != 'master' && github.repository_owner == 'Rosettea'
|
||||||
|
@ -32,14 +39,14 @@ jobs:
|
||||||
|
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
if: env.BRANCH_NAME == 'master' && github.repository_owner == 'Rosettea'
|
if: env.BRANCH_NAME == 'master' && github.repository_owner == 'Rosettea'
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
publish_dir: ./website/public
|
publish_dir: ./website/public
|
||||||
keep_files: true
|
keep_files: true
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
if: env.BRANCH_NAME != 'master' && github.repository_owner == 'Rosettea'
|
if: env.BRANCH_NAME != 'master' && github.repository_owner == 'Rosettea'
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
publish_dir: ./website/public
|
publish_dir: ./website/public
|
||||||
|
|
84
CHANGELOG.md
84
CHANGELOG.md
|
@ -1,6 +1,85 @@
|
||||||
# 🎀 Changelog
|
# 🎀 Changelog
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
### Added
|
||||||
|
- Forward/Right arrow key will fill in hint text (#327)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Skip over file and prevent panic if info cannot be retrieved during file completion (due to permission error or anything else)
|
||||||
|
|
||||||
|
## [2.3.3] - 2024-11-04
|
||||||
|
### Fixed
|
||||||
|
- Heredocs having issues
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Adding `\` at the end of input will add a newline and prompt for more input.
|
||||||
|
|
||||||
|
## [2.3.2] - 2024-07-30
|
||||||
|
### Fixed
|
||||||
|
- Command path searching due to 2.3 changes to the shell interpreter
|
||||||
|
|
||||||
|
## [2.3.1] - 2024-07-27
|
||||||
|
[hehe when you see it release](https://youtu.be/AaAF51Gwbxo?si=rhj2iYuQRkqDa693&t=64)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `hilbish.opts.tips` was added to display random tips on start up.
|
||||||
|
Displayed tips can be modified via the `hilbish.tips` table.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix a minor regression related to the cd command not working with relative paths
|
||||||
|
- Updated the motd for 2.3
|
||||||
|
|
||||||
|
## [2.3.0] - 2024-07-20
|
||||||
|
### Added
|
||||||
|
- `commander.registry` function to get all registered commanders.
|
||||||
|
- `fs.pipe` function to get a pair of connected files (a pipe).
|
||||||
|
- Added an alternative 2nd parameter to `hilbish.run`, which is `streams`.
|
||||||
|
`streams` is a table of input and output streams to run the command with.
|
||||||
|
It uses these 3 keys:
|
||||||
|
- `input` as standard input for the command
|
||||||
|
- `out` as standard output
|
||||||
|
- `err` as standard error
|
||||||
|
|
||||||
|
Here is a minimal example of the new usage which allows users to now pipe commands
|
||||||
|
directly via Lua functions:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local fs = require 'fs'
|
||||||
|
local pr, pw = fs.pipe()
|
||||||
|
hilbish.run('ls -l', {
|
||||||
|
stdout = pw,
|
||||||
|
stderr = pw,
|
||||||
|
})
|
||||||
|
|
||||||
|
pw:close()
|
||||||
|
|
||||||
|
hilbish.run('wc -l', {
|
||||||
|
stdin = pr
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- The `-S` flag will be set to Hilbish's absolute path
|
||||||
|
- Hilbish now builds on any Unix (if any dependencies also work, which should.)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix ansi attributes causing issues with text when cut off in greenhouse
|
||||||
|
- Fix greenhouse appearing on terminal resize
|
||||||
|
- Fix crashes when history goes out of bounds when using history navigation
|
||||||
|
- `exec` command should return if no arg presented
|
||||||
|
- Commanders can now be cancelled by Ctrl-C and wont hang the shell anymore.
|
||||||
|
See [issue 198](https://github.com/Rosettea/Hilbish/issues/198).
|
||||||
|
- Shell interpreter can now preserve its environment and set PWD properly.
|
||||||
|
|
||||||
|
## [2.2.3] - 2024-04-27
|
||||||
|
### Fixed
|
||||||
|
- Highligher and hinter work now, since it was regressed from the previous minor release.
|
||||||
|
- `cat` command no longer prints extra newline at end of each file
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `cat` command now reads files in chunks, allowing for reading large files
|
||||||
|
|
||||||
|
## [2.2.2] - 2024-04-16
|
||||||
### Fixed
|
### Fixed
|
||||||
- Line refresh fixes (less flicker)
|
- Line refresh fixes (less flicker)
|
||||||
- Do more checks for a TTY
|
- Do more checks for a TTY
|
||||||
|
@ -708,6 +787,11 @@ This input for example will prompt for more input to complete:
|
||||||
|
|
||||||
First "stable" release of Hilbish.
|
First "stable" release of Hilbish.
|
||||||
|
|
||||||
|
[2.3.3]: https://github.com/Rosettea/Hilbish/compare/v2.3.2...v2.3.3
|
||||||
|
[2.3.2]: https://github.com/Rosettea/Hilbish/compare/v2.3.1...v2.3.2
|
||||||
|
[2.3.1]: https://github.com/Rosettea/Hilbish/compare/v2.3.0...v2.3.1
|
||||||
|
[2.3.0]: https://github.com/Rosettea/Hilbish/compare/v2.2.3...v2.3.0
|
||||||
|
[2.2.3]: https://github.com/Rosettea/Hilbish/compare/v2.2.2...v2.2.3
|
||||||
[2.2.2]: https://github.com/Rosettea/Hilbish/compare/v2.2.1...v2.2.2
|
[2.2.2]: https://github.com/Rosettea/Hilbish/compare/v2.2.1...v2.2.2
|
||||||
[2.2.1]: https://github.com/Rosettea/Hilbish/compare/v2.2.0...v2.2.1
|
[2.2.1]: https://github.com/Rosettea/Hilbish/compare/v2.2.0...v2.2.1
|
||||||
[2.2.0]: https://github.com/Rosettea/Hilbish/compare/v2.1.0...v2.2.0
|
[2.2.0]: https://github.com/Rosettea/Hilbish/compare/v2.1.0...v2.2.0
|
||||||
|
|
23
README.md
23
README.md
|
@ -1,3 +1,6 @@
|
||||||
|
> [!TIP]
|
||||||
|
> Check out [Hilbish: Midnight Edition](https://github.com/Rosettea/Hilbish/tree/midnight-edition) if you want to use C Lua, LuaJIT or anything related!
|
||||||
|
|
||||||
<img src="./assets/hilbish-logo-and-text.png" width=512><br>
|
<img src="./assets/hilbish-logo-and-text.png" width=512><br>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
🌓 The Moon-powered shell! A comfy and extensible shell for Lua fans! 🌺 ✨
|
🌓 The Moon-powered shell! A comfy and extensible shell for Lua fans! 🌺 ✨
|
||||||
|
@ -10,19 +13,23 @@
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
Hilbish is an extensible shell designed to be highly customizable.
|
Hilbish is an extensible shell designed to be highly customizable.
|
||||||
It is configured in Lua and provides a good range of features.
|
|
||||||
It aims to be easy to use for anyone but powerful enough for
|
It is configured in Lua, and provides a good range of features.
|
||||||
those who need it.
|
It aims to be easy to use for anyone, and powerful enough for
|
||||||
|
those who need more.
|
||||||
|
|
||||||
The motivation for choosing Lua was that its simpler and better to use
|
The motivation for choosing Lua was that its simpler and better to use
|
||||||
than old shell script. It's fine for basic interactive shell uses,
|
than old shell scripts. It's fine for basic interactive shell uses,
|
||||||
but that's the only place Hilbish has shell script; everything else is Lua
|
and supports [both Lua and Sh interactively](https://rosettea.github.io/Hilbish/docs/features/runner-mode/).
|
||||||
and aims to be infinitely configurable. If something isn't, open an issue!
|
|
||||||
|
That's the only place Hilbish can use traditional shell syntax though;
|
||||||
|
everything else is Lua and aims to be infinitely configurable.
|
||||||
|
|
||||||
|
If something isn't, open an issue!
|
||||||
|
|
||||||
# Screenshots
|
# Screenshots
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="gallery/tab.png">
|
<img src="gallery/tab.png">
|
||||||
<img src="gallery/pillprompt.png">
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
# Getting Hilbish
|
# Getting Hilbish
|
||||||
|
@ -36,7 +43,7 @@ on the website for distributed binaries from GitHub or other package repositorie
|
||||||
Otherwise, continue reading for steps on compiling.
|
Otherwise, continue reading for steps on compiling.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
- [Go 1.17+](https://go.dev)
|
- [Go 1.22+](https://go.dev)
|
||||||
- [Task](https://taskfile.dev/installation/) (**Go on the hyperlink here to see Task's install method for your OS.**)
|
- [Task](https://taskfile.dev/installation/) (**Go on the hyperlink here to see Task's install method for your OS.**)
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
129
api.go
129
api.go
|
@ -16,6 +16,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -27,6 +28,7 @@ import (
|
||||||
|
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
"github.com/arnodel/golua/lib/packagelib"
|
"github.com/arnodel/golua/lib/packagelib"
|
||||||
|
"github.com/arnodel/golua/lib/iolib"
|
||||||
"github.com/maxlandon/readline"
|
"github.com/maxlandon/readline"
|
||||||
"mvdan.cc/sh/v3/interp"
|
"mvdan.cc/sh/v3/interp"
|
||||||
)
|
)
|
||||||
|
@ -152,12 +154,64 @@ func unsetVimMode() {
|
||||||
util.SetField(l, hshMod, "vimMode", rt.NilValue)
|
util.SetField(l, hshMod, "vimMode", rt.NilValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// run(cmd, returnOut) -> exitCode (number), stdout (string), stderr (string)
|
func handleStream(v rt.Value, strms *streams, errStream bool) error {
|
||||||
|
ud, ok := v.TryUserData()
|
||||||
|
if !ok {
|
||||||
|
return errors.New("expected metatable argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
val := ud.Value()
|
||||||
|
var varstrm io.Writer
|
||||||
|
if f, ok := val.(*iolib.File); ok {
|
||||||
|
varstrm = f.Handle()
|
||||||
|
}
|
||||||
|
|
||||||
|
if f, ok := val.(*sink); ok {
|
||||||
|
varstrm = f.writer
|
||||||
|
}
|
||||||
|
|
||||||
|
if varstrm == nil {
|
||||||
|
return errors.New("expected either a sink or file")
|
||||||
|
}
|
||||||
|
|
||||||
|
if errStream {
|
||||||
|
strms.stderr = varstrm
|
||||||
|
} else {
|
||||||
|
strms.stdout = varstrm
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// run(cmd, streams) -> exitCode (number), stdout (string), stderr (string)
|
||||||
// Runs `cmd` in Hilbish's shell script interpreter.
|
// Runs `cmd` in Hilbish's shell script interpreter.
|
||||||
|
// The `streams` parameter specifies the output and input streams the command should use.
|
||||||
|
// For example, to write command output to a sink.
|
||||||
|
// As a table, the caller can directly specify the standard output, error, and input
|
||||||
|
// streams of the command with the table keys `out`, `err`, and `input` respectively.
|
||||||
|
// As a boolean, it specifies whether the command should use standard output or return its output streams.
|
||||||
// #param cmd string
|
// #param cmd string
|
||||||
// #param returnOut boolean If this is true, the function will return the standard output and error of the command instead of printing it.
|
// #param streams table|boolean
|
||||||
// #returns number, string, string
|
// #returns number, string, string
|
||||||
|
// #example
|
||||||
|
/*
|
||||||
|
// This code is the same as `ls -l | wc -l`
|
||||||
|
local fs = require 'fs'
|
||||||
|
local pr, pw = fs.pipe()
|
||||||
|
hilbish.run('ls -l', {
|
||||||
|
stdout = pw,
|
||||||
|
stderr = pw,
|
||||||
|
})
|
||||||
|
|
||||||
|
pw:close()
|
||||||
|
|
||||||
|
hilbish.run('wc -l', {
|
||||||
|
stdin = pr
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
// #example
|
||||||
func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
// TODO: ON BREAKING RELEASE, DO NOT ACCEPT `streams` AS A BOOLEAN.
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -166,20 +220,57 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strms := &streams{}
|
||||||
var terminalOut bool
|
var terminalOut bool
|
||||||
if len(c.Etc()) != 0 {
|
if len(c.Etc()) != 0 {
|
||||||
tout := c.Etc()[0]
|
tout := c.Etc()[0]
|
||||||
termOut, ok := tout.TryBool()
|
|
||||||
terminalOut = termOut
|
var ok bool
|
||||||
|
terminalOut, ok = tout.TryBool()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("bad argument to run (expected boolean, got " + tout.TypeName() + ")")
|
luastreams, ok := tout.TryTable()
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("bad argument to run (expected boolean or table, got " + tout.TypeName() + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
handleStream(luastreams.Get(rt.StringValue("out")), strms, false)
|
||||||
|
handleStream(luastreams.Get(rt.StringValue("err")), strms, true)
|
||||||
|
|
||||||
|
stdinstrm := luastreams.Get(rt.StringValue("input"))
|
||||||
|
if !stdinstrm.IsNil() {
|
||||||
|
ud, ok := stdinstrm.TryUserData()
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("bad type as run stdin stream (expected userdata as either sink or file, got " + stdinstrm.TypeName() + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
val := ud.Value()
|
||||||
|
var varstrm io.Reader
|
||||||
|
if f, ok := val.(*iolib.File); ok {
|
||||||
|
varstrm = f.Handle()
|
||||||
|
}
|
||||||
|
|
||||||
|
if f, ok := val.(*sink); ok {
|
||||||
|
varstrm = f.reader
|
||||||
|
}
|
||||||
|
|
||||||
|
if varstrm == nil {
|
||||||
|
return nil, errors.New("bad type as run stdin stream (expected userdata as either sink or file)")
|
||||||
|
}
|
||||||
|
|
||||||
|
strms.stdin = varstrm
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
terminalOut = true
|
if !terminalOut {
|
||||||
|
strms = &streams{
|
||||||
|
stdout: new(bytes.Buffer),
|
||||||
|
stderr: new(bytes.Buffer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var exitcode uint8
|
var exitcode uint8
|
||||||
stdout, stderr, err := execCommand(cmd, terminalOut)
|
stdout, stderr, err := execCommand(cmd, strms)
|
||||||
|
|
||||||
if code, ok := interp.IsExitStatus(err); ok {
|
if code, ok := interp.IsExitStatus(err); ok {
|
||||||
exitcode = code
|
exitcode = code
|
||||||
|
@ -187,11 +278,12 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
exitcode = 1
|
exitcode = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
stdoutStr := ""
|
var stdoutStr, stderrStr string
|
||||||
stderrStr := ""
|
if stdoutBuf, ok := stdout.(*bytes.Buffer); ok {
|
||||||
if !terminalOut {
|
stdoutStr = stdoutBuf.String()
|
||||||
stdoutStr = stdout.(*bytes.Buffer).String()
|
}
|
||||||
stderrStr = stderr.(*bytes.Buffer).String()
|
if stderrBuf, ok := stderr.(*bytes.Buffer); ok {
|
||||||
|
stderrStr = stderrBuf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitcode)), rt.StringValue(stdoutStr), rt.StringValue(stderrStr)), nil
|
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitcode)), rt.StringValue(stdoutStr), rt.StringValue(stderrStr)), nil
|
||||||
|
@ -609,7 +701,7 @@ func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
cmd := strings.Split(alias, " ")[0]
|
cmd := strings.Split(alias, " ")[0]
|
||||||
|
|
||||||
// check for commander
|
// check for commander
|
||||||
if commands[cmd] != nil {
|
if cmds.Commands[cmd] != nil {
|
||||||
// they dont resolve to a path, so just send the cmd
|
// they dont resolve to a path, so just send the cmd
|
||||||
return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
|
return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
|
||||||
}
|
}
|
||||||
|
@ -712,5 +804,14 @@ func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
// #example
|
// #example
|
||||||
// #param line string
|
// #param line string
|
||||||
func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
line, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.PushingNext1(t.Runtime, rt.StringValue(line)), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -488,8 +488,12 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
mdTable.SetContent(i - diff, 0, fmt.Sprintf(`<a href="#%s">%s</a>`, dps.FuncName, dps.FuncSig))
|
mdTable.SetContent(i - diff, 0, fmt.Sprintf(`<a href="#%s">%s</a>`, dps.FuncName, dps.FuncSig))
|
||||||
|
if len(dps.Doc) == 0 {
|
||||||
|
fmt.Printf("WARNING! Function %s on module %s has no documentation!\n", dps.FuncName, modname)
|
||||||
|
} else {
|
||||||
mdTable.SetContent(i - diff, 1, dps.Doc[0])
|
mdTable.SetContent(i - diff, 1, dps.Doc[0])
|
||||||
}
|
}
|
||||||
|
}
|
||||||
f.WriteString(mdTable.String())
|
f.WriteString(mdTable.String())
|
||||||
f.WriteString("\n")
|
f.WriteString("\n")
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add lua registered commands to completions
|
// add lua registered commands to completions
|
||||||
for cmdName := range commands {
|
for cmdName := range cmds.Commands {
|
||||||
if strings.HasPrefix(cmdName, query) {
|
if strings.HasPrefix(cmdName, query) {
|
||||||
completions = append(completions, cmdName)
|
completions = append(completions, cmdName)
|
||||||
}
|
}
|
||||||
|
@ -157,9 +157,12 @@ func matchPath(query string) ([]string, string) {
|
||||||
|
|
||||||
files, _ := os.ReadDir(path)
|
files, _ := os.ReadDir(path)
|
||||||
for _, entry := range files {
|
for _, entry := range files {
|
||||||
// should we handle errors here?
|
|
||||||
file, err := entry.Info()
|
file, err := entry.Info()
|
||||||
if err == nil && file.Mode() & os.ModeSymlink != 0 {
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if file.Mode() & os.ModeSymlink != 0 {
|
||||||
path, err := filepath.EvalSymlinks(filepath.Join(path, file.Name()))
|
path, err := filepath.EvalSymlinks(filepath.Join(path, file.Name()))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
file, err = os.Lstat(path)
|
file, err = os.Lstat(path)
|
||||||
|
|
|
@ -26,8 +26,11 @@ In this example, a command with the name of `hello` is created
|
||||||
that will print `Hello world!` to output. One question you may
|
that will print `Hello world!` to output. One question you may
|
||||||
have is: What is the `sinks` parameter?
|
have is: What is the `sinks` parameter?
|
||||||
|
|
||||||
The `sinks` parameter is a table with 3 keys: `in`, `out`,
|
The `sinks` parameter is a table with 3 keys: `input`, `out`, and `err`.
|
||||||
and `err`. All of them are a <a href="/Hilbish/docs/api/hilbish/#sink" style="text-decoration: none;">Sink</a>.
|
There is an `in` alias to `input`, but it requires using the string accessor syntax (`sinks['in']`)
|
||||||
|
as `in` is also a Lua keyword, so `input` is preferred for use.
|
||||||
|
All of them are a <a href="/Hilbish/docs/api/hilbish/#sink" style="text-decoration: none;">Sink</a>.
|
||||||
|
In the future, `sinks.in` will be removed.
|
||||||
|
|
||||||
- `in` is the standard input.
|
- `in` is the standard input.
|
||||||
You may use the read functions on this sink to get input from the user.
|
You may use the read functions on this sink to get input from the user.
|
||||||
|
@ -41,6 +44,7 @@ This sink is for writing errors, as the name would suggest.
|
||||||
|----|----|
|
|----|----|
|
||||||
|<a href="#deregister">deregister(name)</a>|Removes the named command. Note that this will only remove Commander-registered commands.|
|
|<a href="#deregister">deregister(name)</a>|Removes the named command. Note that this will only remove Commander-registered commands.|
|
||||||
|<a href="#register">register(name, cb)</a>|Adds a new command with the given `name`. When Hilbish has to run a command with a name,|
|
|<a href="#register">register(name, cb)</a>|Adds a new command with the given `name`. When Hilbish has to run a command with a name,|
|
||||||
|
|<a href="#registry">registry() -> table</a>|Returns all registered commanders. Returns a list of tables with the following keys:|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<div id='deregister'>
|
<div id='deregister'>
|
||||||
|
@ -91,3 +95,19 @@ end)
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='registry'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
commander.registry() -> table
|
||||||
|
<a href="#registry" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns all registered commanders. Returns a list of tables with the following keys:
|
||||||
|
- `exec`: The function used to run the commander. Commanders require args and sinks to be passed.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ library offers more functions and will work on any operating system Hilbish does
|
||||||
|<a href="#glob">glob(pattern) -> matches (table)</a>|Match all files based on the provided `pattern`.|
|
|<a href="#glob">glob(pattern) -> matches (table)</a>|Match all files based on the provided `pattern`.|
|
||||||
|<a href="#join">join(...path) -> string</a>|Takes any list of paths and joins them based on the operating system's path separator.|
|
|<a href="#join">join(...path) -> string</a>|Takes any list of paths and joins them based on the operating system's path separator.|
|
||||||
|<a href="#mkdir">mkdir(name, recursive)</a>|Creates a new directory with the provided `name`.|
|
|<a href="#mkdir">mkdir(name, recursive)</a>|Creates a new directory with the provided `name`.|
|
||||||
|
|<a href="#pipe">fpipe() -> File, File</a>|Returns a pair of connected files, also known as a pipe.|
|
||||||
|<a href="#readdir">readdir(path) -> table[string]</a>|Returns a list of all files and directories in the provided path.|
|
|<a href="#readdir">readdir(path) -> table[string]</a>|Returns a list of all files and directories in the provided path.|
|
||||||
|<a href="#stat">stat(path) -> {}</a>|Returns the information about a given `path`.|
|
|<a href="#stat">stat(path) -> {}</a>|Returns the information about a given `path`.|
|
||||||
|
|
||||||
|
@ -183,6 +184,22 @@ fs.mkdir('./foo/bar', true)
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div id='pipe'>
|
||||||
|
<h4 class='heading'>
|
||||||
|
fs.fpipe() -> File, File
|
||||||
|
<a href="#pipe" class='heading-link'>
|
||||||
|
<i class="fas fa-paperclip"></i>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
Returns a pair of connected files, also known as a pipe.
|
||||||
|
The type returned is a Lua file, same as returned from `io` functions.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
This function has no parameters.
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<div id='readdir'>
|
<div id='readdir'>
|
||||||
<h4 class='heading'>
|
<h4 class='heading'>
|
||||||
|
|
|
@ -28,7 +28,7 @@ interfaces and functions which directly relate to shell functionality.
|
||||||
|<a href="#prependPath">prependPath(dir)</a>|Prepends `dir` to $PATH.|
|
|<a href="#prependPath">prependPath(dir)</a>|Prepends `dir` to $PATH.|
|
||||||
|<a href="#prompt">prompt(str, typ)</a>|Changes the shell prompt to the provided string.|
|
|<a href="#prompt">prompt(str, typ)</a>|Changes the shell prompt to the provided string.|
|
||||||
|<a href="#read">read(prompt) -> input (string)</a>|Read input from the user, using Hilbish's line editor/input reader.|
|
|<a href="#read">read(prompt) -> input (string)</a>|Read input from the user, using Hilbish's line editor/input reader.|
|
||||||
|<a href="#run">run(cmd, returnOut) -> exitCode (number), stdout (string), stderr (string)</a>|Runs `cmd` in Hilbish's shell script interpreter.|
|
|<a href="#run">run(cmd, streams) -> exitCode (number), stdout (string), stderr (string)</a>|Runs `cmd` in Hilbish's shell script interpreter.|
|
||||||
|<a href="#runnerMode">runnerMode(mode)</a>|Sets the execution/runner mode for interactive Hilbish.|
|
|<a href="#runnerMode">runnerMode(mode)</a>|Sets the execution/runner mode for interactive Hilbish.|
|
||||||
|<a href="#timeout">timeout(cb, time) -> @Timer</a>|Executed the `cb` function after a period of `time`.|
|
|<a href="#timeout">timeout(cb, time) -> @Timer</a>|Executed the `cb` function after a period of `time`.|
|
||||||
|<a href="#which">which(name) -> string</a>|Checks if `name` is a valid command.|
|
|<a href="#which">which(name) -> string</a>|Checks if `name` is a valid command.|
|
||||||
|
@ -413,21 +413,44 @@ Text to print before input, can be empty.
|
||||||
<hr>
|
<hr>
|
||||||
<div id='run'>
|
<div id='run'>
|
||||||
<h4 class='heading'>
|
<h4 class='heading'>
|
||||||
hilbish.run(cmd, returnOut) -> exitCode (number), stdout (string), stderr (string)
|
hilbish.run(cmd, streams) -> exitCode (number), stdout (string), stderr (string)
|
||||||
<a href="#run" class='heading-link'>
|
<a href="#run" class='heading-link'>
|
||||||
<i class="fas fa-paperclip"></i>
|
<i class="fas fa-paperclip"></i>
|
||||||
</a>
|
</a>
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
Runs `cmd` in Hilbish's shell script interpreter.
|
Runs `cmd` in Hilbish's shell script interpreter.
|
||||||
|
The `streams` parameter specifies the output and input streams the command should use.
|
||||||
|
For example, to write command output to a sink.
|
||||||
|
As a table, the caller can directly specify the standard output, error, and input
|
||||||
|
streams of the command with the table keys `out`, `err`, and `input` respectively.
|
||||||
|
As a boolean, it specifies whether the command should use standard output or return its output streams.
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
`string` **`cmd`**
|
`string` **`cmd`**
|
||||||
|
|
||||||
|
|
||||||
`boolean` **`returnOut`**
|
`table|boolean` **`streams`**
|
||||||
If this is true, the function will return the standard output and error of the command instead of printing it.
|
|
||||||
|
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
|
||||||
|
// This code is the same as `ls -l | wc -l`
|
||||||
|
local fs = require 'fs'
|
||||||
|
local pr, pw = fs.pipe()
|
||||||
|
hilbish.run('ls -l', {
|
||||||
|
stdout = pw,
|
||||||
|
stderr = pw,
|
||||||
|
})
|
||||||
|
|
||||||
|
pw:close()
|
||||||
|
|
||||||
|
hilbish.run('wc -l', {
|
||||||
|
stdin = pr
|
||||||
|
})
|
||||||
|
|
||||||
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
|
@ -21,16 +21,18 @@ A runner is passed the input and has to return a table with these values.
|
||||||
All are not required, only the useful ones the runner needs to return.
|
All are not required, only the useful ones the runner needs to return.
|
||||||
(So if there isn't an error, just omit `err`.)
|
(So if there isn't an error, just omit `err`.)
|
||||||
|
|
||||||
- `exitCode` (number): A numerical code to indicate the exit result.
|
- `exitCode` (number): Exit code of the command
|
||||||
- `input` (string): The user input. This will be used to add
|
- `input` (string): The text input of the user. This is used by Hilbish to append extra input, in case
|
||||||
to the history.
|
more is requested.
|
||||||
- `err` (string): A string to indicate an interal error for the runner.
|
- `err` (string): A string that represents an error from the runner.
|
||||||
It can be set to a few special values for Hilbish to throw the right hooks and have a better looking message:
|
This should only be set when, for example, there is a syntax error.
|
||||||
|
It can be set to a few special values for Hilbish to throw the right
|
||||||
`[command]: not-found` will throw a command.not-found hook based on what `[command]` is.
|
hooks and have a better looking message.
|
||||||
|
- `\<command>: not-found` will throw a `command.not-found` hook
|
||||||
`[command]: not-executable` will throw a command.not-executable hook.
|
based on what `\<command>` is.
|
||||||
- `continue` (boolean): Whether to prompt the user for more input.
|
- `\<command>: not-executable` will throw a `command.not-executable` hook.
|
||||||
|
- `continue` (boolean): Whether Hilbish should prompt the user for no input
|
||||||
|
- `newline` (boolean): Whether a newline should be added at the end of `input`.
|
||||||
|
|
||||||
Here is a simple example of a fennel runner. It falls back to
|
Here is a simple example of a fennel runner. It falls back to
|
||||||
shell script if fennel eval has an error.
|
shell script if fennel eval has an error.
|
||||||
|
|
|
@ -33,19 +33,6 @@ needs to run interactive input. For more detail, see the [API documentation](../
|
||||||
The `hilbish.runner` interface is an alternative to using `hilbish.runnerMode`
|
The `hilbish.runner` interface is an alternative to using `hilbish.runnerMode`
|
||||||
and also provides the shell script and Lua runner functions that Hilbish itself uses.
|
and also provides the shell script and Lua runner functions that Hilbish itself uses.
|
||||||
|
|
||||||
A runner function is expected to return a table with the following values:
|
|
||||||
- `exitCode` (number): Exit code of the command
|
|
||||||
- `input` (string): The text input of the user. This is used by Hilbish to append extra input, in case
|
|
||||||
more is requested.
|
|
||||||
- `err` (string): A string that represents an error from the runner.
|
|
||||||
This should only be set when, for example, there is a syntax error.
|
|
||||||
It can be set to a few special values for Hilbish to throw the right
|
|
||||||
hooks and have a better looking message.
|
|
||||||
- `<command>: not-found` will throw a `command.not-found` hook
|
|
||||||
based on what `<command>` is.
|
|
||||||
- `<command>: not-executable` will throw a `command.not-executable` hook.
|
|
||||||
- `continue` (boolean): Whether Hilbish should prompt the user for no input
|
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
These are the "low level" functions for the `hilbish.runner` interface.
|
These are the "low level" functions for the `hilbish.runner` interface.
|
||||||
|
|
||||||
|
|
|
@ -11,4 +11,8 @@ function commander.deregister(name) end
|
||||||
---
|
---
|
||||||
function commander.register(name, cb) end
|
function commander.register(name, cb) end
|
||||||
|
|
||||||
|
--- Returns all registered commanders. Returns a list of tables with the following keys:
|
||||||
|
--- - `exec`: The function used to run the commander. Commanders require args and sinks to be passed.
|
||||||
|
function commander.registry() end
|
||||||
|
|
||||||
return commander
|
return commander
|
||||||
|
|
|
@ -34,6 +34,10 @@ function fs.join(...path) end
|
||||||
---
|
---
|
||||||
function fs.mkdir(name, recursive) end
|
function fs.mkdir(name, recursive) end
|
||||||
|
|
||||||
|
--- Returns a pair of connected files, also known as a pipe.
|
||||||
|
--- The type returned is a Lua file, same as returned from `io` functions.
|
||||||
|
function fs.fpipe() end
|
||||||
|
|
||||||
--- Returns a list of all files and directories in the provided path.
|
--- Returns a list of all files and directories in the provided path.
|
||||||
function fs.readdir(path) end
|
function fs.readdir(path) end
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,13 @@ function hilbish.prompt(str, typ) end
|
||||||
function hilbish.read(prompt) end
|
function hilbish.read(prompt) end
|
||||||
|
|
||||||
--- Runs `cmd` in Hilbish's shell script interpreter.
|
--- Runs `cmd` in Hilbish's shell script interpreter.
|
||||||
function hilbish.run(cmd, returnOut) end
|
--- The `streams` parameter specifies the output and input streams the command should use.
|
||||||
|
--- For example, to write command output to a sink.
|
||||||
|
--- As a table, the caller can directly specify the standard output, error, and input
|
||||||
|
--- streams of the command with the table keys `out`, `err`, and `input` respectively.
|
||||||
|
--- As a boolean, it specifies whether the command should use standard output or return its output streams.
|
||||||
|
---
|
||||||
|
function hilbish.run(cmd, streams) end
|
||||||
|
|
||||||
--- Sets the execution/runner mode for interactive Hilbish.
|
--- Sets the execution/runner mode for interactive Hilbish.
|
||||||
--- This determines whether Hilbish wll try to run input as Lua
|
--- This determines whether Hilbish wll try to run input as Lua
|
||||||
|
|
157
exec.go
157
exec.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -28,6 +29,12 @@ var errNotExec = errors.New("not executable")
|
||||||
var errNotFound = errors.New("not found")
|
var errNotFound = errors.New("not found")
|
||||||
var runnerMode rt.Value = rt.StringValue("hybrid")
|
var runnerMode rt.Value = rt.StringValue("hybrid")
|
||||||
|
|
||||||
|
type streams struct {
|
||||||
|
stdout io.Writer
|
||||||
|
stderr io.Writer
|
||||||
|
stdin io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
type execError struct{
|
type execError struct{
|
||||||
typ string
|
typ string
|
||||||
cmd string
|
cmd string
|
||||||
|
@ -91,6 +98,7 @@ func runInput(input string, priv bool) {
|
||||||
var exitCode uint8
|
var exitCode uint8
|
||||||
var err error
|
var err error
|
||||||
var cont bool
|
var cont bool
|
||||||
|
var newline bool
|
||||||
// save incase it changes while prompting (For some reason)
|
// save incase it changes while prompting (For some reason)
|
||||||
currentRunner := runnerMode
|
currentRunner := runnerMode
|
||||||
if currentRunner.Type() == rt.StringType {
|
if currentRunner.Type() == rt.StringType {
|
||||||
|
@ -101,9 +109,9 @@ func runInput(input string, priv bool) {
|
||||||
cmdFinish(0, input, priv)
|
cmdFinish(0, input, priv)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
input, exitCode, cont, err = handleSh(input)
|
input, exitCode, cont, newline, err = handleSh(input)
|
||||||
case "hybridRev":
|
case "hybridRev":
|
||||||
_, _, _, err = handleSh(input)
|
_, _, _, _, err = handleSh(input)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cmdFinish(0, input, priv)
|
cmdFinish(0, input, priv)
|
||||||
return
|
return
|
||||||
|
@ -112,12 +120,12 @@ func runInput(input string, priv bool) {
|
||||||
case "lua":
|
case "lua":
|
||||||
input, exitCode, err = handleLua(input)
|
input, exitCode, err = handleLua(input)
|
||||||
case "sh":
|
case "sh":
|
||||||
input, exitCode, cont, err = handleSh(input)
|
input, exitCode, cont, newline, err = handleSh(input)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// can only be a string or function so
|
// can only be a string or function so
|
||||||
var runnerErr error
|
var runnerErr error
|
||||||
input, exitCode, cont, runnerErr, err = runLuaRunner(currentRunner, input)
|
input, exitCode, cont, newline, runnerErr, err = runLuaRunner(currentRunner, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
cmdFinish(124, input, priv)
|
cmdFinish(124, input, priv)
|
||||||
|
@ -130,15 +138,15 @@ func runInput(input string, priv bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if cont {
|
if cont {
|
||||||
input, err = reprompt(input)
|
input, err = continuePrompt(input, newline)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
goto rerun
|
goto rerun
|
||||||
} else if err == io.EOF {
|
} else if err == io.EOF {
|
||||||
return
|
lr.SetPrompt(fmtPrompt(prompt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil && err != io.EOF {
|
||||||
if exErr, ok := isExecError(err); ok {
|
if exErr, ok := isExecError(err); ok {
|
||||||
hooks.Emit("command." + exErr.typ, exErr.cmd)
|
hooks.Emit("command." + exErr.typ, exErr.cmd)
|
||||||
} else {
|
} else {
|
||||||
|
@ -148,26 +156,28 @@ func runInput(input string, priv bool) {
|
||||||
cmdFinish(exitCode, input, priv)
|
cmdFinish(exitCode, input, priv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reprompt(input string) (string, error) {
|
func reprompt(input string, newline bool) (string, error) {
|
||||||
for {
|
for {
|
||||||
in, err := continuePrompt(strings.TrimSuffix(input, "\\"))
|
/*
|
||||||
|
if strings.HasSuffix(input, "\\") {
|
||||||
|
input = strings.TrimSuffix(input, "\\") + "\n"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
in, err := continuePrompt(input, newline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lr.SetPrompt(fmtPrompt(prompt))
|
lr.SetPrompt(fmtPrompt(prompt))
|
||||||
return input, err
|
return input, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasSuffix(in, "\\") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLuaRunner(runr rt.Value, userInput string) (input string, exitCode uint8, continued bool, runnerErr, err error) {
|
func runLuaRunner(runr rt.Value, userInput string) (input string, exitCode uint8, continued bool, newline bool, runnerErr, err error) {
|
||||||
term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 3, false)
|
term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 3, false)
|
||||||
err = rt.Call(l.MainThread(), runr, []rt.Value{rt.StringValue(userInput)}, term)
|
err = rt.Call(l.MainThread(), runr, []rt.Value{rt.StringValue(userInput)}, term)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 124, false, nil, err
|
return "", 124, false, false, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var runner *rt.Table
|
var runner *rt.Table
|
||||||
|
@ -195,6 +205,10 @@ func runLuaRunner(runr rt.Value, userInput string) (input string, exitCode uint8
|
||||||
if c, ok := runner.Get(rt.StringValue("continue")).TryBool(); ok {
|
if c, ok := runner.Get(rt.StringValue("continue")).TryBool(); ok {
|
||||||
continued = c
|
continued = c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if nl, ok := runner.Get(rt.StringValue("newline")).TryBool(); ok {
|
||||||
|
newline = nl
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,55 +239,68 @@ func handleLua(input 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) (input string, exitCode uint8, cont bool, newline bool, runErr error) {
|
||||||
shRunner := hshMod.Get(rt.StringValue("runner")).AsTable().Get(rt.StringValue("sh"))
|
shRunner := hshMod.Get(rt.StringValue("runner")).AsTable().Get(rt.StringValue("sh"))
|
||||||
var err error
|
var err error
|
||||||
input, exitCode, cont, runErr, err = runLuaRunner(shRunner, cmdString)
|
input, exitCode, cont, newline, runErr, err = runLuaRunner(shRunner, cmdString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runErr = err
|
runErr = err
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func execSh(cmdString string) (string, uint8, bool, error) {
|
func execSh(cmdString string) (input string, exitcode uint8, cont bool, newline bool, e error) {
|
||||||
_, _, err := execCommand(cmdString, true)
|
_, _, err := execCommand(cmdString, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If input is incomplete, start multiline prompting
|
// If input is incomplete, start multiline prompting
|
||||||
if syntax.IsIncomplete(err) {
|
if syntax.IsIncomplete(err) {
|
||||||
if !interactive {
|
if !interactive {
|
||||||
return cmdString, 126, false, err
|
return cmdString, 126, false, false, err
|
||||||
}
|
}
|
||||||
return cmdString, 126, true, err
|
|
||||||
|
newline := false
|
||||||
|
if strings.Contains(err.Error(), "unclosed here-document") {
|
||||||
|
newline = true
|
||||||
|
}
|
||||||
|
return cmdString, 126, true, newline, err
|
||||||
} else {
|
} else {
|
||||||
if code, ok := interp.IsExitStatus(err); ok {
|
if code, ok := interp.IsExitStatus(err); ok {
|
||||||
return cmdString, code, false, nil
|
return cmdString, code, false, false, nil
|
||||||
} else {
|
} else {
|
||||||
return cmdString, 126, false, err
|
return cmdString, 126, false, false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmdString, 0, false, nil
|
return cmdString, 0, false, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run command in sh interpreter
|
// Run command in sh interpreter
|
||||||
func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
|
func execCommand(cmd string, strms *streams) (io.Writer, io.Writer, error) {
|
||||||
file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "")
|
file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
runner, _ := interp.New()
|
if strms == nil {
|
||||||
|
strms = &streams{}
|
||||||
var stdout io.Writer
|
|
||||||
var stderr io.Writer
|
|
||||||
if terminalOut {
|
|
||||||
interp.StdIO(os.Stdin, os.Stdout, os.Stderr)(runner)
|
|
||||||
} else {
|
|
||||||
stdout = new(bytes.Buffer)
|
|
||||||
stderr = new(bytes.Buffer)
|
|
||||||
interp.StdIO(os.Stdin, stdout, stderr)(runner)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strms.stdout == nil {
|
||||||
|
strms.stdout = os.Stdout
|
||||||
|
}
|
||||||
|
|
||||||
|
if strms.stderr == nil {
|
||||||
|
strms.stderr = os.Stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
if strms.stdin == nil {
|
||||||
|
strms.stdin = os.Stdin
|
||||||
|
}
|
||||||
|
|
||||||
|
interp.StdIO(strms.stdin, strms.stdout, strms.stderr)(runner)
|
||||||
|
interp.Env(nil)(runner)
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
printer := syntax.NewPrinter()
|
printer := syntax.NewPrinter()
|
||||||
|
|
||||||
|
@ -292,11 +319,11 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
|
||||||
interp.ExecHandler(execHandle(bg))(runner)
|
interp.ExecHandler(execHandle(bg))(runner)
|
||||||
err = runner.Run(context.TODO(), stmt)
|
err = runner.Run(context.TODO(), stmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return stdout, stderr, err
|
return strms.stdout, strms.stderr, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return stdout, stderr, nil
|
return strms.stdout, strms.stderr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func execHandle(bg bool) interp.ExecHandlerFunc {
|
func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
|
@ -327,17 +354,45 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
hc := interp.HandlerCtx(ctx)
|
hc := interp.HandlerCtx(ctx)
|
||||||
if commands[args[0]] != nil {
|
if cmd := cmds.Commands[args[0]]; cmd != nil {
|
||||||
stdin := newSinkInput(hc.Stdin)
|
stdin := newSinkInput(hc.Stdin)
|
||||||
stdout := newSinkOutput(hc.Stdout)
|
stdout := newSinkOutput(hc.Stdout)
|
||||||
stderr := newSinkOutput(hc.Stderr)
|
stderr := newSinkOutput(hc.Stderr)
|
||||||
|
|
||||||
sinks := rt.NewTable()
|
sinks := rt.NewTable()
|
||||||
sinks.Set(rt.StringValue("in"), rt.UserDataValue(stdin.ud))
|
sinks.Set(rt.StringValue("in"), rt.UserDataValue(stdin.ud))
|
||||||
|
sinks.Set(rt.StringValue("input"), rt.UserDataValue(stdin.ud))
|
||||||
sinks.Set(rt.StringValue("out"), rt.UserDataValue(stdout.ud))
|
sinks.Set(rt.StringValue("out"), rt.UserDataValue(stdout.ud))
|
||||||
sinks.Set(rt.StringValue("err"), rt.UserDataValue(stderr.ud))
|
sinks.Set(rt.StringValue("err"), rt.UserDataValue(stderr.ud))
|
||||||
|
|
||||||
luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs), rt.TableValue(sinks))
|
t := rt.NewThread(l)
|
||||||
|
sig := make(chan os.Signal)
|
||||||
|
exit := make(chan bool)
|
||||||
|
|
||||||
|
luaexitcode := rt.IntValue(63)
|
||||||
|
var err error
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
exit <- true
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
signal.Notify(sig, os.Interrupt)
|
||||||
|
select {
|
||||||
|
case <-sig:
|
||||||
|
t.KillContext()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
luaexitcode, err = rt.Call1(t, rt.FunctionValue(cmd), rt.TableValue(luacmdArgs), rt.TableValue(sinks))
|
||||||
|
exit <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-exit
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error())
|
fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error())
|
||||||
return interp.NewExitStatus(1)
|
return interp.NewExitStatus(1)
|
||||||
|
@ -349,14 +404,14 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
exitcode = uint8(code)
|
exitcode = uint8(code)
|
||||||
} else if luaexitcode != rt.NilValue {
|
} else if luaexitcode != rt.NilValue {
|
||||||
// deregister commander
|
// deregister commander
|
||||||
delete(commands, args[0])
|
delete(cmds.Commands, args[0])
|
||||||
fmt.Fprintf(os.Stderr, "Commander did not return number for exit code. %s, you're fired.\n", args[0])
|
fmt.Fprintf(os.Stderr, "Commander did not return number for exit code. %s, you're fired.\n", args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return interp.NewExitStatus(exitcode)
|
return interp.NewExitStatus(exitcode)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := lookpath(args[0])
|
path, err := lookpath(args[0])
|
||||||
if err == errNotExec {
|
if err == errNotExec {
|
||||||
return execError{
|
return execError{
|
||||||
typ: "not-executable",
|
typ: "not-executable",
|
||||||
|
@ -377,15 +432,16 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
killTimeout := 2 * time.Second
|
killTimeout := 2 * time.Second
|
||||||
// from here is basically copy-paste of the default exec handler from
|
// from here is basically copy-paste of the default exec handler from
|
||||||
// sh/interp but with our job handling
|
// sh/interp but with our job handling
|
||||||
path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0])
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(hc.Stderr, err)
|
|
||||||
return interp.NewExitStatus(127)
|
|
||||||
}
|
|
||||||
|
|
||||||
env := hc.Env
|
env := hc.Env
|
||||||
envList := make([]string, 0, 64)
|
envList := make([]string, 0, 64)
|
||||||
env.Each(func(name string, vr expand.Variable) bool {
|
env.Each(func(name string, vr expand.Variable) bool {
|
||||||
|
if name == "PATH" {
|
||||||
|
pathEnv := os.Getenv("PATH")
|
||||||
|
envList = append(envList, "PATH="+pathEnv)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
if !vr.IsSet() {
|
if !vr.IsSet() {
|
||||||
// If a variable is set globally but unset in the
|
// If a variable is set globally but unset in the
|
||||||
// runner, we need to ensure it's not part of the final
|
// runner, we need to ensure it's not part of the final
|
||||||
|
@ -403,6 +459,7 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
cmd := exec.Cmd{
|
cmd := exec.Cmd{
|
||||||
Path: path,
|
Path: path,
|
||||||
Args: args,
|
Args: args,
|
||||||
|
@ -485,7 +542,7 @@ func handleExecErr(err error) (exit uint8) {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func lookpath(file string) error { // custom lookpath function so we know if a command is found *and* is executable
|
func lookpath(file string) (string, error) { // custom lookpath function so we know if a command is found *and* is executable
|
||||||
var skip []string
|
var skip []string
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
skip = []string{"./", "../", "~/", "C:"}
|
skip = []string{"./", "../", "~/", "C:"}
|
||||||
|
@ -494,20 +551,20 @@ func lookpath(file string) error { // custom lookpath function so we know if a c
|
||||||
}
|
}
|
||||||
for _, s := range skip {
|
for _, s := range skip {
|
||||||
if strings.HasPrefix(file, s) {
|
if strings.HasPrefix(file, s) {
|
||||||
return findExecutable(file, false, false)
|
return file, findExecutable(file, false, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, dir := range filepath.SplitList(os.Getenv("PATH")) {
|
for _, dir := range filepath.SplitList(os.Getenv("PATH")) {
|
||||||
path := filepath.Join(dir, file)
|
path := filepath.Join(dir, file)
|
||||||
err := findExecutable(path, true, false)
|
err := findExecutable(path, true, false)
|
||||||
if err == errNotExec {
|
if err == errNotExec {
|
||||||
return err
|
return "", err
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
return nil
|
return path, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.ErrNotExist
|
return "", os.ErrNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitInput(input string) ([]string, string) {
|
func splitInput(input string) ([]string, string) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build linux darwin
|
//go:build unix
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build windows
|
//go:build windows
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
31
go.mod
31
go.mod
|
@ -1,35 +1,38 @@
|
||||||
module hilbish
|
module hilbish
|
||||||
|
|
||||||
go 1.18
|
go 1.21
|
||||||
|
|
||||||
|
toolchain go1.22.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/arnodel/golua v0.0.0-20220221163911-dfcf252b6f86
|
github.com/arnodel/golua v0.0.0-20230215163904-e0b5347eaaa1
|
||||||
github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504
|
github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504
|
||||||
github.com/blackfireio/osinfo v1.0.3
|
github.com/blackfireio/osinfo v1.0.5
|
||||||
github.com/maxlandon/readline v0.1.0-beta.0.20211027085530-2b76cabb8036
|
github.com/maxlandon/readline v1.0.14
|
||||||
github.com/pborman/getopt v1.1.0
|
github.com/pborman/getopt v1.1.0
|
||||||
github.com/rjeczalik/notify v0.9.3
|
github.com/rjeczalik/notify v0.9.3
|
||||||
github.com/sahilm/fuzzy v0.1.0
|
github.com/sahilm/fuzzy v0.1.1
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
|
golang.org/x/sys v0.22.0
|
||||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171
|
golang.org/x/term v0.22.0
|
||||||
mvdan.cc/sh/v3 v3.5.1
|
mvdan.cc/sh/v3 v3.0.0-00010101000000-000000000000
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
||||||
github.com/arnodel/strftime v0.1.6 // indirect
|
github.com/arnodel/strftime v0.1.6 // indirect
|
||||||
github.com/evilsocket/islazy v1.10.6 // indirect
|
github.com/evilsocket/islazy v1.11.0 // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect
|
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b
|
replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20240815163633-562273e09b73
|
||||||
|
|
||||||
replace github.com/maxlandon/readline => ./readline
|
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-20221213193027-cbf6d4e4d345
|
replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20241104031959-5551ea280f23
|
||||||
|
|
87
go.sum
87
go.sum
|
@ -1,72 +1,49 @@
|
||||||
github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345 h1:QNYjYDogUSiNUkffbhFSrSCtpZhofeiVYGFN2FI4wSs=
|
github.com/Rosettea/golua v0.0.0-20241104031959-5551ea280f23 h1:mUZnT0gmDEmTkqXsbnDbuJ3CNil7DCOMiCQYgjbKIdI=
|
||||||
github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
github.com/Rosettea/golua v0.0.0-20241104031959-5551ea280f23/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
||||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b h1:s5eDMhBk6H1BgipgLub/gv9qeyBaTuiHM0k3h2/9TSE=
|
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20240815163633-562273e09b73 h1:zTTUJqNnrF2qf4LgygN8Oae5Uxn6ewH0hA8jyTCHfXw=
|
||||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs=
|
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20240815163633-562273e09b73/go.mod h1:YZalN5H7WNQw3DGij6IvHsEhn5YMW7M2FCwG6gnfKy4=
|
||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||||
github.com/arnodel/edit v0.0.0-20220202110212-dfc8d7a13890/go.mod h1:AcpttpuZBaL9xl8/CX+Em4fBTUbwIkJ66RiAsJlNrBk=
|
|
||||||
github.com/arnodel/strftime v0.1.6 h1:0hc0pUvk8KhEMXE+htyaOUV42zNcf/csIbjzEFCJqsw=
|
github.com/arnodel/strftime v0.1.6 h1:0hc0pUvk8KhEMXE+htyaOUV42zNcf/csIbjzEFCJqsw=
|
||||||
github.com/arnodel/strftime v0.1.6/go.mod h1:5NbK5XqYK8QpRZpqKNt4OlxLtIB8cotkLk4KTKzJfWs=
|
github.com/arnodel/strftime v0.1.6/go.mod h1:5NbK5XqYK8QpRZpqKNt4OlxLtIB8cotkLk4KTKzJfWs=
|
||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
|
||||||
github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504 h1:R1/AOzdMbopSliUTTEHvHbyNmnZ3YxY5GvdhTkpPsSY=
|
github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504 h1:R1/AOzdMbopSliUTTEHvHbyNmnZ3YxY5GvdhTkpPsSY=
|
||||||
github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504/go.mod h1:kHBCvAXJIatTX1pw6tLiOspjGc3MhUDRlog9yrCUS+k=
|
github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504/go.mod h1:kHBCvAXJIatTX1pw6tLiOspjGc3MhUDRlog9yrCUS+k=
|
||||||
github.com/blackfireio/osinfo v1.0.3 h1:Yk2t2GTPjBcESv6nDSWZKO87bGMQgO+Hi9OoXPpxX8c=
|
github.com/blackfireio/osinfo v1.0.5 h1:6hlaWzfcpb87gRmznVf7wSdhysGqLRz9V/xuSdCEXrA=
|
||||||
github.com/blackfireio/osinfo v1.0.3/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA=
|
github.com/blackfireio/osinfo v1.0.5/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||||
github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc=
|
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
github.com/evilsocket/islazy v1.11.0 h1:B5w6uuS6ki6iDG+aH/RFeoMb8ijQh/pGabewqp2UeJ0=
|
||||||
github.com/evilsocket/islazy v1.10.6 h1:MFq000a1ByoumoJWlytqg0qon0KlBeUfPsDjY0hK0bo=
|
github.com/evilsocket/islazy v1.11.0/go.mod h1:muYH4x5MB5YRdkxnrOtrXLIBX6LySj1uFIqys94LKdo=
|
||||||
github.com/evilsocket/islazy v1.10.6/go.mod h1:OrwQGYg3DuZvXUfmH+KIZDjwTCbrjy48T24TUpGqVVw=
|
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||||
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw=
|
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw=
|
||||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0=
|
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0=
|
||||||
github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0=
|
github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0=
|
||||||
github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk=
|
github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
|
||||||
github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY=
|
github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY=
|
||||||
github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc=
|
github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I=
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
|
||||||
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
|
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4=
|
|
||||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||||
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8=
|
|
||||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
|
|
||||||
|
|
|
@ -17,8 +17,11 @@ In this example, a command with the name of `hello` is created
|
||||||
that will print `Hello world!` to output. One question you may
|
that will print `Hello world!` to output. One question you may
|
||||||
have is: What is the `sinks` parameter?
|
have is: What is the `sinks` parameter?
|
||||||
|
|
||||||
The `sinks` parameter is a table with 3 keys: `in`, `out`,
|
The `sinks` parameter is a table with 3 keys: `input`, `out`, and `err`.
|
||||||
and `err`. All of them are a @Sink.
|
There is an `in` alias to `input`, but it requires using the string accessor syntax (`sinks['in']`)
|
||||||
|
as `in` is also a Lua keyword, so `input` is preferred for use.
|
||||||
|
All of them are a @Sink.
|
||||||
|
In the future, `sinks.in` will be removed.
|
||||||
|
|
||||||
- `in` is the standard input.
|
- `in` is the standard input.
|
||||||
You may use the read functions on this sink to get input from the user.
|
You may use the read functions on this sink to get input from the user.
|
||||||
|
@ -40,11 +43,13 @@ import (
|
||||||
type Commander struct{
|
type Commander struct{
|
||||||
Events *bait.Bait
|
Events *bait.Bait
|
||||||
Loader packagelib.Loader
|
Loader packagelib.Loader
|
||||||
|
Commands map[string]*rt.Closure
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(rtm *rt.Runtime) Commander {
|
func New(rtm *rt.Runtime) *Commander {
|
||||||
c := Commander{
|
c := &Commander{
|
||||||
Events: bait.New(rtm),
|
Events: bait.New(rtm),
|
||||||
|
Commands: make(map[string]*rt.Closure),
|
||||||
}
|
}
|
||||||
c.Loader = packagelib.Loader{
|
c.Loader = packagelib.Loader{
|
||||||
Load: c.loaderFunc,
|
Load: c.loaderFunc,
|
||||||
|
@ -58,6 +63,7 @@ func (c *Commander) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
exports := map[string]util.LuaExport{
|
exports := map[string]util.LuaExport{
|
||||||
"register": util.LuaExport{c.cregister, 2, false},
|
"register": util.LuaExport{c.cregister, 2, false},
|
||||||
"deregister": util.LuaExport{c.cderegister, 1, false},
|
"deregister": util.LuaExport{c.cderegister, 1, false},
|
||||||
|
"registry": util.LuaExport{c.cregistry, 0, false},
|
||||||
}
|
}
|
||||||
mod := rt.NewTable()
|
mod := rt.NewTable()
|
||||||
util.SetExports(rtm, mod, exports)
|
util.SetExports(rtm, mod, exports)
|
||||||
|
@ -88,7 +94,7 @@ func (c *Commander) cregister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Events.Emit("commandRegister", cmdName, cmd)
|
c.Commands[cmdName] = cmd
|
||||||
|
|
||||||
return ct.Next(), err
|
return ct.Next(), err
|
||||||
}
|
}
|
||||||
|
@ -105,7 +111,23 @@ func (c *Commander) cderegister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Events.Emit("commandDeregister", cmdName)
|
delete(c.Commands, cmdName)
|
||||||
|
|
||||||
return ct.Next(), err
|
return ct.Next(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// registry() -> table
|
||||||
|
// Returns all registered commanders. Returns a list of tables with the following keys:
|
||||||
|
// - `exec`: The function used to run the commander. Commanders require args and sinks to be passed.
|
||||||
|
// #returns table
|
||||||
|
func (c *Commander) cregistry(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||||
|
registryLua := rt.NewTable()
|
||||||
|
for cmdName, cmd := range c.Commands {
|
||||||
|
cmdTbl := rt.NewTable()
|
||||||
|
cmdTbl.Set(rt.StringValue("exec"), rt.FunctionValue(cmd))
|
||||||
|
|
||||||
|
registryLua.Set(rt.StringValue(cmdName), rt.TableValue(cmdTbl))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ct.PushingNext1(t.Runtime, rt.TableValue(registryLua)), nil
|
||||||
|
}
|
||||||
|
|
|
@ -18,17 +18,29 @@ import (
|
||||||
|
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
"github.com/arnodel/golua/lib/packagelib"
|
"github.com/arnodel/golua/lib/packagelib"
|
||||||
|
"github.com/arnodel/golua/lib/iolib"
|
||||||
|
"mvdan.cc/sh/v3/interp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rtmm *rt.Runtime
|
|
||||||
var watcherMetaKey = rt.StringValue("hshwatcher")
|
var watcherMetaKey = rt.StringValue("hshwatcher")
|
||||||
var Loader = packagelib.Loader{
|
type fs struct{
|
||||||
Load: loaderFunc,
|
runner *interp.Runner
|
||||||
|
Loader packagelib.Loader
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(runner *interp.Runner) *fs {
|
||||||
|
f := &fs{
|
||||||
|
runner: runner,
|
||||||
|
}
|
||||||
|
f.Loader = packagelib.Loader{
|
||||||
|
Load: f.loaderFunc,
|
||||||
Name: "fs",
|
Name: "fs",
|
||||||
}
|
}
|
||||||
|
|
||||||
func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
return f
|
||||||
rtmm = rtm
|
}
|
||||||
|
|
||||||
|
func (f *fs) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
watcherMethods := rt.NewTable()
|
watcherMethods := rt.NewTable()
|
||||||
watcherFuncs := map[string]util.LuaExport{
|
watcherFuncs := map[string]util.LuaExport{
|
||||||
"start": {watcherStart, 1, false},
|
"start": {watcherStart, 1, false},
|
||||||
|
@ -54,15 +66,16 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
rtm.SetRegistry(watcherMetaKey, rt.TableValue(watcherMeta))
|
rtm.SetRegistry(watcherMetaKey, rt.TableValue(watcherMeta))
|
||||||
|
|
||||||
exports := map[string]util.LuaExport{
|
exports := map[string]util.LuaExport{
|
||||||
"cd": util.LuaExport{fcd, 1, false},
|
"cd": util.LuaExport{f.fcd, 1, false},
|
||||||
"mkdir": util.LuaExport{fmkdir, 2, false},
|
"mkdir": util.LuaExport{f.fmkdir, 2, false},
|
||||||
"stat": util.LuaExport{fstat, 1, false},
|
"stat": util.LuaExport{f.fstat, 1, false},
|
||||||
"readdir": util.LuaExport{freaddir, 1, false},
|
"readdir": util.LuaExport{f.freaddir, 1, false},
|
||||||
"abs": util.LuaExport{fabs, 1, false},
|
"abs": util.LuaExport{f.fabs, 1, false},
|
||||||
"basename": util.LuaExport{fbasename, 1, false},
|
"basename": util.LuaExport{f.fbasename, 1, false},
|
||||||
"dir": util.LuaExport{fdir, 1, false},
|
"dir": util.LuaExport{f.fdir, 1, false},
|
||||||
"glob": util.LuaExport{fglob, 1, false},
|
"glob": util.LuaExport{f.fglob, 1, false},
|
||||||
"join": util.LuaExport{fjoin, 0, true},
|
"join": util.LuaExport{f.fjoin, 0, true},
|
||||||
|
"pipe": util.LuaExport{f.fpipe, 0, false},
|
||||||
"watch": util.LuaExport{fwatch, 2, false},
|
"watch": util.LuaExport{fwatch, 2, false},
|
||||||
}
|
}
|
||||||
mod := rt.NewTable()
|
mod := rt.NewTable()
|
||||||
|
@ -78,7 +91,7 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
// This can be used to resolve short paths like `..` to `/home/user`.
|
// This can be used to resolve short paths like `..` to `/home/user`.
|
||||||
// #param path string
|
// #param path string
|
||||||
// #returns string
|
// #returns string
|
||||||
func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (f *fs) fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
path, err := c.StringArg(0)
|
path, err := c.StringArg(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -98,7 +111,7 @@ func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
// `.` will be returned.
|
// `.` will be returned.
|
||||||
// #param path string Path to get the base name of.
|
// #param path string Path to get the base name of.
|
||||||
// #returns string
|
// #returns string
|
||||||
func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (f *fs) fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -113,7 +126,7 @@ func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
// cd(dir)
|
// cd(dir)
|
||||||
// Changes Hilbish's directory to `dir`.
|
// Changes Hilbish's directory to `dir`.
|
||||||
// #param dir string Path to change directory to.
|
// #param dir string Path to change directory to.
|
||||||
func fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (f *fs) fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -123,10 +136,12 @@ func fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
path = util.ExpandHome(strings.TrimSpace(path))
|
path = util.ExpandHome(strings.TrimSpace(path))
|
||||||
|
|
||||||
|
abspath, _ := filepath.Abs(path)
|
||||||
err = os.Chdir(path)
|
err = os.Chdir(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
interp.Dir(abspath)(f.runner)
|
||||||
|
|
||||||
return c.Next(), err
|
return c.Next(), err
|
||||||
}
|
}
|
||||||
|
@ -136,7 +151,7 @@ func fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
// `~/Documents/doc.txt` then this function will return `~/Documents`.
|
// `~/Documents/doc.txt` then this function will return `~/Documents`.
|
||||||
// #param path string Path to get the directory for.
|
// #param path string Path to get the directory for.
|
||||||
// #returns string
|
// #returns string
|
||||||
func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (f *fs) fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -167,7 +182,7 @@ print(matches)
|
||||||
-- -> {'init.lua', 'code.lua'}
|
-- -> {'init.lua', 'code.lua'}
|
||||||
#example
|
#example
|
||||||
*/
|
*/
|
||||||
func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (f *fs) fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -201,7 +216,7 @@ print(fs.join(hilbish.userDir.config, 'hilbish'))
|
||||||
-- -> '/home/user/.config/hilbish' on Linux
|
-- -> '/home/user/.config/hilbish' on Linux
|
||||||
#example
|
#example
|
||||||
*/
|
*/
|
||||||
func fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (f *fs) fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
strs := make([]string, len(c.Etc()))
|
strs := make([]string, len(c.Etc()))
|
||||||
for i, v := range c.Etc() {
|
for i, v := range c.Etc() {
|
||||||
if v.Type() != rt.StringType {
|
if v.Type() != rt.StringType {
|
||||||
|
@ -228,7 +243,7 @@ func fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
fs.mkdir('./foo/bar', true)
|
fs.mkdir('./foo/bar', true)
|
||||||
#example
|
#example
|
||||||
*/
|
*/
|
||||||
func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (f *fs) fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.CheckNArgs(2); err != nil {
|
if err := c.CheckNArgs(2); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -254,11 +269,27 @@ func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), err
|
return c.Next(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fpipe() -> File, File
|
||||||
|
// Returns a pair of connected files, also known as a pipe.
|
||||||
|
// The type returned is a Lua file, same as returned from `io` functions.
|
||||||
|
// #returns File
|
||||||
|
// #returns File
|
||||||
|
func (f *fs) fpipe(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
rf, wf, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rfLua := iolib.NewFile(rf, 0)
|
||||||
|
wfLua := iolib.NewFile(wf, 0)
|
||||||
|
|
||||||
|
return c.PushingNext(t.Runtime, rfLua.Value(t.Runtime), wfLua.Value(t.Runtime)), nil
|
||||||
|
}
|
||||||
// readdir(path) -> table[string]
|
// readdir(path) -> table[string]
|
||||||
// Returns a list of all files and directories in the provided path.
|
// Returns a list of all files and directories in the provided path.
|
||||||
// #param dir string
|
// #param dir string
|
||||||
// #returns table
|
// #returns table
|
||||||
func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (f *fs) freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -306,7 +337,7 @@ Would print the following:
|
||||||
]]--
|
]]--
|
||||||
#example
|
#example
|
||||||
*/
|
*/
|
||||||
func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (f *fs) fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -350,7 +381,7 @@ func fwatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dw := newWatcher(dir, watcher)
|
dw := newWatcher(dir, watcher, t.Runtime)
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, rt.UserDataValue(dw.ud)), nil
|
return c.PushingNext1(t.Runtime, rt.UserDataValue(dw.ud)), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ type watcher struct{
|
||||||
started bool
|
started bool
|
||||||
ud *rt.UserData
|
ud *rt.UserData
|
||||||
notifyChan chan notify.EventInfo
|
notifyChan chan notify.EventInfo
|
||||||
|
rtm *rt.Runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *watcher) start() {
|
func (w *watcher) start() {
|
||||||
|
@ -32,7 +33,7 @@ func (w *watcher) start() {
|
||||||
ev := notif.Event().String()
|
ev := notif.Event().String()
|
||||||
path := notif.Path()
|
path := notif.Path()
|
||||||
|
|
||||||
_, err := rt.Call1(rtmm.MainThread(), rt.FunctionValue(w.callback), rt.StringValue(ev), rt.StringValue(path))
|
_, err := rt.Call1(w.rtm.MainThread(), rt.FunctionValue(w.callback), rt.StringValue(ev), rt.StringValue(path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: throw error
|
// TODO: throw error
|
||||||
}
|
}
|
||||||
|
@ -73,9 +74,10 @@ func watcherStop(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWatcher(path string, callback *rt.Closure) *watcher {
|
func newWatcher(path string, callback *rt.Closure, rtm *rt.Runtime) *watcher {
|
||||||
pw := &watcher{
|
pw := &watcher{
|
||||||
path: path,
|
path: path,
|
||||||
|
rtm: rtm,
|
||||||
callback: callback,
|
callback: callback,
|
||||||
}
|
}
|
||||||
pw.ud = watcherUserData(pw)
|
pw.ud = watcherUserData(pw)
|
||||||
|
@ -104,6 +106,6 @@ func valueToWatcher(val rt.Value) (*watcher, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func watcherUserData(j *watcher) *rt.UserData {
|
func watcherUserData(j *watcher) *rt.UserData {
|
||||||
watcherMeta := rtmm.Registry(watcherMetaKey)
|
watcherMeta := j.rtm.Registry(watcherMetaKey)
|
||||||
return rt.NewUserData(j, watcherMeta.AsTable())
|
return rt.NewUserData(j, watcherMeta.AsTable())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build windows
|
//go:build windows
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build darwin linux
|
//go:build unix
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build windows
|
//go:build windows
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
17
lua.go
17
lua.go
|
@ -30,22 +30,11 @@ func luaInit() {
|
||||||
util.DoString(l, "hilbish = require 'hilbish'")
|
util.DoString(l, "hilbish = require 'hilbish'")
|
||||||
|
|
||||||
// Add fs and terminal module module to Lua
|
// Add fs and terminal module module to Lua
|
||||||
lib.LoadLibs(l, fs.Loader)
|
f := fs.New(runner)
|
||||||
|
lib.LoadLibs(l, f.Loader)
|
||||||
lib.LoadLibs(l, terminal.Loader)
|
lib.LoadLibs(l, terminal.Loader)
|
||||||
|
|
||||||
cmds := commander.New(l)
|
cmds = commander.New(l)
|
||||||
// When a command from Lua is added, register it for use
|
|
||||||
cmds.Events.On("commandRegister", func(args ...interface{}) {
|
|
||||||
cmdName := args[0].(string)
|
|
||||||
cmd := args[1].(*rt.Closure)
|
|
||||||
|
|
||||||
commands[cmdName] = cmd
|
|
||||||
})
|
|
||||||
cmds.Events.On("commandDeregister", func(args ...interface{}) {
|
|
||||||
cmdName := args[0].(string)
|
|
||||||
|
|
||||||
delete(commands, cmdName)
|
|
||||||
})
|
|
||||||
lib.LoadLibs(l, cmds.Loader)
|
lib.LoadLibs(l, cmds.Loader)
|
||||||
|
|
||||||
hooks = bait.New(l)
|
hooks = bait.New(l)
|
||||||
|
|
32
main.go
32
main.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -14,18 +15,19 @@ import (
|
||||||
|
|
||||||
"hilbish/util"
|
"hilbish/util"
|
||||||
"hilbish/golibs/bait"
|
"hilbish/golibs/bait"
|
||||||
|
"hilbish/golibs/commander"
|
||||||
|
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
"github.com/pborman/getopt"
|
"github.com/pborman/getopt"
|
||||||
"github.com/maxlandon/readline"
|
"github.com/maxlandon/readline"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
|
"mvdan.cc/sh/v3/interp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
l *rt.Runtime
|
l *rt.Runtime
|
||||||
lr *lineReader
|
lr *lineReader
|
||||||
|
|
||||||
commands = map[string]*rt.Closure{}
|
|
||||||
luaCompletions = map[string]*rt.Closure{}
|
luaCompletions = map[string]*rt.Closure{}
|
||||||
|
|
||||||
confDir string
|
confDir string
|
||||||
|
@ -33,11 +35,14 @@ var (
|
||||||
curuser *user.User
|
curuser *user.User
|
||||||
|
|
||||||
hooks *bait.Bait
|
hooks *bait.Bait
|
||||||
|
cmds *commander.Commander
|
||||||
defaultConfPath string
|
defaultConfPath string
|
||||||
defaultHistPath string
|
defaultHistPath string
|
||||||
|
runner *interp.Runner
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
runner, _ = interp.New()
|
||||||
curuser, _ = user.Current()
|
curuser, _ = user.Current()
|
||||||
homedir := curuser.HomeDir
|
homedir := curuser.HomeDir
|
||||||
confDir, _ = os.UserConfigDir()
|
confDir, _ = os.UserConfigDir()
|
||||||
|
@ -114,7 +119,13 @@ func main() {
|
||||||
|
|
||||||
// Set $SHELL if the user wants to
|
// Set $SHELL if the user wants to
|
||||||
if *setshflag {
|
if *setshflag {
|
||||||
os.Setenv("SHELL", os.Args[0])
|
os.Setenv("SHELL", "hilbish")
|
||||||
|
|
||||||
|
path, err := exec.LookPath("hilbish")
|
||||||
|
if err == nil {
|
||||||
|
os.Setenv("SHELL", path)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lr = newLineReader("", false)
|
lr = newLineReader("", false)
|
||||||
|
@ -212,8 +223,9 @@ input:
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasSuffix(input, "\\") {
|
if strings.HasSuffix(input, "\\") {
|
||||||
|
print("\n")
|
||||||
for {
|
for {
|
||||||
input, err = continuePrompt(input)
|
input, err = continuePrompt(strings.TrimSuffix(input, "\\") + "\n", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
running = true
|
running = true
|
||||||
lr.SetPrompt(fmtPrompt(prompt))
|
lr.SetPrompt(fmtPrompt(prompt))
|
||||||
|
@ -237,16 +249,24 @@ input:
|
||||||
exit(0)
|
exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func continuePrompt(prev string) (string, error) {
|
func continuePrompt(prev string, newline bool) (string, error) {
|
||||||
hooks.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 {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
cont = strings.TrimSpace(cont)
|
|
||||||
|
|
||||||
return prev + strings.TrimSuffix(cont, "\n"), nil
|
if newline {
|
||||||
|
cont = "\n" + cont
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(cont, "\\") {
|
||||||
|
cont = strings.TrimSuffix(cont, "\\") + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev + cont, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This semi cursed function formats our prompt (obviously)
|
// This semi cursed function formats our prompt (obviously)
|
||||||
|
|
|
@ -9,6 +9,8 @@ commander.register('cat', function(args, sinks)
|
||||||
usage: cat [file]...]]
|
usage: cat [file]...]]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local chunkSize = 2^13 -- 8K buffer size
|
||||||
|
|
||||||
for _, fName in ipairs(args) do
|
for _, fName in ipairs(args) do
|
||||||
local f = io.open(fName)
|
local f = io.open(fName)
|
||||||
if f == nil then
|
if f == nil then
|
||||||
|
@ -17,7 +19,11 @@ usage: cat [file]...]]
|
||||||
goto continue
|
goto continue
|
||||||
end
|
end
|
||||||
|
|
||||||
sinks.out:writeln(f:read '*a')
|
while true do
|
||||||
|
local block = f:read(chunkSize)
|
||||||
|
if not block then break end
|
||||||
|
sinks.out:write(block)
|
||||||
|
end
|
||||||
::continue::
|
::continue::
|
||||||
end
|
end
|
||||||
io.flush()
|
io.flush()
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
local commander = require 'commander'
|
local commander = require 'commander'
|
||||||
|
|
||||||
commander.register('exec', function(args)
|
commander.register('exec', function(args)
|
||||||
|
if #args == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
hilbish.exec(args[1])
|
hilbish.exec(args[1])
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -17,8 +17,8 @@ function M.renderCodeBlock(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
for i, line in ipairs(lines) do
|
for i, line in ipairs(lines) do
|
||||||
lines[i] = ' ' .. M.highlight(line:sub(0, longest))
|
lines[i] = lunacolors.format('{greyBg}' .. ' ' .. M.highlight(line:sub(0, longest))
|
||||||
.. string.rep(' ', longest - line:len()) .. ' '
|
.. string.rep(' ', longest - line:len()) .. ' ')
|
||||||
end
|
end
|
||||||
|
|
||||||
return '\n' .. lunacolors.format('{greyBg}' .. table.concat(lines, '\n')) .. '\n'
|
return '\n' .. lunacolors.format('{greyBg}' .. table.concat(lines, '\n')) .. '\n'
|
||||||
|
|
|
@ -61,17 +61,24 @@ function Greenhouse:updateCurrentPage(text)
|
||||||
page:setText(text)
|
page:setText(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local ansiPatters = {
|
||||||
|
'\x1b%[%d+;%d+;%d+;%d+;%d+%w',
|
||||||
|
'\x1b%[%d+;%d+;%d+;%d+%w',
|
||||||
|
'\x1b%[%d+;%d+;%d+%w',
|
||||||
|
'\x1b%[%d+;%d+%w',
|
||||||
|
'\x1b%[%d+%w'
|
||||||
|
}
|
||||||
|
|
||||||
function Greenhouse:sub(str, offset, limit)
|
function Greenhouse:sub(str, offset, limit)
|
||||||
local overhead = 0
|
local overhead = 0
|
||||||
local function addOverhead(s)
|
local function addOverhead(s)
|
||||||
overhead = overhead + string.len(s)
|
overhead = overhead + string.len(s)
|
||||||
end
|
end
|
||||||
|
|
||||||
local s = str:gsub('\x1b%[%d+;%d+;%d+;%d+;%d+%w', addOverhead)
|
local s = str
|
||||||
:gsub('\x1b%[%d+;%d+;%d+;%d+%w', addOverhead)
|
for _, pat in ipairs(ansiPatters) do
|
||||||
:gsub('\x1b%[%d+;%d+;%d+%w',addOverhead)
|
s = s:gsub(pat, addOverhead)
|
||||||
:gsub('\x1b%[%d+;%d+%w', addOverhead)
|
end
|
||||||
:gsub('\x1b%[%d+%w', addOverhead)
|
|
||||||
|
|
||||||
return s:sub(offset, utf8.offset(str, limit + overhead) or limit + overhead)
|
return s:sub(offset, utf8.offset(str, limit + overhead) or limit + overhead)
|
||||||
--return s:sub(offset, limit + overhead)
|
--return s:sub(offset, limit + overhead)
|
||||||
|
@ -94,14 +101,40 @@ function Greenhouse:draw()
|
||||||
self.sink:write(ansikit.getCSI(2, 'J'))
|
self.sink:write(ansikit.getCSI(2, 'J'))
|
||||||
|
|
||||||
local writer = self.sink.writeln
|
local writer = self.sink.writeln
|
||||||
|
self.attributes = {}
|
||||||
for i = offset, offset + self.region.height - 1 do
|
for i = offset, offset + self.region.height - 1 do
|
||||||
|
local resetEnd = false
|
||||||
if i > #lines then break end
|
if i > #lines then break end
|
||||||
|
|
||||||
if i == offset + self.region.height - 1 then writer = self.sink.write end
|
if i == offset + self.region.height - 1 then writer = self.sink.write end
|
||||||
|
|
||||||
self.sink:write(ansikit.getCSI(self.start + i - offset .. ';1', 'H'))
|
self.sink:write(ansikit.getCSI(self.start + i - offset .. ';1', 'H'))
|
||||||
local line = lines[i]:gsub('{separator}', function() return self.separator:rep(self.region.width - 1) end)
|
local line = lines[i]:gsub('{separator}', function() return self.separator:rep(self.region.width - 1) end)
|
||||||
writer(self.sink, self:sub(line:gsub('\t', ' '), self.horizOffset, self.region.width))
|
for _, pat in ipairs(ansiPatters) do
|
||||||
|
line:gsub(pat, function(s)
|
||||||
|
if s == lunacolors.formatColors.reset then
|
||||||
|
self.attributes = {}
|
||||||
|
resetEnd = true
|
||||||
|
else
|
||||||
|
--resetEnd = false
|
||||||
|
--table.insert(self.attributes, s)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
if #self.attributes ~= 0 then
|
||||||
|
for _, attr in ipairs(self.attributes) do
|
||||||
|
--writer(self.sink, attr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
]]--
|
||||||
|
|
||||||
|
self.sink:write(lunacolors.formatColors.reset)
|
||||||
|
writer(self.sink, self:sub(line:gsub('\t', ' '), self.horizOffset, self.region.width + self.horizOffset))
|
||||||
|
if resetEnd then
|
||||||
|
self.sink:write(lunacolors.formatColors.reset)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
writer(self.sink, '\27[0m')
|
writer(self.sink, '\27[0m')
|
||||||
self:render()
|
self:render()
|
||||||
|
@ -271,6 +304,15 @@ end
|
||||||
function Greenhouse:input(char)
|
function Greenhouse:input(char)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function read()
|
||||||
|
terminal.saveState()
|
||||||
|
terminal.setRaw()
|
||||||
|
local c = hilbish.editor.readChar()
|
||||||
|
|
||||||
|
terminal.restoreState()
|
||||||
|
return c
|
||||||
|
end
|
||||||
|
|
||||||
function Greenhouse:initUi()
|
function Greenhouse:initUi()
|
||||||
local ansikit = require 'ansikit'
|
local ansikit = require 'ansikit'
|
||||||
local bait = require 'bait'
|
local bait = require 'bait'
|
||||||
|
@ -280,14 +322,17 @@ function Greenhouse:initUi()
|
||||||
local Page = require 'nature.greenhouse.page'
|
local Page = require 'nature.greenhouse.page'
|
||||||
local done = false
|
local done = false
|
||||||
|
|
||||||
bait.catch('signal.sigint', function()
|
local function sigint()
|
||||||
ansikit.clear()
|
ansikit.clear()
|
||||||
done = true
|
done = true
|
||||||
end)
|
end
|
||||||
|
|
||||||
bait.catch('signal.resize', function()
|
local function resize()
|
||||||
self:update()
|
self:update()
|
||||||
end)
|
end
|
||||||
|
bait.catch('signal.sigint', sigint)
|
||||||
|
|
||||||
|
bait.catch('signal.resize', resize)
|
||||||
|
|
||||||
ansikit.screenAlt()
|
ansikit.screenAlt()
|
||||||
ansikit.clear(true)
|
ansikit.clear(true)
|
||||||
|
@ -311,15 +356,10 @@ function Greenhouse:initUi()
|
||||||
|
|
||||||
ansikit.showCursor()
|
ansikit.showCursor()
|
||||||
ansikit.screenMain()
|
ansikit.screenMain()
|
||||||
end
|
|
||||||
|
|
||||||
function read()
|
self = nil
|
||||||
terminal.saveState()
|
bait.release('signal.sigint', sigint)
|
||||||
terminal.setRaw()
|
bait.release('signal.resize', resize)
|
||||||
local c = hilbish.editor.readChar()
|
|
||||||
|
|
||||||
terminal.restoreState()
|
|
||||||
return c
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return Greenhouse
|
return Greenhouse
|
||||||
|
|
|
@ -14,7 +14,8 @@ The nice lil shell for {blue}Lua{reset} fanatics!
|
||||||
motd = true,
|
motd = true,
|
||||||
fuzzy = false,
|
fuzzy = false,
|
||||||
notifyJobFinish = true,
|
notifyJobFinish = true,
|
||||||
crimmas = true
|
crimmas = true,
|
||||||
|
tips = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for optsName, default in pairs(defaultOpts) do
|
for optsName, default in pairs(defaultOpts) do
|
||||||
|
|
|
@ -2,8 +2,9 @@ local bait = require 'bait'
|
||||||
local lunacolors = require 'lunacolors'
|
local lunacolors = require 'lunacolors'
|
||||||
|
|
||||||
hilbish.motd = [[
|
hilbish.motd = [[
|
||||||
Finally at {red}v2.2!{reset} So much {green}documentation improvements{reset}
|
Wait ... {magenta}2.3{reset} is basically the same as {red}2.2?{reset}
|
||||||
and 1 single fix for Windows! {blue}.. and a feature they can't use.{reset}
|
Erm.. {blue}Ctrl-C works for Commanders,{reset} {cyan}and the sh runner has some fixes.{reset}
|
||||||
|
Just trust me bro, this is an important bug fix release. {red}- 🌺 sammyette{reset}
|
||||||
]]
|
]]
|
||||||
|
|
||||||
bait.catch('hilbish.init', function()
|
bait.catch('hilbish.init', function()
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
local bait = require 'bait'
|
||||||
|
local lunacolors = require 'lunacolors'
|
||||||
|
|
||||||
|
local postamble = [[
|
||||||
|
{yellow}These tips can be disabled with {reset}{invert} hilbish.opts.tips = false {reset}
|
||||||
|
]]
|
||||||
|
|
||||||
|
hilbish.tips = {
|
||||||
|
'Join the discord and say hi! {blue}https://discord.gg/3PDdcQz{reset}',
|
||||||
|
'{green}hilbish.alias{reset} interface manages shell aliases. See more detail by running {blue}doc api hilbish.alias.',
|
||||||
|
'{green}hilbish.appendPath(\'path\'){reset} -> Appends the provided dir to the command path ($PATH)',
|
||||||
|
'{green}hilbish.completions{reset} -> Used to control suggestions when tab completing.',
|
||||||
|
'{green}hilbish.message{reset} -> Simple notification system which can be used by other plugins and parts of the shell to notify the user of various actions.',
|
||||||
|
[[
|
||||||
|
{green}hilbish.opts{reset} -> Simple toggle or value options a user can set.
|
||||||
|
You may disable the startup greeting by {invert}hilbish.opts.greeting = false{reset}
|
||||||
|
]],
|
||||||
|
[[
|
||||||
|
{green}hilbish.runner{reset} -> The runner interface contains functions to
|
||||||
|
manage how Hilbish interprets interactive input. The default runners can run
|
||||||
|
shell script and Lua code!
|
||||||
|
]],
|
||||||
|
[[
|
||||||
|
Add Lua-written commands with the commander module!
|
||||||
|
Check the command {blue}doc api commander{reset} or the web docs:
|
||||||
|
https://rosettea.github.io/Hilbish/docs/api/commander/
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
|
||||||
|
bait.catch('hilbish.init', function()
|
||||||
|
if hilbish.interactive and hilbish.opts.tips then
|
||||||
|
local idx = math.random(1, #hilbish.tips)
|
||||||
|
print(lunacolors.format('{yellow}🛈 Tip:{reset} ' .. hilbish.tips[idx] .. '\n' .. postamble))
|
||||||
|
end
|
||||||
|
end)
|
|
@ -56,3 +56,10 @@ func (rl *Instance) resetHintText() {
|
||||||
//rl.hintY = 0
|
//rl.hintY = 0
|
||||||
rl.hintText = []rune{}
|
rl.hintText = []rune{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rl *Instance) insertHintText() {
|
||||||
|
if len(rl.hintText) != 0 {
|
||||||
|
// fill in hint text
|
||||||
|
rl.insert(rl.hintText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -128,15 +128,19 @@ func (rl *Instance) walkHistory(i int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rl.histOffset += i
|
rl.histOffset += i
|
||||||
|
historyLen := history.Len()
|
||||||
if rl.histOffset == 0 {
|
if rl.histOffset == 0 {
|
||||||
rl.line = []rune(rl.lineBuf)
|
rl.line = []rune(rl.lineBuf)
|
||||||
rl.pos = len(rl.lineBuf)
|
rl.pos = len(rl.lineBuf)
|
||||||
} else if rl.histOffset <= -1 {
|
} else if rl.histOffset <= -1 {
|
||||||
rl.histOffset = 0
|
rl.histOffset = 0
|
||||||
|
} else if rl.histOffset > historyLen {
|
||||||
|
// TODO: should this wrap around?s
|
||||||
|
rl.histOffset = 0
|
||||||
} else {
|
} else {
|
||||||
dedup = true
|
dedup = true
|
||||||
old = string(rl.line)
|
old = string(rl.line)
|
||||||
new, err = history.GetLine(history.Len() - rl.histOffset)
|
new, err = history.GetLine(historyLen - 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")
|
||||||
|
|
|
@ -707,6 +707,9 @@ func (rl *Instance) escapeSeq(r []rune) {
|
||||||
rl.renderHelpers()
|
rl.renderHelpers()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rl.insertHintText()
|
||||||
|
|
||||||
if (rl.modeViMode == VimInsert && rl.pos < len(rl.line)) ||
|
if (rl.modeViMode == VimInsert && rl.pos < len(rl.line)) ||
|
||||||
(rl.modeViMode != VimInsert && rl.pos < len(rl.line)-1) {
|
(rl.modeViMode != VimInsert && rl.pos < len(rl.line)-1) {
|
||||||
rl.moveCursorByAdjust(1)
|
rl.moveCursorByAdjust(1)
|
||||||
|
|
|
@ -29,7 +29,7 @@ func (rl *Instance) updateTabFind(r []rune) {
|
||||||
rl.search = string(rl.tfLine)
|
rl.search = string(rl.tfLine)
|
||||||
|
|
||||||
// We update and print
|
// We update and print
|
||||||
rl.clearHelpers()
|
//rl.clearHelpers()
|
||||||
rl.getTabCompletion()
|
rl.getTabCompletion()
|
||||||
rl.renderHelpers()
|
rl.renderHelpers()
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ func (rl *Instance) clearHelpers() {
|
||||||
moveCursorForwards(rl.fullX)
|
moveCursorForwards(rl.fullX)
|
||||||
|
|
||||||
// Clear everything below
|
// Clear everything below
|
||||||
//print(seqClearScreenBelow)
|
print(seqClearScreenBelow)
|
||||||
|
|
||||||
// Go back to current cursor position
|
// Go back to current cursor position
|
||||||
moveCursorBackwards(GetTermWidth())
|
moveCursorBackwards(GetTermWidth())
|
||||||
|
|
13
rl.go
13
rl.go
|
@ -70,11 +70,8 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
hooks.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 {
|
hinter := hshMod.Get(rt.StringValue("hinter"))
|
||||||
return []rune{}
|
retVal, err := rt.Call1(l.MainThread(), hinter,
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
||||||
|
@ -89,10 +86,8 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
return []rune(hintText)
|
return []rune(hintText)
|
||||||
}
|
}
|
||||||
rl.SyntaxHighlighter = func(line []rune) string {
|
rl.SyntaxHighlighter = func(line []rune) string {
|
||||||
if highlighter == nil {
|
highlighter := hshMod.Get(rt.StringValue("highlighter"))
|
||||||
return string(line)
|
retVal, err := rt.Call1(l.MainThread(), highlighter,
|
||||||
}
|
|
||||||
retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(highlighter),
|
|
||||||
rt.StringValue(string(line)))
|
rt.StringValue(string(line)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
|
|
@ -21,16 +21,18 @@ A runner is passed the input and has to return a table with these values.
|
||||||
All are not required, only the useful ones the runner needs to return.
|
All are not required, only the useful ones the runner needs to return.
|
||||||
(So if there isn't an error, just omit `err`.)
|
(So if there isn't an error, just omit `err`.)
|
||||||
|
|
||||||
- `exitCode` (number): A numerical code to indicate the exit result.
|
- `exitCode` (number): Exit code of the command
|
||||||
- `input` (string): The user input. This will be used to add
|
- `input` (string): The text input of the user. This is used by Hilbish to append extra input, in case
|
||||||
to the history.
|
more is requested.
|
||||||
- `err` (string): A string to indicate an interal error for the runner.
|
- `err` (string): A string that represents an error from the runner.
|
||||||
It can be set to a few special values for Hilbish to throw the right hooks and have a better looking message:
|
This should only be set when, for example, there is a syntax error.
|
||||||
|
It can be set to a few special values for Hilbish to throw the right
|
||||||
`[command]: not-found` will throw a command.not-found hook based on what `[command]` is.
|
hooks and have a better looking message.
|
||||||
|
- `<command>: not-found` will throw a `command.not-found` hook
|
||||||
`[command]: not-executable` will throw a command.not-executable hook.
|
based on what `<command>` is.
|
||||||
- `continue` (boolean): Whether to prompt the user for more input.
|
- `<command>: not-executable` will throw a `command.not-executable` hook.
|
||||||
|
- `continue` (boolean): Whether Hilbish should prompt the user for no input
|
||||||
|
- `newline` (boolean): Whether a newline should be added at the end of `input`.
|
||||||
|
|
||||||
Here is a simple example of a fennel runner. It falls back to
|
Here is a simple example of a fennel runner. It falls back to
|
||||||
shell script if fennel eval has an error.
|
shell script if fennel eval has an error.
|
||||||
|
@ -85,7 +87,7 @@ func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, exitCode, cont, err := execSh(aliases.Resolve(cmd))
|
_, exitCode, cont, newline, 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())
|
||||||
|
@ -94,6 +96,7 @@ func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
runnerRet.Set(rt.StringValue("input"), rt.StringValue(cmd))
|
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("newline"), rt.BoolValue(newline))
|
||||||
runnerRet.Set(rt.StringValue("err"), luaErr)
|
runnerRet.Set(rt.StringValue("err"), luaErr)
|
||||||
|
|
||||||
return c.PushingNext(t.Runtime, rt.TableValue(runnerRet)), nil
|
return c.PushingNext(t.Runtime, rt.TableValue(runnerRet)), nil
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build darwin linux
|
//go:build unix
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build windows
|
//go:build windows
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
4
vars.go
4
vars.go
|
@ -11,8 +11,8 @@ var (
|
||||||
|
|
||||||
// Version info
|
// Version info
|
||||||
var (
|
var (
|
||||||
ver = "v2.2.1"
|
ver = "v2.3.3"
|
||||||
releaseName = "Poppy"
|
releaseName = "Alyssum"
|
||||||
|
|
||||||
gitCommit string
|
gitCommit string
|
||||||
gitBranch string
|
gitBranch string
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build darwin
|
//go:build darwin
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build linux
|
//go:build unix && !darwin
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build windows
|
//go:build windows
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
---
|
||||||
|
title: "v2.3 Release"
|
||||||
|
date: 2024-07-20T10:05:17-04:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
> The release with full changelogs and prebuilt binaries can be
|
||||||
|
seen at the [v2.3.0](https://github.com/Rosettea/Hilbish/releases/tag/v2.3.0)
|
||||||
|
tag.
|
||||||
|
|
||||||
|
Hilbish v2.3 has now been released! This is small feature and bug fix release
|
||||||
|
which took a while to cme ut since I took a long break from programming in general.
|
||||||
|
The next release will be great, so stay tuned for that.
|
||||||
|
|
||||||
|
# Features
|
||||||
|
## Pipes (via Lua)
|
||||||
|
Commands can now be piped to each other via the Lua API with the `hilbish.run`
|
||||||
|
function and an `fs.pipe`.
|
||||||
|
|
||||||
|
Here is a minimal example of the new usage which allows users to now pipe commands
|
||||||
|
directly via Lua functions:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local fs = require 'fs'
|
||||||
|
local pr, pw = fs.pipe()
|
||||||
|
hilbish.run('ls -l', {
|
||||||
|
stdout = pw,
|
||||||
|
stderr = pw,
|
||||||
|
})
|
||||||
|
|
||||||
|
pw:close()
|
||||||
|
|
||||||
|
hilbish.run('wc -l', {
|
||||||
|
stdin = pr
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
This also means it's easier to make commands output to any stream output,
|
||||||
|
including in commanders.
|
||||||
|
|
||||||
|
# Bug Fixes
|
||||||
|
- Commanders can now be cancelled with Ctrl-C, which means if they froze for some reason
|
||||||
|
they can now be exited.
|
||||||
|
- The shell script interpreter now keeps its environment, and this also fixes the
|
||||||
|
current working directory being wrong with some commands.
|
||||||
|
- Some greenhouse bugs have been fixed, like randomly appearing when resizing the terminal
|
||||||
|
and some text attributes like color appearing where they weren't supposed to.
|
Loading…
Reference in New Issue