Compare commits

..

No commits in common. "d808534da64f71aedd0cae90f0c66222106fe723" and "0b07b93de2b401031d5ff7a793b818eb02c25f79" have entirely different histories.

17 changed files with 76 additions and 382 deletions

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

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

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

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

@ -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,15 +294,12 @@ 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 {

View File

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

View File

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

View File

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

View File

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

View File

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