mirror of https://github.com/Hilbis/Hilbish
Compare commits
No commits in common. "d808534da64f71aedd0cae90f0c66222106fe723" and "0b07b93de2b401031d5ff7a793b818eb02c25f79" have entirely different histories.
d808534da6
...
0b07b93de2
31
CHANGELOG.md
31
CHANGELOG.md
|
@ -23,26 +23,6 @@ is for everything/anything as opposed to just adding a single command completion
|
||||||
[#122](https://github.com/Rosettea/Hilbish/issues/122)
|
[#122](https://github.com/Rosettea/Hilbish/issues/122)
|
||||||
- `fs.abs(path)` to get absolute path.
|
- `fs.abs(path)` to get absolute path.
|
||||||
- Nature module (`doc nature`)
|
- Nature module (`doc nature`)
|
||||||
- `hilbish.jobs.add(cmdstr, args, execPath)` to add a job to the job table.
|
|
||||||
`cmdstr` would be user input, `args` is the args for the command (includes arg0)
|
|
||||||
and `execPath` is absolute path to command executable
|
|
||||||
- `job.add` hook is thrown when a job is added. acts as a unique hook for
|
|
||||||
jobs
|
|
||||||
- `hilbish.jobs.disown(id)` and `disown` builtin to disown a job. `disown`
|
|
||||||
without arguments will disown the last job.
|
|
||||||
- `hilbish.jobs.last()` returns the last added job.
|
|
||||||
- Job output (stdout/stderr) can now be obtained via the `stdout` and `stderr`
|
|
||||||
fields on a job object.
|
|
||||||
- Documentation for jobs is now available via `doc jobs`.
|
|
||||||
- `hilbish.alias.resolve(cmdstr)` to resolve a command alias.
|
|
||||||
- `hilbish.opts` for shell options. Currently, the only opt is `autocd`.
|
|
||||||
- `hilbish.editor` interface for interacting with the line editor that
|
|
||||||
Hilbish uses.
|
|
||||||
- `hilbish.vim` interface to dynamically get/set vim registers.
|
|
||||||
Example usage: `hilbish.vim.registers['a'] = 'hello'`. You can also
|
|
||||||
get the mode with it via `hilbish.vim.mode`
|
|
||||||
- `hilbish.version` interface for more info about Hilbish's version. This
|
|
||||||
includes git commit, branch, and (new!!) release name.
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- **Breaking Change:** Upgraded to Lua 5.4.
|
- **Breaking Change:** Upgraded to Lua 5.4.
|
||||||
|
@ -53,7 +33,6 @@ This is probably one of (if not the) biggest things in this release.
|
||||||
user input, exit code, and error. User input has been added to the return to
|
user input, exit code, and error. User input has been added to the return to
|
||||||
account for runners wanting to prompt for continued input, and to add it
|
account for runners wanting to prompt for continued input, and to add it
|
||||||
properly to history.
|
properly to history.
|
||||||
- All `fs` module functions which take paths now implicitly expand ~ to home.
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- If in Vim replace mode, input at the end of the line inserts instead of
|
- If in Vim replace mode, input at the end of the line inserts instead of
|
||||||
|
@ -78,16 +57,6 @@ certain color rules.
|
||||||
- Cursor position with CJK characters. ([#145](https://github.com/Rosettea/Hilbish/pull/145))
|
- Cursor position with CJK characters. ([#145](https://github.com/Rosettea/Hilbish/pull/145))
|
||||||
- Files with same name as parent folder in completions getting cut off [#136](https://github.com/Rosettea/Hilbish/issues/136))
|
- Files with same name as parent folder in completions getting cut off [#136](https://github.com/Rosettea/Hilbish/issues/136))
|
||||||
- `hilbish.which` now works with commanders and aliases.
|
- `hilbish.which` now works with commanders and aliases.
|
||||||
- Background jobs no longer take stdin so they do not interfere with shell
|
|
||||||
input.
|
|
||||||
- Completions are fixed in cases where the query/line is an alias alone
|
|
||||||
where it can also resolve to the beginning of command names.
|
|
||||||
(reference [this commit](https://github.com/Rosettea/Hilbish/commit/2790982ad123115c6ddbc5764677fdca27668cea))
|
|
||||||
for explanation.
|
|
||||||
- Jobs now throw `job.done` and set running to false when stopped via
|
|
||||||
Lua `job.stop` function.
|
|
||||||
- Jobs are always started in sh exec handler now instead of only successful start.
|
|
||||||
- SIGTERM is handled properly now, which means stopping jobs and timers.
|
|
||||||
|
|
||||||
## [1.2.0] - 2022-03-17
|
## [1.2.0] - 2022-03-17
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
Note: `job` refers to a job object. YOu can check `doc jobs` for more
|
Note: A `job` is a table with the following keys:
|
||||||
detail.
|
- cmd: command string
|
||||||
|
- running: boolean whether the job is running
|
||||||
|
- id: unique id for the job
|
||||||
|
- pid: process id for the job
|
||||||
|
- exitCode: exit code of the job
|
||||||
|
In ordinary cases you'd prefer to use the id instead of pid. The id is unique to
|
||||||
|
Hilbish and is how you get jobs with the `hilbish.jobs` interface.
|
||||||
|
|
||||||
+ `job.start` -> job > Thrown when a new background job starts.
|
+ `job.start` -> job > Thrown when a new background job starts.
|
||||||
|
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
Hilbish has pretty standard job control. It's missing one or two things,
|
|
||||||
but works well. One thing which is different from other shells
|
|
||||||
(besides Hilbish) itself is the API for jobs, and of course it's in Lua.
|
|
||||||
You can add jobs, stop and delete (disown) them and even get output.
|
|
||||||
|
|
||||||
# Job Interface
|
|
||||||
The job interface refers to `hilbish.jobs`.
|
|
||||||
## Functions
|
|
||||||
(Note that in the list here, they're called from `hilbish.jobs`, so
|
|
||||||
a listing of `foo` would mean `hilbish.jobs.foo`)
|
|
||||||
|
|
||||||
- `all()` -> {jobs}: Returns a table of all jobs.
|
|
||||||
- `last()` -> job: Returns the last added job.
|
|
||||||
- `get(id)` -> job: Get a job by its ID.
|
|
||||||
- `add(cmdstr, args, execPath)` -> job: Adds a new job to the job table.
|
|
||||||
Note that this does not run the command; You have to start it manually.
|
|
||||||
`cmdstr` is the user's input for the job, `args` is a table of arguments
|
|
||||||
for the command. It includes arg0 (don't set it as entry 0 in the table)
|
|
||||||
and `execPath` is an absolute path for the command executable.
|
|
||||||
- `disown(id)`: Removes a job by ID from the job table.
|
|
||||||
|
|
||||||
# Job Object
|
|
||||||
A job object on the Lua side is a table with some functions.
|
|
||||||
On the under side it represents a job in the job table.
|
|
||||||
You can still have a job object for a disowned job,
|
|
||||||
it just won't be *working* anywhere. :^)
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
- `cmd`: command string
|
|
||||||
- `running`: boolean whether the job is running
|
|
||||||
- `id`: unique id for the job
|
|
||||||
- `pid`: process id for the job
|
|
||||||
- `exitCode`: exit code of the job
|
|
||||||
In ordinary cases you'd prefer to use the `id` instead of `pid`.
|
|
||||||
The `id` is unique to Hilbish and is how you get jobs with the
|
|
||||||
`hilbish.jobs` interface. It may also not describe the job entirely.
|
|
||||||
|
|
||||||
## Functions
|
|
||||||
- `stop()`: Stops the job.
|
|
||||||
- `start()`: Starts the job.
|
|
|
@ -1,34 +0,0 @@
|
||||||
Lunacolors is an ANSI color/styling library for Lua. It is included
|
|
||||||
by default in standard Hilbish distributions to provide easy styling
|
|
||||||
for things like prompts and text.
|
|
||||||
|
|
||||||
For simple usage, a single color or style is enough. For example,
|
|
||||||
you can just use `lunacolors.blue 'Hello world'` and that'll return
|
|
||||||
blue text which you can print. This includes styles like bold,
|
|
||||||
underline, etc.
|
|
||||||
|
|
||||||
In other usage, you may want to use a format string instead of having
|
|
||||||
multiple nested functions for different styles. This is where the format
|
|
||||||
function comes in. You can used named keywords to style a section of text.
|
|
||||||
|
|
||||||
The list of arguments are:
|
|
||||||
Colors:
|
|
||||||
- black
|
|
||||||
- red
|
|
||||||
- green
|
|
||||||
- yellow
|
|
||||||
- blue
|
|
||||||
- magenta
|
|
||||||
- cyan
|
|
||||||
- white
|
|
||||||
Styles:
|
|
||||||
- bold
|
|
||||||
- dim
|
|
||||||
- italic
|
|
||||||
- underline
|
|
||||||
- invert
|
|
||||||
|
|
||||||
For the colors, there are background and bright variants. The background
|
|
||||||
color variants have a suffix of `Bg` and bright has a prefix of `bright`.
|
|
||||||
Note that appropriate camel casing has to be applied to them. So bright
|
|
||||||
blue would be `brightBlue` and background cyan would be `cyanBg`.
|
|
70
exec.go
70
exec.go
|
@ -239,7 +239,7 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
|
||||||
|
|
||||||
stmtStr := buf.String()
|
stmtStr := buf.String()
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
jobs.add(stmtStr, []string{}, "")
|
jobs.add(stmtStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
interp.ExecHandler(execHandle(bg))(runner)
|
interp.ExecHandler(execHandle(bg))(runner)
|
||||||
|
@ -357,15 +357,13 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
Stderr: hc.Stderr,
|
Stderr: hc.Stderr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = cmd.Start()
|
||||||
var j *job
|
var j *job
|
||||||
if bg {
|
if bg {
|
||||||
j = jobs.getLatest()
|
j = jobs.getLatest()
|
||||||
j.setHandle(&cmd)
|
j.setHandle(cmd.Process)
|
||||||
err = j.start()
|
j.start(cmd.Process.Pid)
|
||||||
} else {
|
|
||||||
err = cmd.Start()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if done := ctx.Done(); done != nil {
|
if done := ctx.Done(); done != nil {
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -390,8 +388,35 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
err = cmd.Wait()
|
err = cmd.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
exit := handleExecErr(err)
|
var exit uint8
|
||||||
|
switch x := err.(type) {
|
||||||
|
case *exec.ExitError:
|
||||||
|
// started, but errored - default to 1 if OS
|
||||||
|
// doesn't have exit statuses
|
||||||
|
if status, ok := x.Sys().(syscall.WaitStatus); ok {
|
||||||
|
if status.Signaled() {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
exit = uint8(128 + status.Signal())
|
||||||
|
goto end
|
||||||
|
}
|
||||||
|
exit = uint8(status.ExitStatus())
|
||||||
|
goto end
|
||||||
|
}
|
||||||
|
exit = 1
|
||||||
|
goto end
|
||||||
|
case *exec.Error:
|
||||||
|
// did not start
|
||||||
|
fmt.Fprintf(hc.Stderr, "%v\n", err)
|
||||||
|
exit = 127
|
||||||
|
goto end
|
||||||
|
case nil:
|
||||||
|
goto end
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
end:
|
||||||
if bg {
|
if bg {
|
||||||
j.exitCode = int(exit)
|
j.exitCode = int(exit)
|
||||||
j.finish()
|
j.finish()
|
||||||
|
@ -400,35 +425,6 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleExecErr(err error) (exit uint8) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
|
|
||||||
switch x := err.(type) {
|
|
||||||
case *exec.ExitError:
|
|
||||||
// started, but errored - default to 1 if OS
|
|
||||||
// doesn't have exit statuses
|
|
||||||
if status, ok := x.Sys().(syscall.WaitStatus); ok {
|
|
||||||
if status.Signaled() {
|
|
||||||
if ctx.Err() != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
exit = uint8(128 + status.Signal())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
exit = uint8(status.ExitStatus())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
exit = 1
|
|
||||||
return
|
|
||||||
case *exec.Error:
|
|
||||||
// did not start
|
|
||||||
//fmt.Fprintf(hc.Stderr, "%v\n", err)
|
|
||||||
exit = 127
|
|
||||||
default: 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) 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" {
|
||||||
|
|
|
@ -4,13 +4,8 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var bgProcAttr *syscall.SysProcAttr = &syscall.SysProcAttr{
|
|
||||||
Setpgid: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
func findExecutable(path string, inPath, dirs bool) error {
|
func findExecutable(path string, inPath, dirs bool) error {
|
||||||
f, err := os.Stat(path)
|
f, err := os.Stat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -5,13 +5,8 @@ package main
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var bgProcAttr *syscall.SysProcAttr = &syscall.SysProcAttr{
|
|
||||||
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
|
|
||||||
}
|
|
||||||
|
|
||||||
func findExecutable(path string, inPath, dirs bool) error {
|
func findExecutable(path string, inPath, dirs bool) error {
|
||||||
nameExt := filepath.Ext(path)
|
nameExt := filepath.Ext(path)
|
||||||
pathExts := filepath.SplitList(os.Getenv("PATHEXT"))
|
pathExts := filepath.SplitList(os.Getenv("PATHEXT"))
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -29,4 +29,4 @@ replace github.com/maxlandon/readline => ./readline
|
||||||
|
|
||||||
replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10
|
replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10
|
||||||
|
|
||||||
replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3
|
replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20220419183026-6d22d6fec5ac
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -1,7 +1,5 @@
|
||||||
github.com/Rosettea/golua v0.0.0-20220419183026-6d22d6fec5ac h1:dtXrgjch8PQyf7C90anZUquB5U3dr8AcMGJofeuirrI=
|
github.com/Rosettea/golua v0.0.0-20220419183026-6d22d6fec5ac h1:dtXrgjch8PQyf7C90anZUquB5U3dr8AcMGJofeuirrI=
|
||||||
github.com/Rosettea/golua v0.0.0-20220419183026-6d22d6fec5ac/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
github.com/Rosettea/golua v0.0.0-20220419183026-6d22d6fec5ac/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
||||||
github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3 h1:I/wWr40FFLFF9pbT3wLb1FAEZhKb/hUWE+nJ5uHBK2g=
|
|
||||||
github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
|
||||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e h1:P2XupP8SaylWaudD1DqbWtZ3mIa8OsE9635LmR+Q+lg=
|
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e h1:P2XupP8SaylWaudD1DqbWtZ3mIa8OsE9635LmR+Q+lg=
|
||||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs=
|
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs=
|
||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||||
|
|
187
job.go
187
job.go
|
@ -1,13 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"os"
|
||||||
|
|
||||||
"hilbish/util"
|
"hilbish/util"
|
||||||
|
|
||||||
|
@ -22,59 +17,18 @@ type job struct {
|
||||||
id int
|
id int
|
||||||
pid int
|
pid int
|
||||||
exitCode int
|
exitCode int
|
||||||
once bool
|
proc *os.Process
|
||||||
args []string
|
|
||||||
// save path for a few reasons, one being security (lmao) while the other
|
|
||||||
// would just be so itll be the same binary command always (path changes)
|
|
||||||
path string
|
|
||||||
handle *exec.Cmd
|
|
||||||
cmdout io.Writer
|
|
||||||
cmderr io.Writer
|
|
||||||
stdout *bytes.Buffer
|
|
||||||
stderr *bytes.Buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *job) start() error {
|
func (j *job) start(pid int) {
|
||||||
if j.handle == nil || j.once {
|
j.pid = pid
|
||||||
// cmd cant be reused so make a new one
|
|
||||||
cmd := exec.Cmd{
|
|
||||||
Path: j.path,
|
|
||||||
Args: j.args,
|
|
||||||
}
|
|
||||||
j.setHandle(&cmd)
|
|
||||||
}
|
|
||||||
// bgProcAttr is defined in execfile_<os>.go, it holds a procattr struct
|
|
||||||
// in a simple explanation, it makes signals from hilbish (sigint)
|
|
||||||
// not go to it (child process)
|
|
||||||
j.handle.SysProcAttr = bgProcAttr
|
|
||||||
// reset output buffers
|
|
||||||
j.stdout.Reset()
|
|
||||||
j.stderr.Reset()
|
|
||||||
// make cmd write to both standard output and output buffers for lua access
|
|
||||||
j.handle.Stdout = io.MultiWriter(j.cmdout, j.stdout)
|
|
||||||
j.handle.Stderr = io.MultiWriter(j.cmderr, j.stderr)
|
|
||||||
|
|
||||||
if !j.once {
|
|
||||||
j.once = true
|
|
||||||
}
|
|
||||||
|
|
||||||
err := j.handle.Start()
|
|
||||||
proc := j.getProc()
|
|
||||||
|
|
||||||
j.pid = proc.Pid
|
|
||||||
j.running = true
|
j.running = true
|
||||||
|
|
||||||
hooks.Em.Emit("job.start", j.lua())
|
hooks.Em.Emit("job.start", j.lua())
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *job) stop() {
|
func (j *job) stop() {
|
||||||
// finish will be called in exec handle
|
// finish will be called in exec handle
|
||||||
proc := j.getProc()
|
j.proc.Kill()
|
||||||
if proc != nil {
|
|
||||||
proc.Kill()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *job) finish() {
|
func (j *job) finish() {
|
||||||
|
@ -82,35 +36,13 @@ func (j *job) finish() {
|
||||||
hooks.Em.Emit("job.done", j.lua())
|
hooks.Em.Emit("job.done", j.lua())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *job) wait() {
|
func (j *job) setHandle(handle *os.Process) {
|
||||||
j.handle.Wait()
|
j.proc = handle
|
||||||
}
|
|
||||||
|
|
||||||
func (j *job) setHandle(handle *exec.Cmd) {
|
|
||||||
j.handle = handle
|
|
||||||
j.args = handle.Args
|
|
||||||
j.path = handle.Path
|
|
||||||
if handle.Stdout != nil {
|
|
||||||
j.cmdout = handle.Stdout
|
|
||||||
}
|
|
||||||
if handle.Stderr != nil {
|
|
||||||
j.cmderr = handle.Stderr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *job) getProc() *os.Process {
|
|
||||||
handle := j.handle
|
|
||||||
if handle != nil {
|
|
||||||
return handle.Process
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *job) lua() rt.Value {
|
func (j *job) lua() rt.Value {
|
||||||
jobFuncs := map[string]util.LuaExport{
|
jobFuncs := map[string]util.LuaExport{
|
||||||
"stop": {j.luaStop, 0, false},
|
"stop": {j.luaStop, 0, false},
|
||||||
"start": {j.luaStart, 0, false},
|
|
||||||
}
|
}
|
||||||
luaJob := rt.NewTable()
|
luaJob := rt.NewTable()
|
||||||
util.SetExports(l, luaJob, jobFuncs)
|
util.SetExports(l, luaJob, jobFuncs)
|
||||||
|
@ -120,23 +52,10 @@ func (j *job) lua() rt.Value {
|
||||||
luaJob.Set(rt.StringValue("id"), rt.IntValue(int64(j.id)))
|
luaJob.Set(rt.StringValue("id"), rt.IntValue(int64(j.id)))
|
||||||
luaJob.Set(rt.StringValue("pid"), rt.IntValue(int64(j.pid)))
|
luaJob.Set(rt.StringValue("pid"), rt.IntValue(int64(j.pid)))
|
||||||
luaJob.Set(rt.StringValue("exitCode"), rt.IntValue(int64(j.exitCode)))
|
luaJob.Set(rt.StringValue("exitCode"), rt.IntValue(int64(j.exitCode)))
|
||||||
luaJob.Set(rt.StringValue("stdout"), rt.StringValue(string(j.stdout.Bytes())))
|
|
||||||
luaJob.Set(rt.StringValue("stderr"), rt.StringValue(string(j.stderr.Bytes())))
|
|
||||||
|
|
||||||
return rt.TableValue(luaJob)
|
return rt.TableValue(luaJob)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *job) luaStart(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
if !j.running {
|
|
||||||
err := j.start()
|
|
||||||
exit := handleExecErr(err)
|
|
||||||
j.exitCode = int(exit)
|
|
||||||
j.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Next(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *job) luaStop(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (j *job) luaStop(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if j.running {
|
if j.running {
|
||||||
j.stop()
|
j.stop()
|
||||||
|
@ -160,7 +79,7 @@ func newJobHandler() *jobHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jobHandler) add(cmd string, args []string, path string) *job {
|
func (j *jobHandler) add(cmd string) *job {
|
||||||
j.mu.Lock()
|
j.mu.Lock()
|
||||||
defer j.mu.Unlock()
|
defer j.mu.Unlock()
|
||||||
|
|
||||||
|
@ -169,15 +88,8 @@ func (j *jobHandler) add(cmd string, args []string, path string) *job {
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
running: false,
|
running: false,
|
||||||
id: j.latestID,
|
id: j.latestID,
|
||||||
args: args,
|
|
||||||
path: path,
|
|
||||||
cmdout: os.Stdout,
|
|
||||||
cmderr: os.Stderr,
|
|
||||||
stdout: &bytes.Buffer{},
|
|
||||||
stderr: &bytes.Buffer{},
|
|
||||||
}
|
}
|
||||||
j.jobs[j.latestID] = jb
|
j.jobs[j.latestID] = jb
|
||||||
hooks.Em.Emit("job.add", jb.lua())
|
|
||||||
|
|
||||||
return jb
|
return jb
|
||||||
}
|
}
|
||||||
|
@ -189,41 +101,11 @@ func (j *jobHandler) getLatest() *job {
|
||||||
return j.jobs[j.latestID]
|
return j.jobs[j.latestID]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jobHandler) disown(id int) error {
|
|
||||||
j.mu.RLock()
|
|
||||||
if j.jobs[id] == nil {
|
|
||||||
return errors.New("job doesnt exist")
|
|
||||||
}
|
|
||||||
j.mu.RUnlock()
|
|
||||||
|
|
||||||
j.mu.Lock()
|
|
||||||
delete(j.jobs, id)
|
|
||||||
j.mu.Unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *jobHandler) stopAll() {
|
|
||||||
j.mu.RLock()
|
|
||||||
defer j.mu.RUnlock()
|
|
||||||
|
|
||||||
for _, jb := range j.jobs {
|
|
||||||
// on exit, unix shell should send sighup to all jobs
|
|
||||||
if jb.running {
|
|
||||||
proc := jb.getProc()
|
|
||||||
proc.Signal(syscall.SIGHUP)
|
|
||||||
jb.wait() // waits for program to exit due to sighup
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table {
|
func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table {
|
||||||
jobFuncs := map[string]util.LuaExport{
|
jobFuncs := map[string]util.LuaExport{
|
||||||
"all": {j.luaAllJobs, 0, false},
|
"all": {j.luaAllJobs, 0, false},
|
||||||
"last": {j.luaLastJob, 0, false},
|
|
||||||
"get": {j.luaGetJob, 1, false},
|
"get": {j.luaGetJob, 1, false},
|
||||||
"add": {j.luaAddJob, 3, false},
|
"add": {j.luaAddJob, 1, false},
|
||||||
"disown": {j.luaDisownJob, 1, false},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
luaJob := rt.NewTable()
|
luaJob := rt.NewTable()
|
||||||
|
@ -253,30 +135,18 @@ func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.CheckNArgs(3); err != nil {
|
j.mu.RLock()
|
||||||
|
defer j.mu.RUnlock()
|
||||||
|
|
||||||
|
if err := c.Check1Arg(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cmd, err := c.StringArg(0)
|
cmd, err := c.StringArg(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
largs, err := c.TableArg(1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
execPath, err := c.StringArg(2)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var args []string
|
jb := j.add(cmd)
|
||||||
util.ForEach(largs, func(k rt.Value, v rt.Value) {
|
|
||||||
if v.Type() == rt.StringType {
|
|
||||||
args = append(args, v.AsString())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
jb := j.add(cmd, args, execPath)
|
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, jb.lua()), nil
|
return c.PushingNext1(t.Runtime, jb.lua()), nil
|
||||||
}
|
}
|
||||||
|
@ -292,32 +162,3 @@ func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, rt.TableValue(jobTbl)), nil
|
return c.PushingNext1(t.Runtime, rt.TableValue(jobTbl)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
if err := c.Check1Arg(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
jobID, err := c.IntArg(0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = j.disown(int(jobID))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Next(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *jobHandler) luaLastJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
j.mu.RLock()
|
|
||||||
defer j.mu.RUnlock()
|
|
||||||
|
|
||||||
job := j.jobs[j.latestID]
|
|
||||||
if job == nil { // incase we dont have any jobs yet
|
|
||||||
return c.Next(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, job.lua()), nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit d60cd77c73875b5bb55e5a2fdc30bae01a7ac499
|
Subproject commit b362397a83e4516415c809c7d690b52e79a95f6e
|
15
main.go
15
main.go
|
@ -221,8 +221,6 @@ input:
|
||||||
}
|
}
|
||||||
fmt.Printf("\u001b[7m∆\u001b[0m" + strings.Repeat(" ", termwidth - 1) + "\r")
|
fmt.Printf("\u001b[7m∆\u001b[0m" + strings.Repeat(" ", termwidth - 1) + "\r")
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func continuePrompt(prev string) (string, error) {
|
func continuePrompt(prev string) (string, error) {
|
||||||
|
@ -296,16 +294,13 @@ func contains(s []string, e string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func exit(code int) {
|
func exit(code int) {
|
||||||
jobs.stopAll()
|
// wait for all timers to finish before exiting
|
||||||
|
for {
|
||||||
// wait for all timers to finish before exiting.
|
if timers.running == 0 {
|
||||||
// only do that when not interactive
|
|
||||||
if !interactive {
|
|
||||||
timers.wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getVersion() string {
|
func getVersion() string {
|
||||||
v := strings.Builder{}
|
v := strings.Builder{}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
local commander = require 'commander'
|
|
||||||
|
|
||||||
commander.register('disown', function(args)
|
|
||||||
if #hilbish.jobs.all() == 0 then
|
|
||||||
print 'disown: no current job'
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local id
|
|
||||||
if #args < 0 then
|
|
||||||
id = tonumber(args[1])
|
|
||||||
if not id then
|
|
||||||
print 'disown: invalid id for job'
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
id = hilbish.jobs.last().id
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok = pcall(hilbish.jobs.disown, id)
|
|
||||||
if not ok then
|
|
||||||
print 'disown: job does not exist'
|
|
||||||
return 2
|
|
||||||
end
|
|
||||||
end)
|
|
|
@ -4,4 +4,3 @@ require 'nature.commands.cdr'
|
||||||
require 'nature.commands.doc'
|
require 'nature.commands.doc'
|
||||||
require 'nature.commands.exit'
|
require 'nature.commands.exit'
|
||||||
require 'nature.commands.guide'
|
require 'nature.commands.guide'
|
||||||
require 'nature.commands.disown'
|
|
||||||
|
|
|
@ -10,13 +10,20 @@ import (
|
||||||
|
|
||||||
func handleSignals() {
|
func handleSignals() {
|
||||||
c := make(chan os.Signal)
|
c := make(chan os.Signal)
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGWINCH, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGQUIT)
|
signal.Notify(c, os.Interrupt, syscall.SIGWINCH, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGQUIT)
|
||||||
|
|
||||||
for s := range c {
|
for s := range c {
|
||||||
switch s {
|
switch s {
|
||||||
case os.Interrupt: hooks.Em.Emit("signal.sigint")
|
case os.Interrupt:
|
||||||
case syscall.SIGTERM: exit(0)
|
hooks.Em.Emit("signal.sigint")
|
||||||
case syscall.SIGWINCH: hooks.Em.Emit("signal.resize")
|
if !running && interactive {
|
||||||
|
lr.ClearInput()
|
||||||
|
}
|
||||||
|
case syscall.SIGWINCH:
|
||||||
|
hooks.Em.Emit("signal.resize")
|
||||||
|
if !running && interactive {
|
||||||
|
lr.Resize()
|
||||||
|
}
|
||||||
case syscall.SIGUSR1: hooks.Em.Emit("signal.sigusr1")
|
case syscall.SIGUSR1: hooks.Em.Emit("signal.sigusr1")
|
||||||
case syscall.SIGUSR2: hooks.Em.Emit("signal.sigusr2")
|
case syscall.SIGUSR2: hooks.Em.Emit("signal.sigusr2")
|
||||||
}
|
}
|
||||||
|
|
6
timer.go
6
timer.go
|
@ -25,7 +25,7 @@ type timer struct{
|
||||||
fun *rt.Closure
|
fun *rt.Closure
|
||||||
th *timerHandler
|
th *timerHandler
|
||||||
ticker *time.Ticker
|
ticker *time.Ticker
|
||||||
channel chan struct{}
|
channel chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *timer) start() error {
|
func (t *timer) start() error {
|
||||||
|
@ -35,7 +35,6 @@ func (t *timer) start() error {
|
||||||
|
|
||||||
t.running = true
|
t.running = true
|
||||||
t.th.running++
|
t.th.running++
|
||||||
t.th.wg.Add(1)
|
|
||||||
t.ticker = time.NewTicker(t.dur)
|
t.ticker = time.NewTicker(t.dur)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -66,10 +65,9 @@ func (t *timer) stop() error {
|
||||||
return errors.New("timer not running")
|
return errors.New("timer not running")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.channel <- struct{}{}
|
t.channel <- true
|
||||||
t.running = false
|
t.running = false
|
||||||
t.th.running--
|
t.th.running--
|
||||||
t.th.wg.Done()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
var timers *timerHandler
|
var timers *timerHandler
|
||||||
type timerHandler struct {
|
type timerHandler struct {
|
||||||
mu *sync.RWMutex
|
mu *sync.RWMutex
|
||||||
wg *sync.WaitGroup
|
|
||||||
timers map[int]*timer
|
timers map[int]*timer
|
||||||
latestID int
|
latestID int
|
||||||
running int
|
running int
|
||||||
|
@ -23,14 +22,9 @@ func newTimerHandler() *timerHandler {
|
||||||
timers: make(map[int]*timer),
|
timers: make(map[int]*timer),
|
||||||
latestID: 0,
|
latestID: 0,
|
||||||
mu: &sync.RWMutex{},
|
mu: &sync.RWMutex{},
|
||||||
wg: &sync.WaitGroup{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *timerHandler) wait() {
|
|
||||||
th.wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (th *timerHandler) create(typ timerType, dur time.Duration, fun *rt.Closure) *timer {
|
func (th *timerHandler) create(typ timerType, dur time.Duration, fun *rt.Closure) *timer {
|
||||||
th.mu.Lock()
|
th.mu.Lock()
|
||||||
defer th.mu.Unlock()
|
defer th.mu.Unlock()
|
||||||
|
@ -40,7 +34,7 @@ func (th *timerHandler) create(typ timerType, dur time.Duration, fun *rt.Closure
|
||||||
typ: typ,
|
typ: typ,
|
||||||
fun: fun,
|
fun: fun,
|
||||||
dur: dur,
|
dur: dur,
|
||||||
channel: make(chan struct{}, 1),
|
channel: make(chan bool, 1),
|
||||||
th: th,
|
th: th,
|
||||||
id: th.latestID,
|
id: th.latestID,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue