Compare commits

...

13 Commits

29 changed files with 524 additions and 153 deletions

View File

@ -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.18.8'
- 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 }}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,22 +1,26 @@
name: Build website name: Build website
on: on:
- push push:
- pull_request branches:
- master
pull_request:
branches:
- master
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
@ -32,14 +36,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

View File

@ -1,5 +1,44 @@
# 🎀 Changelog # 🎀 Changelog
## Unreleased
### Added
- `fs.pipe` function to get a pair of connected files (a pipe).
- Added an alternative 2nd parameter to `hilbish.run`, which is `streams`.
`streams` is a table of input and output streams to run the command with.
It uses these 3 keys:
- `input` as standard input for the command
- `out` as standard output
- `err` as standard error
Here is a minimal example of the new usage which allows users to now pipe commands
directly via Lua functions:
```lua
local fs = require 'fs'
local pr, pw = fs.pipe()
hilbish.run('ls -l', {
stdout = pw,
stderr = pw,
})
pw:close()
hilbish.run('wc -l', {
stdin = pr
})
```
### Fixed
- Fix ansi attributes causing issues with text when cut off in greenhouse
## [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 ## [2.2.2] - 2024-04-16
### Fixed ### Fixed
- Line refresh fixes (less flicker) - Line refresh fixes (less flicker)
@ -708,6 +747,7 @@ This input for example will prompt for more input to complete:
First "stable" release of Hilbish. First "stable" release of Hilbish.
[2.2.3]: https://github.com/Rosettea/Hilbish/compare/v2.2.2...v2.2.3
[2.2.2]: https://github.com/Rosettea/Hilbish/compare/v2.2.1...v2.2.2 [2.2.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

131
api.go
View File

@ -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 {
if !terminalOut {
strms = &streams{
stdout: new(bytes.Buffer),
stderr: new(bytes.Buffer),
}
}
} }
} else {
terminalOut = true
} }
var exitcode uint8 var exitcode uint8
stdout, stderr, err := execCommand(cmd, terminalOut) stdout, stderr, err := execCommand(cmd, strms)
if code, ok := interp.IsExitStatus(err); ok { if code, ok := interp.IsExitStatus(err); ok {
exitcode = code exitcode = code
@ -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
} }

View File

@ -488,7 +488,11 @@ 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))
mdTable.SetContent(i - diff, 1, dps.Doc[0]) if len(dps.Doc) == 0 {
fmt.Printf("WARNING! Function %s on module %s has no documentation!\n", dps.FuncName, modname)
} else {
mdTable.SetContent(i - diff, 1, dps.Doc[0])
}
} }
f.WriteString(mdTable.String()) f.WriteString(mdTable.String())
f.WriteString("\n") f.WriteString("\n")

View File

@ -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)
} }

View File

@ -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>

View File

@ -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'>

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

46
exec.go
View File

@ -28,6 +28,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
@ -236,7 +242,7 @@ func handleSh(cmdString string) (input string, exitCode uint8, cont bool, runErr
} }
func execSh(cmdString string) (string, uint8, bool, error) { func execSh(cmdString string) (string, uint8, bool, error) {
_, _, err := execCommand(cmdString, true) _, _, err := execCommand(cmdString, nil)
if err != nil { if err != nil {
// If input is incomplete, start multiline prompting // If input is incomplete, start multiline prompting
if syntax.IsIncomplete(err) { if syntax.IsIncomplete(err) {
@ -257,7 +263,7 @@ func execSh(cmdString string) (string, uint8, bool, error) {
} }
// Run command in sh interpreter // Run command in sh interpreter
func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) { func execCommand(cmd string, strms *streams) (io.Writer, io.Writer, error) {
file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "") file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "")
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -265,15 +271,24 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
runner, _ := interp.New() runner, _ := interp.New()
var stdout io.Writer if strms == nil {
var stderr io.Writer strms = &streams{}
if terminalOut {
interp.StdIO(os.Stdin, os.Stdout, os.Stderr)(runner)
} else {
stdout = new(bytes.Buffer)
stderr = new(bytes.Buffer)
interp.StdIO(os.Stdin, stdout, stderr)(runner)
} }
if strms.stdout == nil {
strms.stdout = os.Stdout
}
if strms.stderr == nil {
strms.stderr = os.Stderr
}
if strms.stdin == nil {
strms.stdin = os.Stdin
}
interp.StdIO(strms.stdin, strms.stdout, strms.stderr)(runner)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
printer := syntax.NewPrinter() printer := syntax.NewPrinter()
@ -292,11 +307,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 +342,18 @@ 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)) luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(cmd), rt.TableValue(luacmdArgs), rt.TableValue(sinks))
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,7 +365,7 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
exitcode = uint8(code) exitcode = uint8(code)
} else if luaexitcode != rt.NilValue { } else if luaexitcode != rt.NilValue {
// deregister commander // deregister commander
delete(commands, args[0]) delete(cmds.Commands, args[0])
fmt.Fprintf(os.Stderr, "Commander did not return number for exit code. %s, you're fired.\n", args[0]) fmt.Fprintf(os.Stderr, "Commander did not return number for exit code. %s, you're fired.\n", args[0])
} }

24
go.mod
View File

@ -3,26 +3,26 @@ module hilbish
go 1.18 go 1.18
require ( require (
github.com/arnodel/golua v0.0.0-20220221163911-dfcf252b6f86 github.com/arnodel/golua v0.0.0-20230215163904-e0b5347eaaa1
github.com/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/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.19.0
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 golang.org/x/term v0.19.0
mvdan.cc/sh/v3 v3.5.1 mvdan.cc/sh/v3 v3.8.0
) )
require ( require (
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/arnodel/strftime v0.1.6 // indirect github.com/arnodel/strftime v0.1.6 // indirect
github.com/evilsocket/islazy v1.10.6 // indirect github.com/evilsocket/islazy v1.11.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.14.0 // indirect
) )
replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b
@ -31,4 +31,4 @@ replace github.com/maxlandon/readline => ./readline
replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10 replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10
replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345 replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20240427174124-d239074c1749

51
go.sum
View File

@ -1,26 +1,21 @@
github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345 h1:QNYjYDogUSiNUkffbhFSrSCtpZhofeiVYGFN2FI4wSs= github.com/Rosettea/golua v0.0.0-20240427174124-d239074c1749 h1:jIFnWBTsYw8s7RX7H2AOXjDVhWP3ol7OzUVaPN2KnGI=
github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE= github.com/Rosettea/golua v0.0.0-20240427174124-d239074c1749/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b h1:s5eDMhBk6H1BgipgLub/gv9qeyBaTuiHM0k3h2/9TSE= github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b h1:s5eDMhBk6H1BgipgLub/gv9qeyBaTuiHM0k3h2/9TSE=
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs= github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/arnodel/edit v0.0.0-20220202110212-dfc8d7a13890/go.mod h1:AcpttpuZBaL9xl8/CX+Em4fBTUbwIkJ66RiAsJlNrBk=
github.com/arnodel/strftime v0.1.6 h1:0hc0pUvk8KhEMXE+htyaOUV42zNcf/csIbjzEFCJqsw= github.com/arnodel/strftime v0.1.6 h1:0hc0pUvk8KhEMXE+htyaOUV42zNcf/csIbjzEFCJqsw=
github.com/arnodel/strftime v0.1.6/go.mod h1:5NbK5XqYK8QpRZpqKNt4OlxLtIB8cotkLk4KTKzJfWs= github.com/arnodel/strftime v0.1.6/go.mod h1:5NbK5XqYK8QpRZpqKNt4OlxLtIB8cotkLk4KTKzJfWs=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504 h1:R1/AOzdMbopSliUTTEHvHbyNmnZ3YxY5GvdhTkpPsSY= github.com/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.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc= github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc=
github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/evilsocket/islazy v1.10.6 h1:MFq000a1ByoumoJWlytqg0qon0KlBeUfPsDjY0hK0bo= github.com/evilsocket/islazy v1.11.0 h1:B5w6uuS6ki6iDG+aH/RFeoMb8ijQh/pGabewqp2UeJ0=
github.com/evilsocket/islazy v1.10.6/go.mod h1:OrwQGYg3DuZvXUfmH+KIZDjwTCbrjy48T24TUpGqVVw= github.com/evilsocket/islazy v1.11.0/go.mod h1:muYH4x5MB5YRdkxnrOtrXLIBX6LySj1uFIqys94LKdo=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk= github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
@ -30,40 +25,30 @@ 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/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw= github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw=
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0= github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0=
github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0= github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0=
github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk= github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I= github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I=
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8= golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0= mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=

View File

@ -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
}

View File

@ -18,6 +18,7 @@ import (
rt "github.com/arnodel/golua/runtime" rt "github.com/arnodel/golua/runtime"
"github.com/arnodel/golua/lib/packagelib" "github.com/arnodel/golua/lib/packagelib"
"github.com/arnodel/golua/lib/iolib"
) )
var Loader = packagelib.Loader{ var Loader = packagelib.Loader{
@ -36,6 +37,7 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
"dir": util.LuaExport{fdir, 1, false}, "dir": util.LuaExport{fdir, 1, false},
"glob": util.LuaExport{fglob, 1, false}, "glob": util.LuaExport{fglob, 1, false},
"join": util.LuaExport{fjoin, 0, true}, "join": util.LuaExport{fjoin, 0, true},
"pipe": util.LuaExport{fpipe, 0, false},
} }
mod := rt.NewTable() mod := rt.NewTable()
util.SetExports(rtm, mod, exports) util.SetExports(rtm, mod, exports)
@ -226,6 +228,22 @@ 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 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

14
lua.go
View File

@ -33,19 +33,7 @@ func luaInit() {
lib.LoadLibs(l, fs.Loader) lib.LoadLibs(l, fs.Loader)
lib.LoadLibs(l, terminal.Loader) lib.LoadLibs(l, terminal.Loader)
cmds := commander.New(l) cmds = commander.New(l)
// When a command from Lua is added, register it for use
cmds.Events.On("commandRegister", func(args ...interface{}) {
cmdName := args[0].(string)
cmd := args[1].(*rt.Closure)
commands[cmdName] = cmd
})
cmds.Events.On("commandDeregister", func(args ...interface{}) {
cmdName := args[0].(string)
delete(commands, cmdName)
})
lib.LoadLibs(l, cmds.Loader) lib.LoadLibs(l, cmds.Loader)
hooks = bait.New(l) hooks = bait.New(l)

View File

@ -14,6 +14,7 @@ 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"
@ -25,7 +26,6 @@ var (
l *rt.Runtime l *rt.Runtime
lr *lineReader lr *lineReader
commands = map[string]*rt.Closure{}
luaCompletions = map[string]*rt.Closure{} luaCompletions = map[string]*rt.Closure{}
confDir string confDir string
@ -33,6 +33,7 @@ var (
curuser *user.User curuser *user.User
hooks *bait.Bait hooks *bait.Bait
cmds *commander.Commander
defaultConfPath string defaultConfPath string
defaultHistPath string defaultHistPath string
) )

View File

@ -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()

View File

@ -1,3 +1,23 @@
local fs = require 'fs'
-- explanation: this specific function gives to us info about
-- the currently running source. this includes a path to the
-- source file (info.source)
-- we will use that to automatically load all commands by reading
-- all the files in this dir and just requiring it.
local info = debug.getinfo(1)
local commandDir = fs.dir(info.source)
if commandDir == '.' then return end
local commands = fs.readdir(commandDir)
for _, command in ipairs(commands) do
local name = command:gsub('%.lua', '') -- chop off extension
if name ~= 'init' then
-- skip this file (for obvious reasons)
require('nature.completions.' .. name)
end
end
function hilbish.completion.handler(line, pos) function hilbish.completion.handler(line, pos)
if type(line) ~= 'string' then error '#1 must be a string' end if type(line) ~= 'string' then error '#1 must be a string' end
if type(pos) ~= 'number' then error '#2 must be a number' end if type(pos) ~= 'number' then error '#2 must be a number' end

View File

@ -0,0 +1,53 @@
local function curry(f)
return function (x) return function (y) return f(x,y) end end
end
local flags = {}
local function flag(f, description)
flags[f] = {description}
end
local addflag = curry(flag)
addflag '-A' 'Ask for password via askpass or $SUDO_ASKPASS'
addflag '-B' 'Ring the bell as part of the password prompt.'
hilbish.complete('command.sudo', function(query, ctx, fields)
table.remove(fields, 1)
local nonflags = table.filter(fields, function(v)
if v == '' then
return false
end
return v:match '^%-' == nil
end)
if #fields == 1 or #nonflags == 0 then
-- complete commands or sudo flags
if query:match ('^%-') then
local compFlags = {}
for flg, flgstuff in pairs(flags) do
if flg:match('^' .. query) then
compFlags[flg] = flgstuff
end
end
local compGroup = {
items = compFlags,
type = 'list'
}
return {compGroup}, query
end
local comps, pfx = hilbish.completion.bins(query, ctx, fields)
local compGroup = {
items = comps,
type = 'grid'
}
return {compGroup}, pfx
end
-- otherwise, get command flags
return hilbish.completion.call('command.' .. fields[2], query, ctx, fields)
end)

View File

@ -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'

View File

@ -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

13
rl.go
View File

@ -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)

View File

@ -11,7 +11,7 @@ var (
// Version info // Version info
var ( var (
ver = "v2.2.2" ver = "v2.2.3"
releaseName = "Poppy" releaseName = "Poppy"
gitCommit string gitCommit string