mirror of https://github.com/Hilbis/Hilbish
Compare commits
3 Commits
c87fbe2b99
...
f97a04179d
Author | SHA1 | Date |
---|---|---|
sammyette | f97a04179d | |
sammyette | 2f6ab5fd92 | |
sammyette | 088e326bd1 |
|
@ -4,9 +4,11 @@
|
|||
### Added
|
||||
- Documented custom userdata types (Job and Timer Objects)
|
||||
- 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
|
||||
- `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
|
||||
### Fixed
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 Rosettea
|
||||
Copyright (c) 2021-2023 Rosettea
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -414,7 +414,14 @@ func main() {
|
|||
|
||||
f, _ := os.Create(docPath)
|
||||
f.WriteString(fmt.Sprintf(header, modOrIface, modname, modu.ShortDescription))
|
||||
f.WriteString(fmt.Sprintf("## Introduction\n%s\n\n", modu.Description))
|
||||
typeTag, _ := regexp.Compile(`@\w+`)
|
||||
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 {
|
||||
f.WriteString("## Interface fields\n")
|
||||
for _, dps := range modu.Fields {
|
||||
|
@ -435,7 +442,6 @@ func main() {
|
|||
}
|
||||
|
||||
if len(modu.Docs) != 0 {
|
||||
typeTag, _ := regexp.Compile(`@\w+`)
|
||||
f.WriteString("## Functions\n")
|
||||
for _, dps := range modu.Docs {
|
||||
if dps.IsMember {
|
||||
|
@ -475,8 +481,6 @@ func main() {
|
|||
}
|
||||
}
|
||||
f.WriteString("\n")
|
||||
typeTag, _ := regexp.Compile(`@\w+`)
|
||||
|
||||
f.WriteString("### Methods\n")
|
||||
for _, dps := range modu.Docs {
|
||||
if !dps.IsMember {
|
||||
|
|
|
@ -8,7 +8,41 @@ menu:
|
|||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
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
|
||||
### deregister(name)
|
||||
|
|
13
exec.go
13
exec.go
|
@ -323,8 +323,18 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
|||
luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str))
|
||||
}
|
||||
|
||||
hc := interp.HandlerCtx(ctx)
|
||||
if commands[args[0]] != nil {
|
||||
luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs))
|
||||
stdin := newSinkInput(hc.Stdin)
|
||||
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 {
|
||||
fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error())
|
||||
return interp.NewExitStatus(1)
|
||||
|
@ -364,7 +374,6 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
|||
killTimeout := 2 * time.Second
|
||||
// from here is basically copy-paste of the default exec handler from
|
||||
// sh/interp but with our job handling
|
||||
hc := interp.HandlerCtx(ctx)
|
||||
path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0])
|
||||
if err != nil {
|
||||
fmt.Fprintln(hc.Stderr, err)
|
||||
|
|
|
@ -1,5 +1,40 @@
|
|||
// 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
|
||||
|
||||
import (
|
||||
|
|
1
lua.go
1
lua.go
|
@ -23,6 +23,7 @@ func luaInit() {
|
|||
MessageHandler: debuglib.Traceback,
|
||||
})
|
||||
lib.LoadAll(l)
|
||||
setupSinkType(l)
|
||||
|
||||
lib.LoadLibs(l, hilbishLoader)
|
||||
// yes this is stupid, i know
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
local commander = require 'commander'
|
||||
|
||||
commander.register('bg', function()
|
||||
commander.register('bg', function(_, sinks)
|
||||
local job = hilbish.jobs.last()
|
||||
if not job then
|
||||
print 'bg: no last job'
|
||||
sinks.out:writeln 'bg: no last job'
|
||||
return 1
|
||||
end
|
||||
|
||||
local err = job.background()
|
||||
if err then
|
||||
print('bg: ' .. err)
|
||||
sinks.out:writeln('bg: ' .. err)
|
||||
return 2
|
||||
end
|
||||
end)
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
local commander = require 'commander'
|
||||
local fs = require 'fs'
|
||||
|
||||
commander.register('cat', function(args)
|
||||
commander.register('cat', function(args, sinks)
|
||||
local exit = 0
|
||||
|
||||
if #args == 0 then
|
||||
print [[
|
||||
sinks.out:writeln [[
|
||||
usage: cat [file]...]]
|
||||
end
|
||||
|
||||
|
@ -13,11 +13,11 @@ usage: cat [file]...]]
|
|||
local f = io.open(fName)
|
||||
if f == nil then
|
||||
exit = 1
|
||||
print(string.format('cat: %s: no such file or directory', fName))
|
||||
sinks.out:writeln(string.format('cat: %s: no such file or directory', fName))
|
||||
goto continue
|
||||
end
|
||||
|
||||
io.write(f:read '*a')
|
||||
sinks.out:writeln(f:read '*a')
|
||||
::continue::
|
||||
end
|
||||
io.flush()
|
||||
|
|
|
@ -4,16 +4,16 @@ local fs = require 'fs'
|
|||
local dirs = require 'nature.dirs'
|
||||
|
||||
dirs.old = hilbish.cwd()
|
||||
commander.register('cd', function (args)
|
||||
commander.register('cd', function (args, sinks)
|
||||
if #args > 1 then
|
||||
print("cd: too many arguments")
|
||||
sinks.out:writeln("cd: too many arguments")
|
||||
return 1
|
||||
end
|
||||
|
||||
local path = args[1] and args[1] or hilbish.home
|
||||
if path == '-' then
|
||||
path = dirs.old
|
||||
print(path)
|
||||
sinks.out:writeln(path)
|
||||
end
|
||||
|
||||
dirs.setOld(hilbish.cwd())
|
||||
|
@ -21,7 +21,7 @@ commander.register('cd', function (args)
|
|||
|
||||
local ok, err = pcall(function() fs.cd(path) end)
|
||||
if not ok then
|
||||
print(err)
|
||||
sinks.out:writeln(err)
|
||||
return 1
|
||||
end
|
||||
bait.throw('cd', path)
|
||||
|
|
|
@ -3,9 +3,9 @@ local fs = require 'fs'
|
|||
local lunacolors = require 'lunacolors'
|
||||
local dirs = require 'nature.dirs'
|
||||
|
||||
commander.register('cdr', function(args)
|
||||
commander.register('cdr', function(args, sinks)
|
||||
if not args[1] then
|
||||
print(lunacolors.format [[
|
||||
sinks.out:writeln(lunacolors.format [[
|
||||
cdr: change directory to one which has been recently visied
|
||||
|
||||
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
|
||||
local recentDirs = dirs.recentDirs
|
||||
if #recentDirs == 0 then
|
||||
print 'No directories have been visited.'
|
||||
sinks.out:writeln 'No directories have been visited.'
|
||||
return 1
|
||||
end
|
||||
print(table.concat(recentDirs, '\n'))
|
||||
sinks.out:writeln(table.concat(recentDirs, '\n'))
|
||||
return
|
||||
end
|
||||
|
||||
local index = tonumber(args[1])
|
||||
if not index then
|
||||
print(string.format('Received %s as index, which isn\'t a number.', index))
|
||||
sinks.out:writeln(string.format('Received %s as index, which isn\'t a number.', index))
|
||||
return 1
|
||||
end
|
||||
|
||||
if not dirs.recent(index) then
|
||||
print(string.format('No recent directory found at index %s.', index))
|
||||
sinks.out:writeln(string.format('No recent directory found at index %s.', index))
|
||||
return 1
|
||||
end
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
local commander = require 'commander'
|
||||
|
||||
commander.register('disown', function(args)
|
||||
commander.register('disown', function(args, sinks)
|
||||
if #hilbish.jobs.all() == 0 then
|
||||
print 'disown: no current job'
|
||||
sinks.out:writeln 'disown: no current job'
|
||||
return 1
|
||||
end
|
||||
|
||||
|
@ -10,7 +10,7 @@ commander.register('disown', function(args)
|
|||
if #args < 0 then
|
||||
id = tonumber(args[1])
|
||||
if not id then
|
||||
print 'disown: invalid id for job'
|
||||
sinks.out:writeln 'disown: invalid id for job'
|
||||
return 1
|
||||
end
|
||||
else
|
||||
|
@ -19,7 +19,7 @@ commander.register('disown', function(args)
|
|||
|
||||
local ok = pcall(hilbish.jobs.disown, id)
|
||||
if not ok then
|
||||
print 'disown: job does not exist'
|
||||
sinks.out:writeln 'disown: job does not exist'
|
||||
return 2
|
||||
end
|
||||
end)
|
||||
|
|
|
@ -2,7 +2,7 @@ local commander = require 'commander'
|
|||
local fs = require 'fs'
|
||||
local lunacolors = require 'lunacolors'
|
||||
|
||||
commander.register('doc', function(args)
|
||||
commander.register('doc', function(args, sinks)
|
||||
local moddocPath = hilbish.dataDir .. '/docs/'
|
||||
local stat = fs.stat '.git/refs/heads/extended-job-api'
|
||||
if stat then
|
||||
|
@ -48,16 +48,16 @@ Available sections: ]] .. table.concat(modules, ', ')
|
|||
f = io.open(moddocPath .. subdocName .. '.md', 'rb')
|
||||
end
|
||||
if not f then
|
||||
print('No documentation found for ' .. mod .. '.')
|
||||
sinks.out:writeln('No documentation found for ' .. mod .. '.')
|
||||
return 1
|
||||
end
|
||||
end
|
||||
funcdocs = f:read '*a':gsub('-([%d]+)', '%1')
|
||||
local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= '_index.md' or f ~= 'index.md' end)
|
||||
local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= '_index.md' and f ~= 'index.md' end)
|
||||
local subdocs = table.map(moddocs, function(fname)
|
||||
return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.md', '')))
|
||||
end)
|
||||
if subdocName == '_index' then
|
||||
if #moddocs ~= 0 then
|
||||
funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ')
|
||||
end
|
||||
|
||||
|
@ -86,7 +86,7 @@ Available sections: ]] .. table.concat(modules, ', ')
|
|||
end
|
||||
|
||||
local backtickOccurence = 0
|
||||
print(lunacolors.format(doc:gsub('`', function()
|
||||
sinks.out:writeln(lunacolors.format(doc:gsub('`', function()
|
||||
backtickOccurence = backtickOccurence + 1
|
||||
if backtickOccurence % 2 == 0 then
|
||||
return '{reset}'
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
local commander = require 'commander'
|
||||
|
||||
commander.register('fg', function()
|
||||
commander.register('fg', function(_, sinks)
|
||||
local job = hilbish.jobs.last()
|
||||
if not job then
|
||||
print 'fg: no last job'
|
||||
sinks.out:writeln 'fg: no last job'
|
||||
return 1
|
||||
end
|
||||
|
||||
local err = job.foreground() -- waits for job; blocks
|
||||
if err then
|
||||
print('fg: ' .. err)
|
||||
sinks.out:writeln('fg: ' .. err)
|
||||
return 2
|
||||
end
|
||||
end)
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
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())
|
||||
}
|
Loading…
Reference in New Issue