mirror of https://github.com/Hilbis/Hilbish
Compare commits
9 Commits
9491e3e67f
...
b8e3d4d8e6
Author | SHA1 | Date |
---|---|---|
sammyette | b8e3d4d8e6 | |
sammyette | a3b17c5301 | |
sammyette | f3d11ed5fd | |
sammyette | 7464702052 | |
sammyette | 75c3b95517 | |
sammyette | 50bfa14fe8 | |
sammyette | 1af361fcc8 | |
sammyette | 3f06cbd208 | |
sammyette | b09be2e131 |
29
CHANGELOG.md
29
CHANGELOG.md
|
@ -1,5 +1,33 @@
|
||||||
# 🎀 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
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
## [2.2.3] - 2024-04-27
|
## [2.2.3] - 2024-04-27
|
||||||
### Fixed
|
### Fixed
|
||||||
- Highligher and hinter work now, since it was regressed from the previous minor release.
|
- Highligher and hinter work now, since it was regressed from the previous minor release.
|
||||||
|
@ -716,6 +744,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
|
||||||
|
|
90
api.go
90
api.go
|
@ -16,6 +16,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -153,11 +154,62 @@ func unsetVimMode() {
|
||||||
util.SetField(l, hshMod, "vimMode", rt.NilValue)
|
util.SetField(l, hshMod, "vimMode", rt.NilValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
// 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.
|
// TODO: ON BREAKING RELEASE, DO NOT ACCEPT `streams` AS A BOOLEAN.
|
||||||
if err := c.Check1Arg(); err != nil {
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
@ -168,7 +220,7 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var strms *streams
|
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]
|
||||||
|
@ -181,35 +233,31 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, errors.New("bad argument to run (expected boolean or table, got " + tout.TypeName() + ")")
|
return nil, errors.New("bad argument to run (expected boolean or table, got " + tout.TypeName() + ")")
|
||||||
}
|
}
|
||||||
|
|
||||||
var stdoutStream, stderrStream *iolib.File
|
handleStream(luastreams.Get(rt.StringValue("out")), strms, false)
|
||||||
|
handleStream(luastreams.Get(rt.StringValue("err")), strms, true)
|
||||||
|
|
||||||
stdoutstrm := luastreams.Get(rt.StringValue("stdout"))
|
stdinstrm := luastreams.Get(rt.StringValue("input"))
|
||||||
if !stdoutstrm.IsNil() {
|
if !stdinstrm.IsNil() {
|
||||||
f, ok := iolib.ValueToFile(stdoutstrm)
|
ud, ok := stdinstrm.TryUserData()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("bad argument to run streams table (expected file, got " + stdoutstrm.TypeName() + ")")
|
return nil, errors.New("bad type as run stdin stream (expected userdata as either sink or file, got " + stdinstrm.TypeName() + ")")
|
||||||
}
|
}
|
||||||
|
|
||||||
stdoutStream = f
|
val := ud.Value()
|
||||||
|
var varstrm io.Reader
|
||||||
|
if f, ok := val.(*iolib.File); ok {
|
||||||
|
varstrm = f.Handle()
|
||||||
}
|
}
|
||||||
|
|
||||||
stderrstrm := luastreams.Get(rt.StringValue("stderr"))
|
if f, ok := val.(*sink); ok {
|
||||||
if !stderrstrm.IsNil() {
|
varstrm = f.reader
|
||||||
f, ok := iolib.ValueToFile(stderrstrm)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("bad argument to run streams table (expected file, got " + stderrstrm.TypeName() + ")")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stderrStream = f
|
if varstrm == nil {
|
||||||
|
return nil, errors.New("bad type as run stdin stream (expected userdata as either sink or file)")
|
||||||
}
|
}
|
||||||
|
|
||||||
strms = &streams{}
|
strms.stdin = varstrm
|
||||||
if stdoutStream != nil {
|
|
||||||
strms.stdout = stdoutStream.Handle()
|
|
||||||
}
|
|
||||||
|
|
||||||
if stderrStream != nil {
|
|
||||||
strms.stderr = stderrStream.Handle()
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !terminalOut {
|
if !terminalOut {
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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,20 +413,25 @@ 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.
|
||||||
|
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.
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -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,12 @@ 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
|
--- 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
|
||||||
|
|
7
exec.go
7
exec.go
|
@ -283,7 +283,11 @@ func execCommand(cmd string, strms *streams) (io.Writer, io.Writer, error) {
|
||||||
strms.stderr = os.Stderr
|
strms.stderr = os.Stderr
|
||||||
}
|
}
|
||||||
|
|
||||||
interp.StdIO(os.Stdin, strms.stdout, strms.stderr)(runner)
|
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()
|
||||||
|
@ -345,6 +349,7 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
|
|
||||||
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))
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -228,6 +228,11 @@ 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) {
|
func fpipe(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
rf, wf, err := os.Pipe()
|
rf, wf, err := os.Pipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue