Compare commits

..

No commits in common. "f97a04179de41bdaa7586178f1ebae5bf10959c9" and "c87fbe2b99c2e7bc65fc09b99639e3558a9ce0a6" have entirely different histories.

15 changed files with 37 additions and 243 deletions

View File

@ -4,11 +4,9 @@
### Added ### Added
- Documented custom userdata types (Job and Timer Objects) - Documented custom userdata types (Job and Timer Objects)
- Coming with fix is also adding the return types for some functions that were missing it - Coming with fix is also adding the return types for some functions that were missing it
- Added a dedicated input and dedicated outputs for commanders.
### Fixed ### Fixed
- `hilbish.which` not working correctly with aliases - `hilbish.which` not working correctly with aliases
- Commanders not being able to pipe with commands or any related operator.
## [2.0.1] - 2022-12-28 ## [2.0.1] - 2022-12-28
### Fixed ### Fixed

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2021-2023 Rosettea Copyright (c) 2022 Rosettea
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -414,14 +414,7 @@ func main() {
f, _ := os.Create(docPath) f, _ := os.Create(docPath)
f.WriteString(fmt.Sprintf(header, modOrIface, modname, modu.ShortDescription)) f.WriteString(fmt.Sprintf(header, modOrIface, modname, modu.ShortDescription))
typeTag, _ := regexp.Compile(`@\w+`) f.WriteString(fmt.Sprintf("## Introduction\n%s\n\n", modu.Description))
modDescription := typeTag.ReplaceAllStringFunc(strings.Replace(modu.Description, "<", `\<`, -1), func(typ string) string {
typName := typ[1:]
typLookup := typeTable[strings.ToLower(typName)]
linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s/#%s", typLookup[0], typLookup[0] + "." + typLookup[1], strings.ToLower(typName))
return fmt.Sprintf(`<a href="%s" style="text-decoration: none;">%s</a>`, linkedTyp, typName)
})
f.WriteString(fmt.Sprintf("## Introduction\n%s\n\n", modDescription))
if len(modu.Fields) != 0 { if len(modu.Fields) != 0 {
f.WriteString("## Interface fields\n") f.WriteString("## Interface fields\n")
for _, dps := range modu.Fields { for _, dps := range modu.Fields {
@ -442,6 +435,7 @@ func main() {
} }
if len(modu.Docs) != 0 { if len(modu.Docs) != 0 {
typeTag, _ := regexp.Compile(`@\w+`)
f.WriteString("## Functions\n") f.WriteString("## Functions\n")
for _, dps := range modu.Docs { for _, dps := range modu.Docs {
if dps.IsMember { if dps.IsMember {
@ -481,6 +475,8 @@ func main() {
} }
} }
f.WriteString("\n") f.WriteString("\n")
typeTag, _ := regexp.Compile(`@\w+`)
f.WriteString("### Methods\n") f.WriteString("### Methods\n")
for _, dps := range modu.Docs { for _, dps := range modu.Docs {
if !dps.IsMember { if !dps.IsMember {

View File

@ -8,41 +8,7 @@ menu:
--- ---
## Introduction ## Introduction
Commander is a library for writing custom commands in Lua. Commander is a library for writing custom commands in Lua.
In order to make it easier to write commands for Hilbish,
not require separate scripts and to be able to use in a config,
the Commander library exists. This is like a very simple wrapper
that works with Hilbish for writing commands. Example:
```lua
local commander = require 'commander'
commander.register('hello', function(args, sinks)
sinks.out:writeln 'Hello world!'
end)
```
In this example, a command with the name of `hello` is created
that will print `Hello world!` to output. One question you may
have is: What is the `sinks` parameter?
A sink is a writable/readable pipe, or you can imagine a Lua
file. It's used in this case to write to the proper output,
incase a user either pipes to another command or redirects somewhere else.
So, the `sinks` parameter is a table containing 3 sinks:
`in`, `out`, and `err`.
- `in` is the standard input. You can read from this sink
to get user input. (**This is currently unimplemented.**)
- `out` is standard output. This is usually where text meant for
output should go.
- `err` is standard error. This sink is for writing errors, as the
name would suggest.
A sink has 2 methods:
- `write(str)` will write to the sink.
- `writeln(str)` will write to the sink with a newline at the end.
## Functions ## Functions
### deregister(name) ### deregister(name)

13
exec.go
View File

@ -323,18 +323,8 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str)) luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str))
} }
hc := interp.HandlerCtx(ctx)
if commands[args[0]] != nil { if commands[args[0]] != nil {
stdin := newSinkInput(hc.Stdin) luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs))
stdout := newSinkOutput(hc.Stdout)
stderr := newSinkOutput(hc.Stderr)
sinks := rt.NewTable()
sinks.Set(rt.StringValue("in"), rt.UserDataValue(stdin.ud))
sinks.Set(rt.StringValue("out"), rt.UserDataValue(stdout.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))
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)
@ -374,6 +364,7 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
killTimeout := 2 * time.Second killTimeout := 2 * time.Second
// from here is basically copy-paste of the default exec handler from // from here is basically copy-paste of the default exec handler from
// sh/interp but with our job handling // sh/interp but with our job handling
hc := interp.HandlerCtx(ctx)
path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0]) path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0])
if err != nil { if err != nil {
fmt.Fprintln(hc.Stderr, err) fmt.Fprintln(hc.Stderr, err)

View File

@ -1,40 +1,5 @@
// library for custom commands // library for custom commands
/* // Commander is a library for writing custom commands in Lua.
Commander is a library for writing custom commands in Lua.
In order to make it easier to write commands for Hilbish,
not require separate scripts and to be able to use in a config,
the Commander library exists. This is like a very simple wrapper
that works with Hilbish for writing commands. Example:
```lua
local commander = require 'commander'
commander.register('hello', function(args, sinks)
sinks.out:writeln 'Hello world!'
end)
```
In this example, a command with the name of `hello` is created
that will print `Hello world!` to output. One question you may
have is: What is the `sinks` parameter?
A sink is a writable/readable pipe, or you can imagine a Lua
file. It's used in this case to write to the proper output,
incase a user either pipes to another command or redirects somewhere else.
So, the `sinks` parameter is a table containing 3 sinks:
`in`, `out`, and `err`.
- `in` is the standard input. You can read from this sink
to get user input. (**This is currently unimplemented.**)
- `out` is standard output. This is usually where text meant for
output should go.
- `err` is standard error. This sink is for writing errors, as the
name would suggest.
A sink has 2 methods:
- `write(str)` will write to the sink.
- `writeln(str)` will write to the sink with a newline at the end.
*/
package commander package commander
import ( import (

1
lua.go
View File

@ -23,7 +23,6 @@ func luaInit() {
MessageHandler: debuglib.Traceback, MessageHandler: debuglib.Traceback,
}) })
lib.LoadAll(l) lib.LoadAll(l)
setupSinkType(l)
lib.LoadLibs(l, hilbishLoader) lib.LoadLibs(l, hilbishLoader)
// yes this is stupid, i know // yes this is stupid, i know

View File

@ -1,15 +1,15 @@
local commander = require 'commander' local commander = require 'commander'
commander.register('bg', function(_, sinks) commander.register('bg', function()
local job = hilbish.jobs.last() local job = hilbish.jobs.last()
if not job then if not job then
sinks.out:writeln 'bg: no last job' print 'bg: no last job'
return 1 return 1
end end
local err = job.background() local err = job.background()
if err then if err then
sinks.out:writeln('bg: ' .. err) print('bg: ' .. err)
return 2 return 2
end end
end) end)

View File

@ -1,11 +1,11 @@
local commander = require 'commander' local commander = require 'commander'
local fs = require 'fs' local fs = require 'fs'
commander.register('cat', function(args, sinks) commander.register('cat', function(args)
local exit = 0 local exit = 0
if #args == 0 then if #args == 0 then
sinks.out:writeln [[ print [[
usage: cat [file]...]] usage: cat [file]...]]
end end
@ -13,11 +13,11 @@ usage: cat [file]...]]
local f = io.open(fName) local f = io.open(fName)
if f == nil then if f == nil then
exit = 1 exit = 1
sinks.out:writeln(string.format('cat: %s: no such file or directory', fName)) print(string.format('cat: %s: no such file or directory', fName))
goto continue goto continue
end end
sinks.out:writeln(f:read '*a') io.write(f:read '*a')
::continue:: ::continue::
end end
io.flush() io.flush()

View File

@ -4,16 +4,16 @@ local fs = require 'fs'
local dirs = require 'nature.dirs' local dirs = require 'nature.dirs'
dirs.old = hilbish.cwd() dirs.old = hilbish.cwd()
commander.register('cd', function (args, sinks) commander.register('cd', function (args)
if #args > 1 then if #args > 1 then
sinks.out:writeln("cd: too many arguments") print("cd: too many arguments")
return 1 return 1
end end
local path = args[1] and args[1] or hilbish.home local path = args[1] and args[1] or hilbish.home
if path == '-' then if path == '-' then
path = dirs.old path = dirs.old
sinks.out:writeln(path) print(path)
end end
dirs.setOld(hilbish.cwd()) dirs.setOld(hilbish.cwd())
@ -21,7 +21,7 @@ commander.register('cd', function (args, sinks)
local ok, err = pcall(function() fs.cd(path) end) local ok, err = pcall(function() fs.cd(path) end)
if not ok then if not ok then
sinks.out:writeln(err) print(err)
return 1 return 1
end end
bait.throw('cd', path) bait.throw('cd', path)

View File

@ -3,9 +3,9 @@ local fs = require 'fs'
local lunacolors = require 'lunacolors' local lunacolors = require 'lunacolors'
local dirs = require 'nature.dirs' local dirs = require 'nature.dirs'
commander.register('cdr', function(args, sinks) commander.register('cdr', function(args)
if not args[1] then if not args[1] then
sinks.out:writeln(lunacolors.format [[ print(lunacolors.format [[
cdr: change directory to one which has been recently visied cdr: change directory to one which has been recently visied
usage: cdr <index> usage: cdr <index>
@ -17,21 +17,21 @@ to get a list of recent directories, use {green}{underline}cdr list{reset}]])
if args[1] == 'list' then if args[1] == 'list' then
local recentDirs = dirs.recentDirs local recentDirs = dirs.recentDirs
if #recentDirs == 0 then if #recentDirs == 0 then
sinks.out:writeln 'No directories have been visited.' print 'No directories have been visited.'
return 1 return 1
end end
sinks.out:writeln(table.concat(recentDirs, '\n')) print(table.concat(recentDirs, '\n'))
return return
end end
local index = tonumber(args[1]) local index = tonumber(args[1])
if not index then if not index then
sinks.out:writeln(string.format('Received %s as index, which isn\'t a number.', index)) print(string.format('Received %s as index, which isn\'t a number.', index))
return 1 return 1
end end
if not dirs.recent(index) then if not dirs.recent(index) then
sinks.out:writeln(string.format('No recent directory found at index %s.', index)) print(string.format('No recent directory found at index %s.', index))
return 1 return 1
end end

View File

@ -1,8 +1,8 @@
local commander = require 'commander' local commander = require 'commander'
commander.register('disown', function(args, sinks) commander.register('disown', function(args)
if #hilbish.jobs.all() == 0 then if #hilbish.jobs.all() == 0 then
sinks.out:writeln 'disown: no current job' print 'disown: no current job'
return 1 return 1
end end
@ -10,7 +10,7 @@ commander.register('disown', function(args, sinks)
if #args < 0 then if #args < 0 then
id = tonumber(args[1]) id = tonumber(args[1])
if not id then if not id then
sinks.out:writeln 'disown: invalid id for job' print 'disown: invalid id for job'
return 1 return 1
end end
else else
@ -19,7 +19,7 @@ commander.register('disown', function(args, sinks)
local ok = pcall(hilbish.jobs.disown, id) local ok = pcall(hilbish.jobs.disown, id)
if not ok then if not ok then
sinks.out:writeln 'disown: job does not exist' print 'disown: job does not exist'
return 2 return 2
end end
end) end)

View File

@ -2,7 +2,7 @@ local commander = require 'commander'
local fs = require 'fs' local fs = require 'fs'
local lunacolors = require 'lunacolors' local lunacolors = require 'lunacolors'
commander.register('doc', function(args, sinks) commander.register('doc', function(args)
local moddocPath = hilbish.dataDir .. '/docs/' local moddocPath = hilbish.dataDir .. '/docs/'
local stat = fs.stat '.git/refs/heads/extended-job-api' local stat = fs.stat '.git/refs/heads/extended-job-api'
if stat then if stat then
@ -48,16 +48,16 @@ Available sections: ]] .. table.concat(modules, ', ')
f = io.open(moddocPath .. subdocName .. '.md', 'rb') f = io.open(moddocPath .. subdocName .. '.md', 'rb')
end end
if not f then if not f then
sinks.out:writeln('No documentation found for ' .. mod .. '.') print('No documentation found for ' .. mod .. '.')
return 1 return 1
end end
end end
funcdocs = f:read '*a':gsub('-([%d]+)', '%1') funcdocs = f:read '*a':gsub('-([%d]+)', '%1')
local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= '_index.md' and f ~= 'index.md' end) local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= '_index.md' or f ~= 'index.md' end)
local subdocs = table.map(moddocs, function(fname) local subdocs = table.map(moddocs, function(fname)
return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.md', ''))) return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.md', '')))
end) end)
if #moddocs ~= 0 then if subdocName == '_index' then
funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ') funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ')
end end
@ -86,7 +86,7 @@ Available sections: ]] .. table.concat(modules, ', ')
end end
local backtickOccurence = 0 local backtickOccurence = 0
sinks.out:writeln(lunacolors.format(doc:gsub('`', function() print(lunacolors.format(doc:gsub('`', function()
backtickOccurence = backtickOccurence + 1 backtickOccurence = backtickOccurence + 1
if backtickOccurence % 2 == 0 then if backtickOccurence % 2 == 0 then
return '{reset}' return '{reset}'

View File

@ -1,15 +1,15 @@
local commander = require 'commander' local commander = require 'commander'
commander.register('fg', function(_, sinks) commander.register('fg', function()
local job = hilbish.jobs.last() local job = hilbish.jobs.last()
if not job then if not job then
sinks.out:writeln 'fg: no last job' print 'fg: no last job'
return 1 return 1
end end
local err = job.foreground() -- waits for job; blocks local err = job.foreground() -- waits for job; blocks
if err then if err then
sinks.out:writeln('fg: ' .. err) print('fg: ' .. err)
return 2 return 2
end end
end) end)

121
sink.go
View File

@ -1,121 +0,0 @@
package main
import (
"fmt"
"io"
"hilbish/util"
rt "github.com/arnodel/golua/runtime"
)
var sinkMetaKey = rt.StringValue("hshsink")
// a sink is a structure that has input and/or output
// it is like a lua file when used in popen, but specific to hilbish
type sink struct{
writer io.Writer
reader io.Reader
ud *rt.UserData
}
func setupSinkType(rtm *rt.Runtime) {
sinkMeta := rt.NewTable()
sinkMethods := rt.NewTable()
sinkFuncs := map[string]util.LuaExport{
"write": {luaSinkWrite, 2, false},
"writeln": {luaSinkWriteln, 2, false},
}
util.SetExports(l, sinkMethods, sinkFuncs)
sinkIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
arg := c.Arg(1)
val := sinkMethods.Get(arg)
return c.PushingNext1(t.Runtime, val), nil
}
sinkMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(sinkIndex, "__index", 2, false)))
l.SetRegistry(sinkMetaKey, rt.TableValue(sinkMeta))
}
func luaSinkWrite(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil {
return nil, err
}
s, err := sinkArg(c, 0)
if err != nil {
return nil, err
}
data, err := c.StringArg(1)
if err != nil {
return nil, err
}
s.writer.Write([]byte(data))
return c.Next(), nil
}
func luaSinkWriteln(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil {
return nil, err
}
s, err := sinkArg(c, 0)
if err != nil {
return nil, err
}
data, err := c.StringArg(1)
if err != nil {
return nil, err
}
s.writer.Write([]byte(data + "\n"))
return c.Next(), nil
}
func newSinkInput(r io.Reader) *sink {
s := &sink{
reader: r,
}
s.ud = sinkUserData(s)
return s
}
func newSinkOutput(w io.Writer) *sink {
s := &sink{
writer: w,
}
s.ud = sinkUserData(s)
return s
}
func sinkArg(c *rt.GoCont, arg int) (*sink, error) {
s, ok := valueToSink(c.Arg(arg))
if !ok {
return nil, fmt.Errorf("#%d must be a sink", arg + 1)
}
return s, nil
}
func valueToSink(val rt.Value) (*sink, bool) {
u, ok := val.TryUserData()
if !ok {
return nil, false
}
s, ok := u.Value().(*sink)
return s, ok
}
func sinkUserData(s *sink) *rt.UserData {
sinkMeta := l.Registry(sinkMetaKey)
return rt.NewUserData(s, sinkMeta.AsTable())
}