Compare commits

...

13 Commits

Author SHA1 Message Date
TorchedSammy 3db6334445
docs: [ci] generate new docs 2022-04-23 00:03:50 -04:00
TorchedSammy 9e596dc271
refactor: (re)organize and change prelude path and structure
prelude is no longer. it is now nature.
organized the single file prelude into multiple
source files and renamed it to nature. this is coming
after thought that it can turn into a general hilbish
lua core, with user facing modules as well.

this introduces the `nature.dirs` module, to interact
and get recently changed to directories and last/old
cwd.
2022-04-23 00:03:50 -04:00
TorchedSammy f003249632
docs: update doc reference for vim mode actions 2022-04-23 00:03:49 -04:00
TorchedSammy 1714aeac36
feat: add fs.abs 2022-04-23 00:03:49 -04:00
TorchedSammy bca89197ad
fix: add io flush after doc help message 2022-04-23 00:03:49 -04:00
TorchedSammy 27ac60b856
fix: remove unused var 2022-04-23 00:03:48 -04:00
TorchedSammy dbb27f7739
docs: [ci] generate new docs 2022-04-23 00:03:48 -04:00
TorchedSammy 03a57fce5b
docs: add more documentation for completions 2022-04-23 00:03:48 -04:00
TorchedSammy abfbeb5f84
feat: allow overwrite of completion handler (closes #122)
this also makes the completion functions `bins`
and `files` also return the prefix to pass
to the completion handler.

this is an overhaul to the completion system,
which gets the completion handler from lua
instead of being made to only have lua provided
*command* completions.

it does not have any performance deficit, even
though it calls in to golua for completions.
2022-04-23 00:03:47 -04:00
TorchedSammy 3194add3dc
fix: restore doc related metafields on hilbish table 2022-04-23 00:03:47 -04:00
TorchedSammy 1ba88fea88
fix: correct custom runner mode handling with recent changes 2022-04-23 00:03:47 -04:00
TorchedSammy 1274811739
docs: [ci] generate new docs 2022-04-23 00:03:46 -04:00
TorchedSammy 0af36db6ff
fix!: change the way highlighter and hinter are set
with the change of blocking changes to the
hilbish table, i took an opportunity
to make the highlighter and hinter callbacks
set in a more natural way. instead of being
a function which takes a callback, you set
the function itself.
2022-04-23 00:03:38 -04:00
27 changed files with 706 additions and 504 deletions

View File

@ -15,7 +15,7 @@ build:
install: install:
install -v -d "$(DESTDIR)$(BINDIR)/" && install -m 0755 -v hilbish "$(DESTDIR)$(BINDIR)/hilbish" install -v -d "$(DESTDIR)$(BINDIR)/" && install -m 0755 -v hilbish "$(DESTDIR)$(BINDIR)/hilbish"
mkdir -p "$(DESTDIR)$(LIBDIR)" mkdir -p "$(DESTDIR)$(LIBDIR)"
cp -r libs docs emmyLuaDocs prelude .hilbishrc.lua "$(DESTDIR)$(LIBDIR)" cp -r libs docs emmyLuaDocs nature .hilbishrc.lua "$(DESTDIR)$(LIBDIR)"
grep -qxF "$(DESTDIR)$(BINDIR)/hilbish" /etc/shells || echo "$(DESTDIR)$(BINDIR)/hilbish" >> /etc/shells grep -qxF "$(DESTDIR)$(BINDIR)/hilbish" /etc/shells || echo "$(DESTDIR)$(BINDIR)/hilbish" >> /etc/shells
uninstall: uninstall:

112
api.go
View File

@ -63,12 +63,29 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
return c.PushingNext1(t.Runtime, val), nil return c.PushingNext1(t.Runtime, val), nil
} }
modNewIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { modNewIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
k := c.Arg(1) k, err := c.StringArg(1)
if err != nil {
return nil, err
}
v := c.Arg(2) v := c.Arg(2)
if modVal := mod.Get(k); modVal != rt.NilValue { if k == "highlighter" {
var err error
// fine to assign, since itll be either nil or a closure
highlighter, err = c.ClosureArg(2)
if err != nil {
return nil, errors.New("hilbish.highlighter has to be a function")
}
} else if k == "hinter" {
var err error
hinter, err = c.ClosureArg(2)
if err != nil {
return nil, errors.New("hilbish.hinter has to be a function")
}
} else if modVal := mod.Get(rt.StringValue(k)); modVal != rt.NilValue {
return nil, errors.New("not allowed to override in hilbish table") return nil, errors.New("not allowed to override in hilbish table")
} }
mod.Set(k, v) mod.Set(rt.StringValue(k), v)
return c.Next(), nil return c.Next(), nil
} }
@ -90,17 +107,17 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
The nice lil shell for {blue}Lua{reset} fanatics! The nice lil shell for {blue}Lua{reset} fanatics!
Check out the {blue}{bold}guide{reset} command to get started. Check out the {blue}{bold}guide{reset} command to get started.
` `
util.SetField(rtm, mod, "ver", rt.StringValue(version), "Hilbish version") util.SetFieldProtected(fakeMod, mod, "ver", rt.StringValue(version), "Hilbish version")
util.SetField(rtm, mod, "user", rt.StringValue(username), "Username of user") util.SetFieldProtected(fakeMod, mod, "user", rt.StringValue(username), "Username of user")
util.SetField(rtm, mod, "host", rt.StringValue(host), "Host name of the machine") util.SetFieldProtected(fakeMod, mod, "host", rt.StringValue(host), "Host name of the machine")
util.SetField(rtm, mod, "home", rt.StringValue(curuser.HomeDir), "Home directory of the user") util.SetFieldProtected(fakeMod, mod, "home", rt.StringValue(curuser.HomeDir), "Home directory of the user")
util.SetField(rtm, mod, "dataDir", rt.StringValue(dataDir), "Directory for Hilbish's data files") util.SetFieldProtected(fakeMod, mod, "dataDir", rt.StringValue(dataDir), "Directory for Hilbish's data files")
util.SetField(rtm, mod, "interactive", rt.BoolValue(interactive), "If this is an interactive shell") util.SetFieldProtected(fakeMod, mod, "interactive", rt.BoolValue(interactive), "If this is an interactive shell")
util.SetField(rtm, mod, "login", rt.BoolValue(login), "Whether this is a login shell") util.SetFieldProtected(fakeMod, mod, "login", rt.BoolValue(login), "Whether this is a login shell")
util.SetField(rtm, mod, "greeting", rt.StringValue(greeting), "Hilbish's welcome message for interactive shells. It has Lunacolors formatting.") util.SetFieldProtected(fakeMod, mod, "greeting", rt.StringValue(greeting), "Hilbish's welcome message for interactive shells. It has Lunacolors formatting.")
util.SetField(rtm, mod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)") util.SetFieldProtected(fakeMod, mod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)")
util.SetField(rtm, mod, "exitCode", rt.IntValue(0), "Exit code of last exected command") util.SetFieldProtected(fakeMod, mod, "exitCode", rt.IntValue(0), "Exit code of last exected command")
util.Document(mod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.") util.Document(fakeMod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.")
// hilbish.userDir table // hilbish.userDir table
hshuser := rt.NewTable() hshuser := rt.NewTable()
@ -132,19 +149,7 @@ Check out the {blue}{bold}guide{reset} command to get started.
util.Document(historyModule, "History interface for Hilbish.") util.Document(historyModule, "History interface for Hilbish.")
// hilbish.completion table // hilbish.completion table
hshcomp := rt.NewTable() hshcomp := completionLoader(rtm)
util.SetField(rtm, hshcomp, "files",
rt.FunctionValue(rt.NewGoFunction(luaFileComplete, "files", 3, false)),
"Completer for files")
util.SetField(rtm, hshcomp, "bins",
rt.FunctionValue(rt.NewGoFunction(luaBinaryComplete, "bins", 3, false)),
"Completer for executables/binaries")
util.SetField(rtm, hshcomp, "call",
rt.FunctionValue(rt.NewGoFunction(callLuaCompleter, "call", 4, false)),
"Calls a completer and get its entries for completions")
util.Document(hshcomp, "Completions interface for Hilbish.") util.Document(hshcomp, "Completions interface for Hilbish.")
mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp)) mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp))
@ -492,9 +497,7 @@ func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
// A `scope` is currently only expected to be `command.<cmd>`, // A `scope` is currently only expected to be `command.<cmd>`,
// replacing <cmd> with the name of the command (for example `command.git`). // replacing <cmd> with the name of the command (for example `command.git`).
// `cb` must be a function that returns a table of "completion groups." // `cb` must be a function that returns a table of "completion groups."
// A completion group is a table with the keys `items` and `type`. // Check `doc completions` for more information.
// `items` being a table of items and `type` being the display type of
// `grid` (the normal file completion display) or `list` (with a description)
// --- @param scope string // --- @param scope string
// --- @param cb function // --- @param cb function
func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
@ -599,7 +602,6 @@ func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
switch mode.Type() { switch mode.Type() {
case rt.StringType: case rt.StringType:
switch mode.AsString() { switch mode.AsString() {
// no fallthrough doesnt work so eh
case "hybrid", "hybridRev", "lua", "sh": runnerMode = mode case "hybrid", "hybridRev", "lua", "sh": runnerMode = mode
default: return nil, errors.New("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received " + mode.AsString()) default: return nil, errors.New("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received " + mode.AsString())
} }
@ -610,40 +612,24 @@ func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// hinter(cb) // hinter(line, pos)
// Sets the hinter function. This will be called on every key insert to determine // The command line hint handler. It gets called on every key insert to
// what text to use as an inline hint. The callback is passed 2 arguments: // determine what text to use as an inline hint. It is passed the current
// the current line and the position. It is expected to return a string // line and cursor position. It is expected to return a string which is used
// which will be used for the hint. // as the text for the hint. This is by default a shim. To set hints,
// --- @param cb function // override this function with your custom handler.
// --- @param line string
// --- @param pos int
func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { return c.Next(), nil
return nil, err
}
hinterCb, err := c.ClosureArg(0)
if err != nil {
return nil, err
}
hinter = hinterCb
return c.Next(), err
} }
// highlighter(cb) // highlighter(line)
// Sets the highlighter function. This is mainly for syntax hightlighting, but in // Line highlighter handler. This is mainly for syntax highlighting, but in
// reality could set the input of the prompt to display anything. The callback // reality could set the input of the prompt to *display* anything. The
// is passed the current line as typed and is expected to return a line that will // callback is passed the current line and is expected to return a line that
// be used to display in the line. // will be used as the input display.
// --- @param cb function // --- @param line string
func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { return c.Next(), nil
return nil, err
}
highlighterCb, err := c.ClosureArg(0)
if err != nil {
return nil, err
}
highlighter = highlighterCb
return c.Next(), err
} }

View File

@ -117,6 +117,100 @@ func escapeFilename(fname string) string {
return r.Replace(fname) return r.Replace(fname)
} }
func completionLoader(rtm *rt.Runtime) *rt.Table {
exports := map[string]util.LuaExport{
"files": {luaFileComplete, 3, false},
"bins": {luaBinaryComplete, 3, false},
"call": {callLuaCompleter, 4, false},
"handler": {completionHandler, 2, false},
}
mod := rt.NewTable()
util.SetExports(rtm, mod, exports)
return mod
}
func completionHandler(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil {
return nil, err
}
line, err := c.StringArg(0)
if err != nil {
return nil, err
}
// just for validation
_, err = c.IntArg(1)
if err != nil {
return nil, err
}
ctx := strings.TrimLeft(line, " ")
if len(ctx) == 0 {
return c.PushingNext(t.Runtime, rt.TableValue(rt.NewTable()), rt.StringValue("")), nil
}
ctx = aliases.Resolve(ctx)
fields := strings.Split(ctx, " ")
query := fields[len(fields) - 1]
luaFields := rt.NewTable()
for i, f := range fields {
luaFields.Set(rt.IntValue(int64(i + 1)), rt.StringValue(f))
}
compMod := hshMod.Get(rt.StringValue("completion")).AsTable()
var term *rt.Termination
if len(fields) == 1 {
term = rt.NewTerminationWith(t.CurrentCont(), 2, false)
err := rt.Call(t, compMod.Get(rt.StringValue("bins")), []rt.Value{
rt.StringValue(query),
rt.StringValue(ctx),
rt.TableValue(luaFields),
}, term)
if err != nil {
return nil, err
}
} else {
gterm := rt.NewTerminationWith(t.CurrentCont(), 2, false)
err := rt.Call(t, compMod.Get(rt.StringValue("call")), []rt.Value{
rt.StringValue("commands." + fields[0]),
rt.StringValue(query),
rt.StringValue(ctx),
rt.TableValue(luaFields),
}, gterm)
if err == nil {
groups := gterm.Get(0)
pfx := gterm.Get(1)
return c.PushingNext(t.Runtime, groups, pfx), nil
}
// error means there isnt a command handler - default to files in that case
term = rt.NewTerminationWith(t.CurrentCont(), 2, false)
err = rt.Call(t, compMod.Get(rt.StringValue("files")), []rt.Value{
rt.StringValue(query),
rt.StringValue(ctx),
rt.TableValue(luaFields),
}, term)
}
comps := term.Get(0)
pfx := term.Get(1)
groups := rt.NewTable()
compGroup := rt.NewTable()
compGroup.Set(rt.StringValue("items"), comps)
compGroup.Set(rt.StringValue("type"), rt.StringValue("grid"))
groups.Set(rt.IntValue(1), rt.TableValue(compGroup))
return c.PushingNext(t.Runtime, rt.TableValue(groups), pfx), nil
}
func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(4); err != nil { if err := c.CheckNArgs(4); err != nil {
return nil, err return nil, err
@ -162,14 +256,14 @@ func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return nil, err return nil, err
} }
completions, _ := fileComplete(query, ctx, fds) completions, pfx := fileComplete(query, ctx, fds)
luaComps := rt.NewTable() luaComps := rt.NewTable()
for i, comp := range completions { for i, comp := range completions {
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp)) luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
} }
return c.PushingNext1(t.Runtime, rt.TableValue(luaComps)), nil return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil
} }
func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
@ -178,14 +272,14 @@ func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return nil, err return nil, err
} }
completions, _ := binaryComplete(query, ctx, fds) completions, pfx := binaryComplete(query, ctx, fds)
luaComps := rt.NewTable() luaComps := rt.NewTable()
for i, comp := range completions { for i, comp := range completions {
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp)) luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
} }
return c.PushingNext1(t.Runtime, rt.TableValue(luaComps)), nil return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil
} }
func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, error) { func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, error) {

View File

@ -0,0 +1,44 @@
Hilbish has a pretty good completion system. It has a nice looking menu,
with 2 types of menus: grid (like file completions) or list.
Like most parts of Hilbish, it's made to be extensible and customizable.
The default handler for completions in general can be overwritten to provide
more advanced completions if needed.
# Completion Handler
By default, it provides 3 things: for the first argument, binaries (with a
plain name requested to complete, those in $PATH), files, or command
completions. With the default completion handler, it will try to run a
handler for the command or fallback to file completions.
To overwrite it, just assign a function to `hilbish.completion.handler`
like so:
function hilbish.completion.handler(line, pos)
-- do things
end
It is passed 2 arguments, the entire line, and the current cursor position.
The functions in the completion interface take 3 arguments: query, ctx,
and fields. The `query`, which what the user is currently trying to complete,
`ctx`, being just the entire line, and `fields` being a table of arguments.
It's just `ctx` split up, delimited by spaces.
It's expected to return 2 things: a table of completion groups, and a prefix.
A completion group is defined as a table with 2 keys: `items` and `type`.
The `items` field is just a table of items to use for completions.
The `type` is for the completion menu type, being either `grid` or `list`.
The prefix is what all the completions start with. It should be empty
if the user doesn't have a query. If the beginning of the completion
item does not match the prefix, it will be replaced and fixed properly
in the line. It is case sensitive.
If you want to overwrite the functionality of the general completion handler,
or make your command completion have files as well (and filter them),
then there is the `files` function, which is mentioned below.
# Completion Interface
## Functions
- `files(query, ctx, fields)` -> table, prefix: get file completions, based
on the user's query.
- `bins(query, ctx, fields)` -> table, prefix: get binary/executable
completions, based on user query.
- `call(scope, query, ctx, fields)` -> table, prefix: call a completion handler
with `scope`, usually being in the form of `command.<name>`

View File

@ -1,3 +1,5 @@
abs(path) > Gives an absolute version of `path`.
cd(dir) > Changes directory to `dir` cd(dir) > Changes directory to `dir`
mkdir(name, recursive) > Makes a directory called `name`. If `recursive` is true, it will create its parent directories. mkdir(name, recursive) > Makes a directory called `name`. If `recursive` is true, it will create its parent directories.

View File

@ -6,9 +6,7 @@ complete(scope, cb) > Registers a completion handler for `scope`.
A `scope` is currently only expected to be `command.<cmd>`, A `scope` is currently only expected to be `command.<cmd>`,
replacing <cmd> with the name of the command (for example `command.git`). replacing <cmd> with the name of the command (for example `command.git`).
`cb` must be a function that returns a table of "completion groups." `cb` must be a function that returns a table of "completion groups."
A completion group is a table with the keys `items` and `type`. Check `doc completions` for more information.
`items` being a table of items and `type` being the display type of
`grid` (the normal file completion display) or `list` (with a description)
cwd() > Returns the current directory of the shell cwd() > Returns the current directory of the shell
@ -16,15 +14,16 @@ exec(cmd) > Replaces running hilbish with `cmd`
goro(fn) > Puts `fn` in a goroutine goro(fn) > Puts `fn` in a goroutine
highlighter(cb) > Sets the highlighter function. This is mainly for syntax hightlighting, but in highlighter(line) > Line highlighter handler. This is mainly for syntax highlighting, but in
reality could set the input of the prompt to display anything. The callback reality could set the input of the prompt to *display* anything. The
is passed the current line as typed and is expected to return a line that will callback is passed the current line and is expected to return a line that
be used to display in the line. will be used as the input display.
hinter(cb) > Sets the hinter function. This will be called on every key insert to determine hinter(line, pos) > The command line hint handler. It gets called on every key insert to
what text to use as an inline hint. The callback is passed 2 arguments: determine what text to use as an inline hint. It is passed the current
the current line and the position. It is expected to return a string line and cursor position. It is expected to return a string which is used
which will be used for the hint. as the text for the hint. This is by default a shim. To set hints,
override this function with your custom handler.
inputMode(mode) > Sets the input mode for Hilbish's line reader. Accepts either emacs or vim inputMode(mode) > Sets the input mode for Hilbish's line reader. Accepts either emacs or vim

View File

@ -4,4 +4,4 @@
`modeName` is the name of the mode changed to (can be `insert`, `normal`, `delete` or `replace`). `modeName` is the name of the mode changed to (can be `insert`, `normal`, `delete` or `replace`).
+ `hilbish.vimAction` -> actionName, args > Sent when the user does a "vim action," being something + `hilbish.vimAction` -> actionName, args > Sent when the user does a "vim action," being something
like yanking or pasting text. See `doc vimMode actions` for more info. like yanking or pasting text. See `doc vim-mode actions` for more info.

View File

@ -2,6 +2,10 @@
local fs = {} local fs = {}
--- Gives an absolute version of `path`.
--- @param path string
function fs.abs(path) end
--- Changes directory to `dir` --- Changes directory to `dir`
--- @param dir string --- @param dir string
function fs.cd(dir) end function fs.cd(dir) end

View File

@ -15,9 +15,7 @@ function hilbish.appendPath(dir) end
--- A `scope` is currently only expected to be `command.<cmd>`, --- A `scope` is currently only expected to be `command.<cmd>`,
--- replacing <cmd> with the name of the command (for example `command.git`). --- replacing <cmd> with the name of the command (for example `command.git`).
--- `cb` must be a function that returns a table of "completion groups." --- `cb` must be a function that returns a table of "completion groups."
--- A completion group is a table with the keys `items` and `type`. --- Check `doc completions` for more information.
--- `items` being a table of items and `type` being the display type of
--- `grid` (the normal file completion display) or `list` (with a description)
--- @param scope string --- @param scope string
--- @param cb function --- @param cb function
function hilbish.complete(scope, cb) end function hilbish.complete(scope, cb) end
@ -33,19 +31,21 @@ function hilbish.exec(cmd) end
--- @param fn function --- @param fn function
function hilbish.goro(fn) end function hilbish.goro(fn) end
--- Sets the highlighter function. This is mainly for syntax hightlighting, but in --- Line highlighter handler. This is mainly for syntax highlighting, but in
--- reality could set the input of the prompt to display anything. The callback --- reality could set the input of the prompt to *display* anything. The
--- is passed the current line as typed and is expected to return a line that will --- callback is passed the current line and is expected to return a line that
--- be used to display in the line. --- will be used as the input display.
--- @param cb function --- @param line string
function hilbish.highlighter(cb) end function hilbish.highlighter(line) end
--- Sets the hinter function. This will be called on every key insert to determine --- The command line hint handler. It gets called on every key insert to
--- what text to use as an inline hint. The callback is passed 2 arguments: --- determine what text to use as an inline hint. It is passed the current
--- the current line and the position. It is expected to return a string --- line and cursor position. It is expected to return a string which is used
--- which will be used for the hint. --- as the text for the hint. This is by default a shim. To set hints,
--- @param cb function --- override this function with your custom handler.
function hilbish.hinter(cb) end --- @param line string
--- @param pos int
function hilbish.hinter(line, pos) end
--- Sets the input mode for Hilbish's line reader. Accepts either emacs or vim --- Sets the input mode for Hilbish's line reader. Accepts either emacs or vim
--- @param mode string --- @param mode string

View File

@ -73,7 +73,7 @@ func runInput(input string, priv bool) {
} }
} else { } else {
// can only be a string or function so // can only be a string or function so
term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 2, false) term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 3, false)
err := rt.Call(l.MainThread(), runnerMode, []rt.Value{rt.StringValue(cmdString)}, term) err := rt.Call(l.MainThread(), runnerMode, []rt.Value{rt.StringValue(cmdString)}, term)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
@ -81,9 +81,9 @@ func runInput(input string, priv bool) {
return return
} }
luaexitcode := term.Get(0) luaInput := term.Get(0)
runErr := term.Get(1) luaexitcode := term.Get(1)
luaInput := term.Get(1) runErr := term.Get(2)
var exitCode uint8 var exitCode uint8
if code, ok := luaexitcode.TryInt(); ok { if code, ok := luaexitcode.TryInt(); ok {

View File

@ -1,6 +1,7 @@
package fs package fs
import ( import (
"path/filepath"
"strconv" "strconv"
"os" "os"
"strings" "strings"
@ -22,6 +23,7 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
"mkdir": util.LuaExport{fmkdir, 2, false}, "mkdir": util.LuaExport{fmkdir, 2, false},
"stat": util.LuaExport{fstat, 1, false}, "stat": util.LuaExport{fstat, 1, false},
"readdir": util.LuaExport{freaddir, 1, false}, "readdir": util.LuaExport{freaddir, 1, false},
"abs": util.LuaExport{fabs, 1, false},
} }
mod := rt.NewTable() mod := rt.NewTable()
util.SetExports(rtm, mod, exports) util.SetExports(rtm, mod, exports)
@ -132,3 +134,20 @@ func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil
} }
// abs(path)
// Gives an absolute version of `path`.
// --- @param path string
func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
path, err := c.StringArg(0)
if err != nil {
return nil, err
}
abspath, err := filepath.Abs(path)
if err != nil {
return nil, err
}
return c.PushingNext1(t.Runtime, rt.StringValue(abspath)), nil
}

2
lua.go
View File

@ -54,7 +54,7 @@ func luaInit() {
fmt.Fprintln(os.Stderr, "Could not add preload paths! Libraries will be missing. This shouldn't happen.") fmt.Fprintln(os.Stderr, "Could not add preload paths! Libraries will be missing. This shouldn't happen.")
} }
err = util.DoFile(l, "prelude/init.lua") err = util.DoFile(l, "nature/init.lua")
if err != nil { if err != nil {
err = util.DoFile(l, preloadPath) err = util.DoFile(l, preloadPath)
if err != nil { if err != nil {

View File

@ -0,0 +1,35 @@
local bait = require 'bait'
local commander = require 'commander'
local fs = require 'fs'
local dirs = require 'nature.dirs'
dirs.old = hilbish.cwd()
commander.register('cd', function (args)
if #args > 1 then
print("cd: too many arguments")
return 1
elseif #args > 0 then
local path = args[1]:gsub('$%$','\0'):gsub('${([%w_]+)}', os.getenv)
:gsub('$([%w_]+)', os.getenv):gsub('%z','$'):gsub('^%s*(.-)%s*$', '%1')
if path == '-' then
path = dirs.old
print(path)
end
dirs.setOld(hilbish.cwd())
dirs.push(path)
local ok, err = pcall(function() fs.cd(path) end)
if not ok then
print(err)
return 1
end
bait.throw('cd', path)
return
end
fs.cd(hilbish.home)
bait.throw('cd', hilbish.home)
dirs.addRecent(hilbish.home)
end)

View File

@ -0,0 +1,39 @@
local commander = require 'commander'
local fs = require 'fs'
local lunacolors = require 'lunacolors'
local dirs = require 'nature.dirs'
commander.register('cdr', function(args)
if not args[1] then
print(lunacolors.format [[
cdr: change directory to one which has been recently visied
usage: cdr <index>
to get a list of recent directories, use {green}{underline}cdr list{reset}]])
return
end
if args[1] == 'list' then
local recentDirs = dirs.recentDirs
if #recentDirs == 0 then
print 'No directories have been visited.'
return 1
end
print(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))
return 1
end
if not dirs.recent(index) then
print(string.format('No recent directory found at index %s.', index))
return 1
end
fs.cd(dirs.recent(index))
end)

View File

@ -0,0 +1,93 @@
local commander = require 'commander'
local fs = require 'fs'
local lunacolors = require 'lunacolors'
commander.register('doc', function(args)
local moddocPath = hilbish.dataDir .. '/docs/'
local modDocFormat = [[
%s
%s
# Functions
]]
if #args > 0 then
local mod = args[1]
local f = io.open(moddocPath .. mod .. '.txt', 'rb')
local funcdocs = nil
if not f then
-- assume subdir
-- dataDir/docs/<mod>/<mod>.txt
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 moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= 'index.txt' end)
local subdocs = table.map(moddocs, function(fname)
return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.txt', '')))
end)
if subdocName == 'index' then
funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ')
end
end
if not funcdocs then
funcdocs = f:read '*a'
end
local desc = ''
local ok = pcall(require, mod)
local backtickOccurence = 0
local formattedFuncs = lunacolors.format(funcdocs:sub(1, #funcdocs - 1):gsub('`', function()
backtickOccurence = backtickOccurence + 1
if backtickOccurence % 2 == 0 then
return '{reset}'
else
return '{underline}{green}'
end
end))
if ok then
local props = {}
local propstr = ''
local modDesc = ''
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
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()
return
end
local modules = table.map(fs.readdir(moddocPath), function(f)
return lunacolors.underline(lunacolors.blue(string.gsub(f, '.txt', '')))
end)
io.write [[
Welcome to Hilbish's doc tool! Here you can find documentation for builtin
functions and other things.
Usage: doc <section> [subdoc]
A section is a module or a literal section and a subdoc is a subsection for it.
Available sections: ]]
io.flush()
print(table.concat(modules, ', '))
end)

View File

@ -0,0 +1,7 @@
local bait = require 'bait'
local commander = require 'commander'
commander.register('exit', function()
bait.throw('hilbish.exit')
os.exit(0)
end)

View File

@ -0,0 +1,54 @@
local ansikit = require 'ansikit'
local commander = require 'commander'
local helpTexts = {
[[
Hello there! Welcome to Hilbish, the comfy and nice little shell for
Lua users and fans. Hilbish is configured with Lua, and its
scripts are also in Lua. It also runs both Lua and shell script when
interactive (aka normal usage).
]],
[[
What does that mean for you, the user? It means that if you prefer to
use Lua for scripting instead of shell script but still have ordinary
shell usage for interactive use.
]],
[[
If this is your first time using Hilbish and Lua, check out the
Programming in Lua book here: https://www.lua.org/pil
After (or if you already know Lua) check out the doc command.
It is an in shell tool for documentation about Hilbish provided
functions and modules.
]],
[[
If you've updated from a pre-1.0 version (0.7.1 as an example)
you'll want to move your config from ~/.hilbishrc.lua to
]] ..
hilbish.userDir.config .. '/hilbish/init.lua' ..
[[
and also change all global functions (prompt, alias) to be
in the hilbish module (hilbish.prompt, hilbish.alias as examples).
And if this is your first time (most likely), you can copy a config
from ]] .. hilbish.dataDir,
[[
Since 1.0 is a big release, you'll want to check the changelog
at https://github.com/Rosettea/Hilbish/releases/tag/v1.0.0
to find more breaking changes.
]]
}
commander.register('guide', function()
ansikit.clear()
ansikit.cursorTo(0, 0)
for _, text in ipairs(helpTexts) do
print(text)
local out = hilbish.read('Hit enter to continue ')
ansikit.clear()
ansikit.cursorTo(0, 0)
if not out then
return
end
end
print 'Hope you enjoy using Hilbish!'
end)

View File

@ -0,0 +1,6 @@
-- Add command builtins
require 'nature.commands.cd'
require 'nature.commands.cdr'
require 'nature.commands.doc'
require 'nature.commands.exit'
require 'nature.commands.guide'

75
nature/dirs.lua 100644
View File

@ -0,0 +1,75 @@
local fs = require 'fs'
local dirs = {}
--- Last (current working) directory. Separate from recentDirs mainly for
--- easier use.
dirs.old = ''
--- Table of recent directories. For use, look at public functions.
dirs.recentDirs = {}
--- Size of the recentDirs table.
dirs.recentSize = 10
--- Get (and remove) a `num` of entries from recent directories.
--- @param num number
--- @param remove boolean Whether to remove items
function dirRecents(num, remove)
num = num or 1
local entries = {}
if #dirs.recentDirs ~= 0 then
for i = 1, num do
local idx = remove and 1 or i
if not dirs.recentDirs[idx] then break end
table.insert(entries, dirs.recentDirs[idx])
if remove then table.remove(dirs.recentDirs, 1) end
end
end
if #entries == 1 then
return entries[1]
end
return entries
end
--- Look at `num` amount of recent directories, starting from the latest.
--- @param num? number
function dirs.peak(num)
return dirRecents(num)
end
--- Add `d` to the recent directories.
function dirs.push(d)
dirs.recentDirs[dirs.recentSize + 1] = nil
if dirs.recentDirs[#dirs.recentDirs - 1] ~= d then
ok, d = pcall(fs.abs, d)
assert(ok, 'could not turn "' .. d .. '"into an absolute path')
table.insert(dirs.recentDirs, 1, d)
end
end
--- Remove `num` amount of dirs from the recent directories.
--- @param num number
function dirs.pop(num)
return dirRecents(num, true)
end
--- Get entry from recent directories.
--- @param idx number
function dirs.recent(idx)
return dirs.recentDirs[idx]
end
--- Sets the old directory.
--- @param d string
function dirs.setOld(d)
ok, d = pcall(fs.abs, d)
assert(ok, 'could not turn "' .. d .. '"into an absolute path')
os.setenv('OLDPWD', d)
dirs.old = d
end
return dirs

11
nature/hooks.lua 100644
View File

@ -0,0 +1,11 @@
local bait = require 'bait'
-- Hook handles
bait.catch('command.not-found', function(cmd)
print(string.format('hilbish: %s not found', cmd))
end)
bait.catch('command.not-executable', function(cmd)
print(string.format('hilbish: %s: not executable', cmd))
end)

44
nature/init.lua 100644
View File

@ -0,0 +1,44 @@
-- Prelude initializes everything else for our shell
local _ = require 'succulent' -- Function additions
package.path = package.path .. ';' .. hilbish.dataDir .. '/?/init.lua'
.. ';' .. hilbish.dataDir .. '/?/?.lua'
require 'nature.hooks'
require 'nature.commands'
local shlvl = tonumber(os.getenv 'SHLVL')
if shlvl ~= nil then
os.setenv('SHLVL', tostring(shlvl + 1))
else
os.setenv('SHLVL', '0')
end
do
local virt_G = { }
setmetatable(_G, {
__index = function (_, key)
local got_virt = virt_G[key]
if got_virt ~= nil then
return got_virt
end
virt_G[key] = os.getenv(key)
return virt_G[key]
end,
__newindex = function (_, key, value)
if type(value) == 'string' then
os.setenv(key, value)
virt_G[key] = value
else
if type(virt_G[key]) == 'string' then
os.setenv(key, '')
end
virt_G[key] = value
end
end,
})
end

View File

@ -1,264 +0,0 @@
-- The preload file initializes everything else for our shell
local ansikit = require 'ansikit'
local bait = require 'bait'
local commander = require 'commander'
local fs = require 'fs'
local lunacolors = require 'lunacolors'
local _ = require 'succulent' -- Function additions
local oldDir = hilbish.cwd()
local shlvl = tonumber(os.getenv 'SHLVL')
if shlvl ~= nil then os.setenv('SHLVL', tostring(shlvl + 1)) else os.setenv('SHLVL', '0') end
-- Builtins
local recentDirs = {}
commander.register('cd', function (args)
if #args > 0 then
local path = table.concat(args, ' '):gsub('$%$','\0'):gsub('${([%w_]+)}', os.getenv)
:gsub('$([%w_]+)', os.getenv):gsub('%z','$'):gsub('^%s*(.-)%s*$', '%1')
if path == '-' then
path = oldDir
print(path)
end
oldDir = hilbish.cwd()
local ok, err = pcall(function() fs.cd(path) end)
if not ok then
print(err:sub(17))
return 1
end
bait.throw('cd', path)
-- add to table of recent dirs
recentDirs[11] = nil
if recentDirs[#recentDirs - 1] ~= path then
table.insert(recentDirs, 1, path)
end
return
end
fs.cd(hilbish.home)
bait.throw('cd', hilbish.home)
table.insert(recentDirs, 1, hilbish.home)
recentDirs[11] = nil
end)
commander.register('exit', function()
bait.throw('hilbish.exit')
os.exit(0)
end)
commander.register('doc', function(args)
local moddocPath = hilbish.dataDir .. '/docs/'
local modDocFormat = [[
%s
%s
# Functions
]]
if #args > 0 then
local mod = args[1]
local f = io.open(moddocPath .. mod .. '.txt', 'rb')
local funcdocs = nil
if not f then
-- assume subdir
-- dataDir/docs/<mod>/<mod>.txt
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 moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= 'index.txt' end)
local subdocs = table.map(moddocs, function(fname)
return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.txt', '')))
end)
if subdocName == 'index' then
funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ')
end
end
if not funcdocs then
funcdocs = f:read '*a'
end
local desc = ''
local ok = pcall(require, mod)
local backtickOccurence = 0
local formattedFuncs = lunacolors.format(funcdocs:sub(1, #funcdocs - 1):gsub('`', function()
backtickOccurence = backtickOccurence + 1
if backtickOccurence % 2 == 0 then
return '{reset}'
else
return '{underline}{green}'
end
end))
if ok then
local props = {}
local propstr = ''
local modDesc = ''
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
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()
return
end
local modules = table.map(fs.readdir(moddocPath), function(f)
return lunacolors.underline(lunacolors.blue(string.gsub(f, '.txt', '')))
end)
io.write [[
Welcome to Hilbish's doc tool! Here you can find documentation for builtin
functions and other things.
Usage: doc <section> [subdoc]
A section is a module or a literal section and a subdoc is a subsection for it.
Available sections: ]]
print(table.concat(modules, ', '))
end)
local helpTexts = {
[[
Hello there! Welcome to Hilbish, the comfy and nice little shell for
Lua users and fans. Hilbish is configured with Lua, and its
scripts are also in Lua. It also runs both Lua and shell script when
interactive (aka normal usage).
]],
[[
What does that mean for you, the user? It means that if you prefer to
use Lua for scripting instead of shell script but still have ordinary
shell usage for interactive use.
]],
[[
If this is your first time using Hilbish and Lua, check out the
Programming in Lua book here: https://www.lua.org/pil
After (or if you already know Lua) check out the doc command.
It is an in shell tool for documentation about Hilbish provided
functions and modules.
]],
[[
If you've updated from a pre-1.0 version (0.7.1 as an example)
you'll want to move your config from ~/.hilbishrc.lua to
]] ..
hilbish.userDir.config .. '/hilbish/init.lua' ..
[[
and also change all global functions (prompt, alias) to be
in the hilbish module (hilbish.prompt, hilbish.alias as examples).
And if this is your first time (most likely), you can copy a config
from ]] .. hilbish.dataDir,
[[
Since 1.0 is a big release, you'll want to check the changelog
at https://github.com/Rosettea/Hilbish/releases/tag/v1.0.0
to find more breaking changes.
]]
}
commander.register('guide', function()
ansikit.clear()
ansikit.cursorTo(0, 0)
for _, text in ipairs(helpTexts) do
print(text)
local out = hilbish.read('Hit enter to continue ')
ansikit.clear()
ansikit.cursorTo(0, 0)
if not out then
return
end
end
print 'Hope you enjoy using Hilbish!'
end)
do
local virt_G = { }
setmetatable(_G, {
__index = function (_, key)
local got_virt = virt_G[key]
if got_virt ~= nil then
return got_virt
end
virt_G[key] = os.getenv(key)
return virt_G[key]
end,
__newindex = function (_, key, value)
if type(value) == 'string' then
os.setenv(key, value)
virt_G[key] = value
else
if type(virt_G[key]) == 'string' then
os.setenv(key, '')
end
virt_G[key] = value
end
end,
})
end
commander.register('cdr', function(args)
if not args[1] then
print(lunacolors.format [[
cdr: change directory to one which has been recently visied
usage: cdr <index>
to get a list of recent directories, use {green}{underline}cdr list{reset}]])
return
end
if args[1] == 'list' then
if #recentDirs == 0 then
print 'No directories have been visited.'
return 1
end
print(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))
return 1
end
if not recentDirs[index] then
print(string.format('no recent directory found at index %s', index))
return 1
end
fs.cd(recentDirs[index])
end)
-- Hook handles
bait.catch('command.not-found', function(cmd)
print(string.format('hilbish: %s not found', cmd))
end)
bait.catch('command.not-executable', function(cmd)
print(string.format('hilbish: %s: not executable', cmd))
end)

206
rl.go
View File

@ -84,151 +84,91 @@ func newLineReader(prompt string, noHist bool) *lineReader {
return highlighted return highlighted
} }
rl.TabCompleter = func(line []rune, pos int, _ readline.DelayedTabContext) (string, []*readline.CompletionGroup) { rl.TabCompleter = func(line []rune, pos int, _ readline.DelayedTabContext) (string, []*readline.CompletionGroup) {
ctx := string(line) term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 2, false)
compHandle := hshMod.Get(rt.StringValue("completion")).AsTable().Get(rt.StringValue("handler"))
err := rt.Call(l.MainThread(), compHandle, []rt.Value{rt.StringValue(string(line)),
rt.IntValue(int64(pos))}, term)
var compGroup []*readline.CompletionGroup var compGroups []*readline.CompletionGroup
if err != nil {
ctx = strings.TrimLeft(ctx, " ") return "", compGroups
if len(ctx) == 0 {
return "", compGroup
} }
fields := strings.Split(ctx, " ") luaCompGroups := term.Get(0)
if len(fields) == 0 { luaPrefix := term.Get(1)
return "", compGroup
if luaCompGroups.Type() != rt.TableType {
return "", compGroups
} }
query := fields[len(fields) - 1]
ctx = aliases.Resolve(ctx) groups := luaCompGroups.AsTable()
// prefix is optional
pfx, _ := luaPrefix.TryString()
if len(fields) == 1 { util.ForEach(groups, func(key rt.Value, val rt.Value) {
completions, prefix := binaryComplete(query, ctx, fields) if key.Type() != rt.IntType || val.Type() != rt.TableType {
return
}
compGroup = append(compGroup, &readline.CompletionGroup{ valTbl := val.AsTable()
TrimSlash: false, luaCompType := valTbl.Get(rt.StringValue("type"))
NoSpace: true, luaCompItems := valTbl.Get(rt.StringValue("items"))
Suggestions: completions,
if luaCompType.Type() != rt.StringType || luaCompItems.Type() != rt.TableType {
return
}
items := []string{}
itemDescriptions := make(map[string]string)
util.ForEach(luaCompItems.AsTable(), func(lkey rt.Value, lval rt.Value) {
if keytyp := lkey.Type(); keytyp == rt.StringType {
// ['--flag'] = {'description', '--flag-alias'}
itemName, ok := lkey.TryString()
vlTbl, okk := lval.TryTable()
if !ok && !okk {
// TODO: error
return
}
items = append(items, itemName)
itemDescription, ok := vlTbl.Get(rt.IntValue(1)).TryString()
if !ok {
// TODO: error
return
}
itemDescriptions[itemName] = itemDescription
} else if keytyp == rt.IntType {
vlStr, ok := lval.TryString()
if !ok {
// TODO: error
return
}
items = append(items, vlStr)
} else {
// TODO: error
return
}
}) })
return prefix, compGroup var dispType readline.TabDisplayType
} else { switch luaCompType.AsString() {
if completecb, ok := luaCompletions["command." + fields[0]]; ok { case "grid": dispType = readline.TabDisplayGrid
luaFields := rt.NewTable() case "list": dispType = readline.TabDisplayList
for i, f := range fields { // need special cases, will implement later
luaFields.Set(rt.IntValue(int64(i + 1)), rt.StringValue(f)) //case "map": dispType = readline.TabDisplayMap
}
// we must keep the holy 80 cols
luacompleteTable, err := rt.Call1(l.MainThread(),
rt.FunctionValue(completecb), rt.StringValue(query),
rt.StringValue(ctx), rt.TableValue(luaFields))
if err != nil {
return "", compGroup
}
/*
as an example with git,
completion table should be structured like:
{
{
items = {
'add',
'clone',
'init'
},
type = 'grid'
},
{
items = {
'-c',
'--git-dir'
},
type = 'list'
}
}
^ a table of completion groups.
it is the responsibility of the completer
to work on subcommands and subcompletions
*/
if cmpTbl, ok := luacompleteTable.TryTable(); ok {
util.ForEach(cmpTbl, func(key rt.Value, val rt.Value) {
if key.Type() != rt.IntType && val.Type() != rt.TableType {
return
}
valTbl := val.AsTable()
luaCompType := valTbl.Get(rt.StringValue("type"))
luaCompItems := valTbl.Get(rt.StringValue("items"))
if luaCompType.Type() != rt.StringType && luaCompItems.Type() != rt.TableType {
return
}
items := []string{}
itemDescriptions := make(map[string]string)
util.ForEach(luaCompItems.AsTable(), func(lkey rt.Value, lval rt.Value) {
if keytyp := lkey.Type(); keytyp == rt.StringType {
// ['--flag'] = {'description', '--flag-alias'}
itemName, ok := lkey.TryString()
vlTbl, okk := lval.TryTable()
if !ok && !okk {
// TODO: error
return
}
items = append(items, itemName)
itemDescription, ok := vlTbl.Get(rt.IntValue(1)).TryString()
if !ok {
// TODO: error
return
}
itemDescriptions[itemName] = itemDescription
} else if keytyp == rt.IntType {
vlStr, ok := lval.TryString()
if !ok {
// TODO: error
return
}
items = append(items, vlStr)
} else {
// TODO: error
return
}
})
var dispType readline.TabDisplayType
switch luaCompType.AsString() {
case "grid": dispType = readline.TabDisplayGrid
case "list": dispType = readline.TabDisplayList
// need special cases, will implement later
//case "map": dispType = readline.TabDisplayMap
}
compGroup = append(compGroup, &readline.CompletionGroup{
DisplayType: dispType,
Descriptions: itemDescriptions,
Suggestions: items,
TrimSlash: false,
NoSpace: true,
})
})
}
} }
if len(compGroup) == 0 { compGroups = append(compGroups, &readline.CompletionGroup{
completions, p := fileComplete(query, ctx, fields) DisplayType: dispType,
fcompGroup := []*readline.CompletionGroup{{ Descriptions: itemDescriptions,
TrimSlash: false, Suggestions: items,
NoSpace: true, TrimSlash: false,
Suggestions: completions, NoSpace: true,
}} })
})
return p, fcompGroup return pfx, compGroups
}
}
return "", compGroup
} }
return &lineReader{ return &lineReader{

View File

@ -25,11 +25,17 @@ func Document(module *rt.Table, doc string) {
// It is accessible via the __docProp metatable. It is a table of the names of the fields. // It is accessible via the __docProp metatable. It is a table of the names of the fields.
func SetField(rtm *rt.Runtime, module *rt.Table, field string, value rt.Value, doc string) { func SetField(rtm *rt.Runtime, module *rt.Table, field string, value rt.Value, doc string) {
// TODO: ^ rtm isnt needed, i should remove it // TODO: ^ rtm isnt needed, i should remove it
SetFieldDoc(module, field, doc)
module.Set(rt.StringValue(field), value)
}
// SetFieldDoc sets the __docProp metatable for a field on the
// module.
func SetFieldDoc(module *rt.Table, field, doc string) {
mt := module.Metatable() mt := module.Metatable()
if mt == nil { if mt == nil {
mt = rt.NewTable() mt = rt.NewTable()
module.SetMetatable(mt) module.SetMetatable(mt)
} }
@ -41,7 +47,15 @@ func SetField(rtm *rt.Runtime, module *rt.Table, field string, value rt.Value, d
} }
docProp.AsTable().Set(rt.StringValue(field), rt.StringValue(doc)) docProp.AsTable().Set(rt.StringValue(field), rt.StringValue(doc))
module.Set(rt.StringValue(field), value) }
// SetFieldProtected sets a field in a protected table. A protected table
// is one which has a metatable proxy to ensure no overrides happen to it.
// It sets the field in the table and sets the __docProp metatable on the
// user facing table.
func SetFieldProtected(module, realModule *rt.Table, field string, value rt.Value, doc string) {
SetFieldDoc(module, field, doc)
realModule.Set(rt.StringValue(field), value)
} }
// DoString runs the code string in the Lua runtime. // DoString runs the code string in the Lua runtime.

View File

@ -15,7 +15,7 @@ var (
.. hilbish.userDir.config .. '/hilbish/?/?.lua;' .. hilbish.userDir.config .. '/hilbish/?/?.lua;'
.. hilbish.userDir.config .. '/hilbish/?.lua'` .. hilbish.userDir.config .. '/hilbish/?.lua'`
dataDir = "/usr/local/share/hilbish" dataDir = "/usr/local/share/hilbish"
preloadPath = dataDir + "/prelude/init.lua" preloadPath = dataDir + "/nature/init.lua"
sampleConfPath = dataDir + "/.hilbishrc.lua" // Path to default/sample config sampleConfPath = dataDir + "/.hilbishrc.lua" // Path to default/sample config
defaultConfDir = getenv("XDG_CONFIG_HOME", "~/.config") defaultConfDir = getenv("XDG_CONFIG_HOME", "~/.config")
) )

View File

@ -15,7 +15,7 @@ var (
.. hilbish.userDir.config .. '/hilbish/?/?.lua;' .. hilbish.userDir.config .. '/hilbish/?/?.lua;'
.. hilbish.userDir.config .. '/hilbish/?.lua'` .. hilbish.userDir.config .. '/hilbish/?.lua'`
dataDir = "/usr/share/hilbish" dataDir = "/usr/share/hilbish"
preloadPath = dataDir + "/prelude/init.lua" preloadPath = dataDir + "/nature/init.lua"
sampleConfPath = dataDir + "/.hilbishrc.lua" // Path to default/sample config sampleConfPath = dataDir + "/.hilbishrc.lua" // Path to default/sample config
defaultConfDir = "" defaultConfDir = ""
) )

View File

@ -9,7 +9,7 @@ var (
.. hilbish.userDir.config .. '\\Hilbish\\libs\\?\\?.lua;' .. hilbish.userDir.config .. '\\Hilbish\\libs\\?\\?.lua;'
.. hilbish.userDir.config .. '\\Hilbish\\libs\\?.lua;'` .. hilbish.userDir.config .. '\\Hilbish\\libs\\?.lua;'`
dataDir = "~\\Appdata\\Roaming\\Hilbish" // ~ and \ gonna cry? dataDir = "~\\Appdata\\Roaming\\Hilbish" // ~ and \ gonna cry?
preloadPath = dataDir + "\\prelude\\init.lua" preloadPath = dataDir + "\\nature\\init.lua"
sampleConfPath = dataDir + "\\hilbishrc.lua" // Path to default/sample config sampleConfPath = dataDir + "\\hilbishrc.lua" // Path to default/sample config
defaultConfDir = "" defaultConfDir = ""
) )