Compare commits

...

4 Commits

Author SHA1 Message Date
TorchedSammy 1fd99a78cc
docs: add docs and changelogs relating to jobs 2022-05-21 20:33:05 -04:00
TorchedSammy e50ee0b511
fix!: make exec path in job add explicit in lua side 2022-05-21 20:11:15 -04:00
TorchedSammy f43ff7d03c
chore: fix comments 2022-05-21 19:58:41 -04:00
TorchedSammy 3bcff3e350
feat: add job output 2022-05-21 19:55:05 -04:00
4 changed files with 89 additions and 25 deletions

View File

@ -23,6 +23,17 @@ is for everything/anything as opposed to just adding a single command completion
[#122](https://github.com/Rosettea/Hilbish/issues/122)
- `fs.abs(path)` to get absolute path.
- 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`.
### Changed
- **Breaking Change:** Upgraded to Lua 5.4.
@ -57,6 +68,8 @@ certain color rules.
- 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))
- `hilbish.which` now works with commanders and aliases.
- Background jobs no longer take stdin so they do not interfere with shell
input.
## [1.2.0] - 2022-03-17
### Added

View File

@ -1,11 +1,5 @@
Note: A `job` is a table with the following keys:
- 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.
Note: `job` refers to a job object. YOu can check `doc jobs` for more
detail.
+ `job.start` -> job > Thrown when a new background job starts.

40
docs/jobs.txt 100644
View File

@ -0,0 +1,40 @@
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.

51
job.go
View File

@ -1,6 +1,7 @@
package main
import (
"bytes"
"errors"
"io"
"os"
@ -27,9 +28,10 @@ type job struct {
// would just be so itll be the same binary command always (path changes)
path string
handle *exec.Cmd
stdin io.Reader
stdout io.Writer
stderr io.Writer
cmdout io.Writer
cmderr io.Writer
stdout *bytes.Buffer
stderr *bytes.Buffer
}
func (j *job) start() error {
@ -38,13 +40,19 @@ func (j *job) start() error {
cmd := exec.Cmd{
Path: j.path,
Args: j.args,
Stdin: j.stdin,
Stdout: j.stdout,
Stderr: j.stderr,
}
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
@ -82,9 +90,12 @@ func (j *job) setHandle(handle *exec.Cmd) {
j.handle = handle
j.args = handle.Args
j.path = handle.Path
j.stdin = handle.Stdin
j.stdout = handle.Stdout
j.stderr = handle.Stderr
if handle.Stdout != nil {
j.cmdout = handle.Stdout
}
if handle.Stderr != nil {
j.cmderr = handle.Stderr
}
}
func (j *job) getProc() *os.Process {
@ -109,6 +120,8 @@ func (j *job) lua() rt.Value {
luaJob.Set(rt.StringValue("id"), rt.IntValue(int64(j.id)))
luaJob.Set(rt.StringValue("pid"), rt.IntValue(int64(j.pid)))
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)
}
@ -158,9 +171,10 @@ func (j *jobHandler) add(cmd string, args []string, path string) *job {
id: j.latestID,
args: args,
path: path,
stdin: os.Stdin,
stdout: os.Stdout,
stderr: os.Stderr,
cmdout: os.Stdout,
cmderr: os.Stderr,
stdout: &bytes.Buffer{},
stderr: &bytes.Buffer{},
}
j.jobs[j.latestID] = jb
hooks.Em.Emit("job.add", jb.lua())
@ -208,7 +222,7 @@ func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table {
"all": {j.luaAllJobs, 0, false},
"last": {j.luaLastJob, 0, false},
"get": {j.luaGetJob, 1, false},
"add": {j.luaAddJob, 2, false},
"add": {j.luaAddJob, 3, false},
"disown": {j.luaDisownJob, 1, false},
}
@ -239,7 +253,7 @@ 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) {
if err := c.CheckNArgs(2); err != nil {
if err := c.CheckNArgs(3); err != nil {
return nil, err
}
cmd, err := c.StringArg(0)
@ -250,6 +264,10 @@ func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err != nil {
return nil, err
}
execPath, err := c.StringArg(2)
if err != nil {
return nil, err
}
var args []string
util.ForEach(largs, func(k rt.Value, v rt.Value) {
@ -257,8 +275,8 @@ func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
args = append(args, v.AsString())
}
})
// TODO: change to lookpath for args[0]
jb := j.add(cmd, args, args[0])
jb := j.add(cmd, args, execPath)
return c.PushingNext1(t.Runtime, jb.lua()), nil
}
@ -303,4 +321,3 @@ func (j *jobHandler) luaLastJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, job.lua()), nil
}