Compare commits

..

No commits in common. "4e244e141f561eb336b0ca430ebef6aef4e5a086" and "123f8992b1b258bd649ea351ecb25ea9afc43dff" have entirely different histories.

11 changed files with 38 additions and 168 deletions

View File

@ -1,7 +0,0 @@
+ `command.exit` -> code, cmdStr > Thrown when a command exits.
`code` is the exit code of the command, and `cmdStr` is the command that was run.
+ `command.not-found` -> cmdStr > Thrown when a command is not found.
+ `command.no-perm` -> cmdStr > Thrown when Hilbish attempts to execute a file but
has no permission.

View File

@ -1,8 +0,0 @@
Here is listed all scopes for bait hooks. If a hook is related to a command,
it will have the `command` scope, as example.
Here is the format for a doc for a hook:
+ <hook name> -> <args> > <description>
`<args>` just means the arguments of the hook. If a hook doc has the format
of `arg...`, it means the hook can take/recieve any number of `arg`.

View File

@ -1,3 +0,0 @@
+ `signal.sigint` > Sent when Hilbish receives SIGINT (used to Ctrl-C).
+ `signal.resize` > Sent when the terminal is resized.

View File

@ -20,19 +20,15 @@ func New() Bait {
func (b *Bait) Loader(L *lua.LState) int { func (b *Bait) Loader(L *lua.LState) int {
mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{}) mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{})
util.Document(L, mod,
`Bait is the event emitter for Hilbish. Why name it bait?
Because it throws hooks that you can catch (emits events
that you can listen to) and because why not, fun naming
is fun. This is what you will use if you want to listen
in on hooks to know when certain things have happened,
like when you've changed directory, a command has
failed, etc. To find all available hooks, see doc hooks.`)
L.SetField(mod, "throw", luar.New(L, b.bthrow)) L.SetField(mod, "throw", luar.New(L, b.bthrow))
L.SetField(mod, "catch", luar.New(L, b.bcatch)) L.SetField(mod, "catch", luar.New(L, b.bcatch))
util.Document(L, mod, `Bait is the event emitter for Hilbish. Why name it bait?
Because it throws hooks that you can catch
(emits events that you can listen to) and because why not,
fun naming is fun. This is what you will use if you want to
listen in on hooks to know when certain things have happened,
like when you've changed directory, a command has failed, etc.`)
L.Push(mod) L.Push(mod)
return 1 return 1

View File

@ -32,13 +32,13 @@ func HilbishLoader(L *lua.LState) int {
username = strings.Split(username, "\\")[1] // for some reason Username includes the hostname on windows username = strings.Split(username, "\\")[1] // for some reason Username includes the hostname on windows
} }
util.SetField(L, mod, "ver", lua.LString(version), "Hilbish version") L.SetField(mod, "ver", lua.LString(version))
util.SetField(L, mod, "user", lua.LString(username), "Username of user") L.SetField(mod, "user", lua.LString(username))
util.SetField(L, mod, "host", lua.LString(host), "Host name of the machine") L.SetField(mod, "host", lua.LString(host))
util.SetField(L, mod, "home", lua.LString(homedir), "Home directory of the user") L.SetField(mod, "home", lua.LString(homedir))
util.SetField(L, mod, "dataDir", lua.LString(dataDir), "Directory for Hilbish's data files") L.SetField(mod, "dataDir", lua.LString(dataDir))
util.SetField(L, mod, "interactive", lua.LBool(interactive), "If this is an interactive shell") L.SetField(mod, "interactive", lua.LBool(interactive))
util.SetField(L, mod, "login", lua.LBool(interactive), "Whether this is a login shell") L.SetField(mod, "login", lua.LBool(interactive))
xdg := L.NewTable() xdg := L.NewTable()
L.SetField(xdg, "config", lua.LString(confDir)) L.SetField(xdg, "config", lua.LString(confDir))

25
lua.go
View File

@ -32,7 +32,6 @@ func LuaInit() {
l.SetGlobal("multiprompt", l.NewFunction(hshmlprompt)) l.SetGlobal("multiprompt", l.NewFunction(hshmlprompt))
l.SetGlobal("alias", l.NewFunction(hshalias)) l.SetGlobal("alias", l.NewFunction(hshalias))
l.SetGlobal("appendPath", l.NewFunction(hshappendPath)) l.SetGlobal("appendPath", l.NewFunction(hshappendPath))
l.SetGlobal("prependPath", l.NewFunction(hshprependPath))
l.SetGlobal("exec", l.NewFunction(hshexec)) l.SetGlobal("exec", l.NewFunction(hshexec))
l.SetGlobal("goro", luar.New(l, hshgoroutine)) l.SetGlobal("goro", luar.New(l, hshgoroutine))
l.SetGlobal("timeout", luar.New(l, hshtimeout)) l.SetGlobal("timeout", luar.New(l, hshtimeout))
@ -59,13 +58,6 @@ func LuaInit() {
hooks = bait.New() hooks = bait.New()
l.PreloadModule("bait", hooks.Loader) l.PreloadModule("bait", hooks.Loader)
// Add Ctrl-C handler
hooks.Em.On("signal.sigint", func() {
if !interactive {
os.Exit(0)
}
})
l.SetGlobal("complete", l.NewFunction(hshcomplete)) l.SetGlobal("complete", l.NewFunction(hshcomplete))
// Add more paths that Lua can require from // Add more paths that Lua can require from
@ -148,7 +140,7 @@ func hshappendPath(L *lua.LState) int {
// if dir isnt already in $PATH, add it // if dir isnt already in $PATH, add it
if !strings.Contains(pathenv, dir) { if !strings.Contains(pathenv, dir) {
os.Setenv("PATH", pathenv + string(os.PathListSeparator) + dir) os.Setenv("PATH", pathenv + ":" + dir)
} }
return 0 return 0
@ -234,18 +226,3 @@ func hshcomplete(L *lua.LState) int {
return 0 return 0
} }
// prependPath(dir)
// Prepends `dir` to $PATH
func hshprependPath(L *lua.LState) int {
dir := L.CheckString(1)
dir = strings.Replace(dir, "~", curuser.HomeDir, 1)
pathenv := os.Getenv("PATH")
// if dir isnt already in $PATH, add in
if !strings.Contains(pathenv, dir) {
os.Setenv("PATH", dir + string(os.PathListSeparator) + pathenv)
}
return 0
}

View File

@ -265,12 +265,13 @@ func HandleSignals() {
for s := range c { for s := range c {
switch s { switch s {
case os.Interrupt: case os.Interrupt:
hooks.Em.Emit("signals.sigint")
if !running { if !running {
if !interactive {
os.Exit(0)
}
lr.ClearInput() lr.ClearInput()
} }
case syscall.SIGWINCH: case syscall.SIGWINCH:
hooks.Em.Emit("signals.resize")
if !running { if !running {
lr.Resize() lr.Resize()
} }

View File

@ -49,90 +49,41 @@ commander.register('doc', function(args)
local moddocPath = hilbish.dataDir .. '/docs/' local moddocPath = hilbish.dataDir .. '/docs/'
local globalDesc = [[ local globalDesc = [[
These are the global Hilbish functions that are always available and not part of a module.]] These are the global Hilbish functions that are always available and not part of a module.]]
local modDocFormat = [[
%s
%s
# Functions
]]
if #args > 0 then if #args > 0 then
local mod = args[1] local mod = table.concat(args, ' '):gsub('^%s*(.-)%s*$', '%1')
local f = io.open(moddocPath .. mod .. '.txt', 'rb') local f = io.open(moddocPath .. mod .. '.txt', 'rb')
local funcdocs = nil
if not f then if not f then
-- assume subdir print('Could not find docs for module named ' .. mod .. '.')
-- dataDir/docs/<mod>/<mod>.txt return 1
moddocPath = moddocPath .. mod .. '/'
local subdocName = args[2]
if not subdocName then
subdocName = 'index'
end
f = io.open(moddocPath .. subdocName .. '.txt', 'rb')
if not f then
print('No documentation found for ' .. mod .. '.')
return
end
funcdocs = f:read '*a'
local subdocs = table.map(fs.readdir(moddocPath), function(f)
return lunacolors.underline(lunacolors.blue(string.gsub(f, '.txt', '')))
end)
if subdocName == 'index' then
funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ')
end
end end
if not funcdocs then local desc = (mod == 'global' and globalDesc or getmetatable(require(mod)).__doc)
funcdocs = f:read '*a' local funcdocs = f:read '*a'
end
local desc = ''
local ok = pcall(require, mod)
local backtickOccurence = 0 local backtickOccurence = 0
local formattedFuncs = lunacolors.format(funcdocs:sub(1, #funcdocs - 1):gsub('`', function() print(desc .. '\n\n' .. lunacolors.format(funcdocs:sub(1, #funcdocs - 1):gsub('`', function()
backtickOccurence = backtickOccurence + 1 backtickOccurence = backtickOccurence + 1
if backtickOccurence % 2 == 0 then if backtickOccurence % 2 == 0 then
return '{reset}' return '{reset}'
else else
return '{underline}{green}' return '{underline}{green}'
end end
end)) end)))
if mod == 'global' or ok then
local props = {}
local propstr = ''
local modDesc = globalDesc
if ok then
local modmt = getmetatable(require(mod))
modDesc = modmt.__doc
if modmt.__docProp then
-- not all modules have docs for properties
props = table.map(modmt.__docProp, function(v, k)
return lunacolors.underline(lunacolors.blue(k)) .. ' > ' .. v
end)
end
end
if #props > 0 then
propstr = '\n# Properties\n' .. table.concat(props, '\n') .. '\n'
end
desc = string.format(modDocFormat, modDesc, propstr)
end
print(desc .. formattedFuncs)
f:close() f:close()
return return
end end
local modules = table.map(fs.readdir(moddocPath), function(f) local modules = table.map(fs.readdir(moddocPath), function(f)
return lunacolors.underline(lunacolors.blue(string.gsub(f, '.txt', ''))) return lunacolors.underline(lunacolors.blue(f:sub(1, -5)))
end) end)
io.write [[ io.write [[
Welcome to Hilbish's doc tool! Here you can find documentation for builtin Welcome to Hilbish's doc tool! Here you can find documentation for builtin
functions and other things. functions and other things.
Usage: doc <section> [subdoc] Usage: doc <module>
A section is a module or a literal section and a subdoc is a subsection for it.
Available sections: ]] Available modules: ]]
print(table.concat(modules, ', ')) print(table.concat(modules, ', '))

18
rl.go
View File

@ -8,9 +8,9 @@ package main
// this is normal readline // this is normal readline
import ( import (
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"os"
"github.com/Rosettea/readline" "github.com/Rosettea/readline"
"github.com/yuin/gopher-lua" "github.com/yuin/gopher-lua"
@ -25,24 +25,12 @@ func NewLineReader(prompt string) *LineReader {
var completions []string var completions []string
// trim whitespace from ctx // trim whitespace from ctx
ctx = strings.TrimLeft(ctx, " ") ctx = strings.TrimLeft(ctx, " ")
fields, ctx := splitInput(ctx) fields := strings.Split(ctx, " ")
if len(fields) == 0 { if len(fields) == 0 {
return nil return nil
} }
for aliases[fields[0]] != "" {
alias := aliases[fields[0]]
ctx = alias + strings.TrimPrefix(ctx, fields[0])
fields = strings.Split(ctx, " ")
if aliases[fields[0]] == alias {
break
}
if aliases[fields[0]] != "" {
continue
}
}
if len(fields) == 1 { if len(fields) == 1 {
prefixes := []string{"./", "../"} prefixes := []string{"./", "../"}
for _, prefix := range prefixes { for _, prefix := range prefixes {

View File

@ -124,13 +124,6 @@ func execCommand(cmd string) error {
NRet: 1, NRet: 1,
Protect: true, Protect: true,
}, luacmdArgs) }, luacmdArgs)
if err != nil {
fmt.Fprintln(os.Stderr,
"Error in command:\n\n" + err.Error())
return interp.NewExitStatus(1)
}
luaexitcode := l.Get(-1) luaexitcode := l.Get(-1)
var exitcode uint8 = 0 var exitcode uint8 = 0
@ -140,6 +133,10 @@ func execCommand(cmd string) error {
exitcode = uint8(code) exitcode = uint8(code)
} }
if err != nil {
fmt.Fprintln(os.Stderr,
"Error in command:\n\n" + err.Error())
}
cmdFinish(exitcode, argstring) cmdFinish(exitcode, argstring)
return interp.NewExitStatus(exitcode) return interp.NewExitStatus(exitcode)
} }

View File

@ -2,31 +2,9 @@ package util
import "github.com/yuin/gopher-lua" import "github.com/yuin/gopher-lua"
// Document adds a documentation string to a module.
// It is accessible via the __doc metatable.
func Document(L *lua.LState, module lua.LValue, doc string) { func Document(L *lua.LState, module lua.LValue, doc string) {
mt := L.GetMetatable(module) mt := L.NewTable()
if mt == lua.LNil {
mt = L.NewTable()
L.SetMetatable(module, mt)
}
L.SetField(mt, "__doc", lua.LString(doc)) L.SetField(mt, "__doc", lua.LString(doc))
}
// SetField sets a field in a table, adding docs for it.
// It is accessible via the __docProp metatable. It is a table of the names of the fields.
func SetField(L *lua.LState, module lua.LValue, field string, value lua.LValue, doc string) {
mt := L.GetMetatable(module)
if mt == lua.LNil {
mt = L.NewTable()
docProp := L.NewTable()
L.SetField(mt, "__docProp", docProp)
L.SetMetatable(module, mt) L.SetMetatable(module, mt)
} }
docProp := L.GetTable(mt, lua.LString("__docProp"))
L.SetField(docProp, field, lua.LString(doc))
L.SetField(module, field, value)
}