Compare commits

...

13 Commits

Author SHA1 Message Date
TorchedSammy f3f49fc398
docs: make changelog up to date 2022-03-05 21:34:59 -04:00
TorchedSammy 893d72a236
chore: prepare for 1.0 release 2022-03-05 21:29:38 -04:00
TorchedSammy 0637f2763b
fix: dont write history automatically with rl library 2022-03-05 21:29:13 -04:00
TorchedSammy 1cb536b1ac
fix: write to bottom of history file instead of at top 2022-03-05 21:26:03 -04:00
TorchedSammy 6740e012a5
fix: finish properly when command exit is successful after contine prompt 2022-03-05 21:25:32 -04:00
TorchedSammy b1ad90443e docs: [ci] generate new docs 2022-03-05 20:13:15 +00:00
TorchedSammy 76c94bfcce
docs: fix docs for hilbish.complete 2022-03-05 16:12:46 -04:00
TorchedSammy 0ed365170c
refactor!: completion api, add hilbish.completion interface
this is a pretty big commit which mainly contains a refactor
and breaking change to how command completions are done.

before that, a hilbish.completion interface has been added
which for now just has 2 functions (`files` and `bins`)
for completions of normal files and executables.

hilbish.complete is now expected to return a table of
"completions groups," which are as the name suggests a group
for a completion. a completion group is a table which has
the fields `type`, which can be either `list` or `grid`,
and `items`, being an array (or string keyed table) of items

if an item is string keyed the item itself is the key name
and the value is a table with the first value in it being the
description for the item. this description is only applied
with the list type.

this is probably the longest commit message ive written
2022-03-05 15:59:00 -04:00
TorchedSammy 70724ec015
feat: make hilbish.history functional for go readline 2022-03-05 15:57:46 -04:00
TorchedSammy f1dfd59c4c
fix: add back prompt global var
fixes an issue with multiline prompt overriding user's prompt
permanently
2022-03-05 15:46:38 -04:00
TorchedSammy a0dff5babf
fix: remove print in history handler 2022-03-05 15:23:17 -04:00
TorchedSammy 058d6ac456
chore: update readline 2022-03-05 14:40:38 -04:00
TorchedSammy 9f206ebed0
fix: export hilbish.complete 2022-03-05 11:38:33 -04:00
12 changed files with 281 additions and 163 deletions

View File

@ -1,6 +1,6 @@
# 🎀 Changelog
## Unreleased
## [1.0.0] - 2021-03-04
### Added
- MacOS is now officialy supported, default compile time vars have been added
for it
@ -18,6 +18,7 @@ it finds the path to `binName` in $PATH
(like it always was) or Vim via `hilbish.inputMode()`
- Changing Vim mode throws a `hilbish.vimMode` hook
- The current Vim mode is also accessible with the `hilbish.vimMode` property
- Print errors in `hilbish.timeout()` and `hilbish.goro()` callbacks
### Fixed
- Tab completion for executables
@ -31,19 +32,20 @@ it finds the path to `binName` in $PATH
- Alias expansion with quotes
- Add full command to history in the case of incomplete input
- `hilbish.exec()` now has a windows substitute
- Fixed case of successful command after prompted for more input not writing to history
### Changed
- The minimal config is truly minimal now
- Default config is no longer copied to user's config and is instead ran its location
#### Breaking Changes
(there were a lot...)
(there were a lot...)
- Change default SHLVL to 0 instead of 1
- ~/.hilbishrc.lua will no longer be run by default, it now
only uses the paths mentioned below.
- Changed Hilbish's config path to something more suited
according to the OS (`$XDG_CONFIG_HOME/hilbish/init.lua` on Linux,
`~/Library/Application Support/hilbish/init.lua` on MacOS and
(`%APPDATA%/hilbish/init.lua` on Windows). Previously on Unix-like it was
(`%APPDATA%/hilbish/init.lua` on Windows). Previously on Unix-like it was
`$XDG_CONFIG_HOME/hilbish/hilbishrc.lua`
- The history path has been changed to a better suited path.
On Linux, it is `$XDG_DATA_HOME/hilbish/.hilbish-history` and for others it is
@ -53,6 +55,30 @@ as it functions the same but is OS agnostic
- `hilbish.flag()` has been removed
- `~/.hprofile.lua` has been removed, instead check in your config if `hilbish.login`
is true
- `hilbish.complete()` has had a slight refactor to fit with the new readline library.
It now expects a table of "completion groups" which are just tables with the
`type` and `items` keys. Here is a (more or less) complete example of how it works now:
```lua
hilbish.complete('command.git', function()
return {
{
items = {
'add',
'clone'
},
type = 'grid'
},
{
items = {
['--git-dir'] = {'Description of flag'},
'-c'
},
type = 'list'
}
}
end)
```
Completer functions are now also expected to handle subcommands/subcompletions
## [0.7.1] - 2021-11-22
### Fixed
@ -161,9 +187,9 @@ An absolutely massive release. Probably the biggest yet, includes a bunch of fix
### Added
- `-n` flag, which checks Lua for syntax errors without running it
- `exec(command)` function, acts like the `exec` builtin in sh
- `exec(command)` function, acts like the `exec` builtin in sh
- Example: `exec 'awesome'` in an .xinitrc file with Hilbish as shebang
- Commands from commander can now `return` an exit code
- Commands from commander can now `return` an exit code
```lua
commander.register('false', function()
return 1
@ -176,7 +202,7 @@ When `false` is run, it will have the exit code of `1`, this is shorter/easier t
- Recursive aliases
- At the moment this only works for the first argument
- Hilbish can now be used with Hilbiline if compiled to do so (currently only for testing purposes)
- `goro(func)` runs a `func`tion in a goroutine. With channels that gopher-lua also provides, one can do parallelism and concurrency in Lua (but go style).
- `goro(func)` runs a `func`tion in a goroutine. With channels that gopher-lua also provides, one can do parallelism and concurrency in Lua (but go style).
- `coroutine` no those dont exist they dont matter `goro` is easier
- `cd -` will change to the previous directory
- `hilbish.cwd()` gets the current working directory
@ -215,7 +241,7 @@ When `false` is run, it will have the exit code of `1`, this is shorter/easier t
### Added
- Ctrl C in the prompt now cancels/clear input (I've needed this for so long also)
- Made Hilbish act like a login shell on login
- Made Hilbish act like a login shell on login
- If Hilbish is the login shell, or the `-l`/`--login` flags are used, Hilbish will use an additional `~/.hprofile.lua` file, you can use this to set environment variables once on login
- `-c` has been added to run a single command (this works exactly like being in the prompt would, so Lua works as well)
- `-i` (also `--interactive`) has been added to force Hilbish to be an interactive shell in cases where it usually wont be (like with `-c`)
@ -223,13 +249,13 @@ When `false` is run, it will have the exit code of `1`, this is shorter/easier t
- Added a `mulitline` hook that's thrown when in the continue/multiline prompt
- Added `appendPath` function to append a directory to `$PATH`
- `~` will be expanded to `$HOME` as well
- A utility `string.split` function is now added
- A utility `string.split` function is now added
- `string.split(str, delimiter)`
- Added a `_user` variable to easily get current user's name
### Changed
- **BREAKING Change**: [Lunacolors](https://github.com/Hilbis/Lunacolors) has replaced ansikit for formatting colors, which means the format function has been removed from ansikit and moved to Lunacolors.
- **BREAKING Change**: [Lunacolors](https://github.com/Hilbis/Lunacolors) has replaced ansikit for formatting colors, which means the format function has been removed from ansikit and moved to Lunacolors.
- Users must replace ansikit with `lunacolors` in their config files
- A getopt-like library is now used for command line flag parsing
- `cd` builtin now supports using environment variables
@ -341,6 +367,7 @@ This input for example will prompt for more input to complete:
First "stable" release of Hilbish.
[0.7.1]: https://github.com/Rosettea/Hilbish/compare/v0.7.1...v1.0.0
[0.7.1]: https://github.com/Rosettea/Hilbish/compare/v0.7.0...v0.7.1
[0.7.0]: https://github.com/Rosettea/Hilbish/compare/v0.6.1...v0.7.0
[0.6.1]: https://github.com/Rosettea/Hilbish/compare/v0.6.0...v0.6.1
@ -354,5 +381,5 @@ First "stable" release of Hilbish.
[0.2.0]: https://github.com/Rosettea/Hilbish/compare/v0.1.2...v0.2.0
[0.1.2]: https://github.com/Rosettea/Hilbish/compare/v0.1.1...v0.1.2
[0.1.1]: https://github.com/Rosettea/Hilbish/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/Rosettea/Hilbish/compare/v0.0.12...v0.1.0
[0.1.0]: https://github.com/Rosettea/Hilbish/compare/v0.0.12...v0.1.0
[0.0.12]: https://github.com/Rosettea/Hilbish/releases/tag/v0.0.12

68
api.go
View File

@ -23,6 +23,7 @@ import (
var exports = map[string]lua.LGFunction {
"alias": hlalias,
"appendPath": hlappendPath,
"complete": hlcomplete,
"cwd": hlcwd,
"exec": hlexec,
"goro": hlgoro,
@ -73,10 +74,11 @@ The nice lil shell for {blue}Lua{reset} fanatics!
util.SetField(L, hshuser, "data", lua.LString(userDataDir), "XDG data directory")
util.Document(L, hshuser, "User directories to store configs and/or modules.")
L.SetField(mod, "userDir", hshuser)
// hilbish.os table
hshos := L.NewTable()
info, _ := osinfo.GetOSInfo()
util.SetField(L, hshos, "family", lua.LString(info.Family), "Family name of the current OS")
util.SetField(L, hshos, "name", lua.LString(info.Name), "Pretty name of the current OS")
util.SetField(L, hshos, "version", lua.LString(info.Version), "Version of the current OS")
@ -94,11 +96,63 @@ The nice lil shell for {blue}Lua{reset} fanatics!
util.Document(L, historyModule, "History interface for Hilbish.")
L.SetField(mod, "history", historyModule)
// hilbish.completions table
hshcomp := L.NewTable()
util.SetField(L, hshcomp, "files", L.NewFunction(luaFileComplete), "Completer for files")
util.SetField(L, hshcomp, "bins", L.NewFunction(luaBinaryComplete), "Completer for executables/binaries")
util.Document(L, hshcomp, "Completions interface for Hilbish.")
L.SetField(mod, "completion", hshcomp)
L.Push(mod)
return 1
}
func luaFileComplete(L *lua.LState) int {
query := L.CheckString(1)
ctx := L.CheckString(2)
fields := L.CheckTable(3)
var fds []string
fields.ForEach(func(k lua.LValue, v lua.LValue) {
fds = append(fds, v.String())
})
completions := fileComplete(query, ctx, fds)
luaComps := L.NewTable()
for _, comp := range completions {
luaComps.Append(lua.LString(comp))
}
L.Push(luaComps)
return 1
}
func luaBinaryComplete(L *lua.LState) int {
query := L.CheckString(1)
ctx := L.CheckString(2)
fields := L.CheckTable(3)
var fds []string
fields.ForEach(func(k lua.LValue, v lua.LValue) {
fds = append(fds, v.String())
})
completions, _ := binaryComplete(query, ctx, fds)
luaComps := L.NewTable()
for _, comp := range completions {
luaComps.Append(lua.LString(comp))
}
L.Push(luaComps)
return 1
}
func setVimMode(mode string) {
hooks.Em.Emit("hilbish.vimMode", mode)
util.SetField(l, hshMod, "vimMode", lua.LString(mode), "Current Vim mode of Hilbish (nil if not in Vim mode)")
@ -174,7 +228,7 @@ These will be formatted and replaced with the appropriate values.
--- @param str string
*/
func hlprompt(L *lua.LState) int {
prompt := L.CheckString(1)
prompt = L.CheckString(1)
lr.SetPrompt(fmtPrompt(prompt))
return 0
@ -346,8 +400,10 @@ func hlinterval(L *lua.LState) int {
// Registers a completion handler for `scope`.
// A `scope` is currently only expected to be `command.<cmd>`,
// replacing <cmd> with the name of the command (for example `command.git`).
// `cb` must be a function that returns a table of the entries to complete.
// Nested tables will be used as sub-completions.
// `cb` must be a function that returns a table of "completion groups."
// A completion group is a table with the keys `items` and `type`.
// `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 cb function
func hlcomplete(L *lua.LState) int {
@ -385,7 +441,7 @@ func hlwhich(L *lua.LState) int {
l.Push(lua.LNil)
return 1
}
l.Push(lua.LString(path))
return 1
}

View File

@ -15,7 +15,7 @@ func fileComplete(query, ctx string, fields []string) []string {
completions, _ = matchPath(strings.Replace(query, "~", curuser.HomeDir, 1), query)
}
}
if len(completions) == 0 && len(fields) > 1 {
completions, _ = matchPath("./" + query, query)
}
@ -23,6 +23,55 @@ func fileComplete(query, ctx string, fields []string) []string {
return completions
}
func binaryComplete(query, ctx string, fields []string) ([]string, string) {
var completions []string
prefixes := []string{"./", "../", "/", "~/"}
for _, prefix := range prefixes {
if strings.HasPrefix(query, prefix) {
fileCompletions := fileComplete(query, ctx, fields)
if len(fileCompletions) != 0 {
for _, f := range fileCompletions {
name := strings.Replace(query + f, "~", curuser.HomeDir, 1)
if info, err := os.Stat(name); err == nil && info.Mode().Perm() & 0100 == 0 {
continue
}
completions = append(completions, f)
}
}
return completions, ""
}
}
// filter out executables, but in path
for _, dir := range filepath.SplitList(os.Getenv("PATH")) {
// print dir to stderr for debugging
// search for an executable which matches our query string
if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil {
// get basename from matches
for _, match := range matches {
// check if we have execute permissions for our match
if info, err := os.Stat(match); err == nil && info.Mode().Perm() & 0100 == 0 {
continue
}
// get basename from match
name := filepath.Base(match)
// add basename to completions
completions = append(completions, name)
}
}
}
// add lua registered commands to completions
for cmdName := range commands {
if strings.HasPrefix(cmdName, query) {
completions = append(completions, cmdName)
}
}
return completions, query
}
func matchPath(path, pref string) ([]string, error) {
var entries []string
matches, err := filepath.Glob(path + "*")
@ -54,6 +103,6 @@ func matchPath(path, pref string) ([]string, error) {
entries = append(entries, name)
}
}
return entries, err
}

View File

@ -5,8 +5,10 @@ appendPath(dir) > Appends `dir` to $PATH
complete(scope, cb) > Registers a completion handler for `scope`.
A `scope` is currently only expected to be `command.<cmd>`,
replacing <cmd> with the name of the command (for example `command.git`).
`cb` must be a function that returns a table of the entries to complete.
Nested tables will be used as sub-completions.
`cb` must be a function that returns a table of "completion groups."
A completion group is a table with the keys `items` and `type`.
`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

View File

@ -14,8 +14,10 @@ function hilbish.appendPath(dir) end
--- Registers a completion handler for `scope`.
--- A `scope` is currently only expected to be `command.<cmd>`,
--- replacing <cmd> with the name of the command (for example `command.git`).
--- `cb` must be a function that returns a table of the entries to complete.
--- Nested tables will be used as sub-completions.
--- `cb` must be a function that returns a table of "completion groups."
--- A completion group is a table with the keys `items` and `type`.
--- `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 cb function
function hilbish.complete(scope, cb) end

View File

@ -61,6 +61,8 @@ func runInput(input, origInput string) {
} else if err != nil {
fmt.Fprintln(os.Stderr, err)
cmdFinish(1, cmdString, origInput)
} else {
cmdFinish(0, cmdString, origInput)
}
break
}
@ -111,7 +113,7 @@ func execCommand(cmd, old string) error {
NRet: 1,
Protect: true,
}, luacmdArgs)
if err != nil {
fmt.Fprintln(os.Stderr,
"Error in command:\n\n" + err.Error())

2
go.mod
View File

@ -16,6 +16,6 @@ require (
replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20211022004519-f67a49cb50f5
replace github.com/maxlandon/readline => github.com/Rosettea/readline-1 v0.0.0-20220305004552-071c22768119
replace github.com/maxlandon/readline => github.com/Rosettea/readline-1 v0.0.0-20220305123014-31d4d4214c93
replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10

2
go.sum
View File

@ -2,6 +2,8 @@ github.com/Rosettea/readline-1 v0.0.0-20220302012429-9ce5d23760f7 h1:LoY+kBKqMQq
github.com/Rosettea/readline-1 v0.0.0-20220302012429-9ce5d23760f7/go.mod h1:QiUAvbhg8PzCA4hlafCUl0bKD/0VmcocM4AjqtszAJs=
github.com/Rosettea/readline-1 v0.0.0-20220305004552-071c22768119 h1:rGsc30WTD5hk+oiXrAKsAIwZn5qBeTAdr29y3HhJh9E=
github.com/Rosettea/readline-1 v0.0.0-20220305004552-071c22768119/go.mod h1:QiUAvbhg8PzCA4hlafCUl0bKD/0VmcocM4AjqtszAJs=
github.com/Rosettea/readline-1 v0.0.0-20220305123014-31d4d4214c93 h1:SmOkAEm3O7si8CURZSsSN0ZxCQ8IGiiulw8LMZ1V1Yc=
github.com/Rosettea/readline-1 v0.0.0-20220305123014-31d4d4214c93/go.mod h1:QiUAvbhg8PzCA4hlafCUl0bKD/0VmcocM4AjqtszAJs=
github.com/Rosettea/readline-1 v0.1.0-beta.0.20211207003625-341c7985ad7d h1:KBttN41h/tPahmpaZavviwQ8q4rCkt5CD0HdVmfgPVA=
github.com/Rosettea/readline-1 v0.1.0-beta.0.20211207003625-341c7985ad7d/go.mod h1:QiUAvbhg8PzCA4hlafCUl0bKD/0VmcocM4AjqtszAJs=
github.com/Rosettea/readline-1 v0.1.0-beta.0.20220228022904-61f5e4493011 h1:+a61iNamZiO3Xru+l/1qtpKqqltVfWEm2r/rxH9hXxY=

View File

@ -19,26 +19,25 @@ func newFileHistory() (*fileHistory, error) {
return nil, err
}
}
itms := []string{""}
lines := strings.Split(string(data), "\n")
for i, l := range lines {
if i == len(lines) - 1 {
println(i, l)
continue
}
itms = append(itms, l)
}
f, err := os.OpenFile(defaultHistPath, os.O_RDWR | os.O_CREATE, 0755)
f, err := os.OpenFile(defaultHistPath, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0755)
if err != nil {
return nil, err
}
fh := &fileHistory{
items: itms,
f: f,
}
return fh, nil
}
@ -52,7 +51,7 @@ func (h *fileHistory) Write(line string) (int, error) {
return 0, err
}
h.f.Sync()
h.items = append(h.items, line)
return len(h.items), nil
}
@ -72,5 +71,5 @@ func (h *fileHistory) Len() int {
}
func (h *fileHistory) Dump() interface{} {
return nil
return h.items
}

View File

@ -40,7 +40,7 @@ func main() {
confDir, _ = os.UserConfigDir()
preloadPath = strings.Replace(preloadPath, "~", homedir, 1)
sampleConfPath = strings.Replace(sampleConfPath, "~", homedir, 1)
// i honestly dont know what directories to use for this
switch runtime.GOOS {
case "linux":
@ -165,6 +165,7 @@ func main() {
input:
for interactive {
lr.SetPrompt(fmtPrompt(prompt))
running = false
input, err := lr.Read()

237
rl.go
View File

@ -3,9 +3,7 @@ package main
import (
"fmt"
"io"
"path/filepath"
"strings"
"os"
"github.com/maxlandon/readline"
"github.com/yuin/gopher-lua"
@ -14,15 +12,18 @@ import (
type lineReader struct {
rl *readline.Instance
}
var fileHist *fileHistory
// other gophers might hate this naming but this is local, shut up
func newLineReader(prompt string) *lineReader {
rl := readline.NewInstance()
fileHist, err := newFileHistory()
fh, err := newFileHistory()
fileHist = fh // go stupid
if err != nil {
panic(err)
}
rl.SetHistoryCtrlR("file", fileHist)
rl.HistoryAutoWrite = false
rl.ShowVimMode = false
rl.ViModeCallback = func(mode readline.ViMode) {
modeStr := ""
@ -38,13 +39,8 @@ func newLineReader(prompt string) *lineReader {
rl.TabCompleter = func(line []rune, pos int, _ readline.DelayedTabContext) (string, []*readline.CompletionGroup) {
ctx := string(line)
var completions []string
compGroup := []*readline.CompletionGroup{
&readline.CompletionGroup{
TrimSlash: false,
NoSpace: true,
},
}
var compGroup []*readline.CompletionGroup
ctx = strings.TrimLeft(ctx, " ")
if len(ctx) == 0 {
@ -58,62 +54,28 @@ func newLineReader(prompt string) *lineReader {
query := fields[len(fields) - 1]
ctx = aliases.Resolve(ctx)
if len(fields) == 1 {
prefixes := []string{"./", "../", "/", "~/"}
for _, prefix := range prefixes {
if strings.HasPrefix(query, prefix) {
fileCompletions := fileComplete(query, ctx, fields)
if len(fileCompletions) != 0 {
for _, f := range fileCompletions {
name := strings.Replace(query + f, "~", curuser.HomeDir, 1)
if info, err := os.Stat(name); err == nil && info.Mode().Perm() & 0100 == 0 {
continue
}
completions = append(completions, f)
}
compGroup[0].Suggestions = completions
}
return "", compGroup
}
}
// filter out executables, but in path
for _, dir := range filepath.SplitList(os.Getenv("PATH")) {
// print dir to stderr for debugging
// search for an executable which matches our query string
if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil {
// get basename from matches
for _, match := range matches {
// check if we have execute permissions for our match
if info, err := os.Stat(match); err == nil && info.Mode().Perm() & 0100 == 0 {
continue
}
// get basename from match
name := filepath.Base(match)
// print name to stderr for debugging
// add basename to completions
completions = append(completions, name)
}
}
}
// add lua registered commands to completions
for cmdName := range commands {
if strings.HasPrefix(cmdName, query) {
completions = append(completions, cmdName)
}
}
compGroup[0].Suggestions = completions
return query, compGroup
if len(fields) == 1 {
completions, prefix := binaryComplete(query, ctx, fields)
compGroup = append(compGroup, &readline.CompletionGroup{
TrimSlash: false,
NoSpace: true,
Suggestions: completions,
})
return prefix, compGroup
} else {
if completecb, ok := luaCompletions["command." + fields[0]]; ok {
luaFields := l.NewTable()
for _, f := range fields {
luaFields.Append(lua.LString(f))
}
err := l.CallByParam(lua.P{
Fn: completecb,
NRet: 1,
Protect: true,
})
}, lua.LString(query), lua.LString(ctx), luaFields)
if err != nil {
return "", compGroup
@ -122,88 +84,86 @@ func newLineReader(prompt string) *lineReader {
luacompleteTable := l.Get(-1)
l.Pop(1)
/*
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.(*lua.LTable); ok {
cmpTbl.ForEach(func(key lua.LValue, value lua.LValue) {
// if key is a number (index), we just check and complete that
if key.Type() == lua.LTNumber {
// if we have only 2 fields then this is fine
if len(fields) == 2 {
if strings.HasPrefix(value.String(), fields[1]) {
completions = append(completions, value.String())
// completion group
if value.Type() == lua.LTTable {
luaCmpGroup := value.(*lua.LTable)
compType := luaCmpGroup.RawGet(lua.LString("type"))
compItems := luaCmpGroup.RawGet(lua.LString("items"))
if compType.Type() != lua.LTString {
l.RaiseError("bad type name for completion (expected string, got %v)", compType.Type().String())
}
}
} else if key.Type() == lua.LTString {
if len(fields) == 2 {
if strings.HasPrefix(key.String(), fields[1]) {
completions = append(completions, key.String())
if compItems.Type() != lua.LTTable {
l.RaiseError("bad items for completion (expected table, got %v)", compItems.Type().String())
}
} else {
// if we have more than 2 fields, we need to check if the key matches
// the current field and if it does, we need to check if the value is a string
// or table (nested sub completions)
if key.String() == fields[1] {
// if value is a table, we need to iterate over it
// and add each value to completions
// check if value is either a table or function
if value.Type() == lua.LTTable {
valueTbl := value.(*lua.LTable)
valueTbl.ForEach(func(key lua.LValue, value lua.LValue) {
val := value.String()
if val == "<file>" {
// complete files
completions = append(completions, fileComplete(query, ctx, fields)...)
} else {
if strings.HasPrefix(val, query) {
completions = append(completions, val)
}
}
})
} else if value.Type() == lua.LTFunction {
// if value is a function, we need to call it
// and add each value to completions
// completionsCtx is the context we pass to the function,
// removing 2 fields from the fields array
completionsCtx := strings.Join(fields[2:], " ")
err := l.CallByParam(lua.P{
Fn: value,
NRet: 1,
Protect: true,
}, lua.LString(query), lua.LString(completionsCtx))
if err != nil {
return
}
luacompleteTable := l.Get(-1)
l.Pop(1)
// just check if its actually a table and add it to the completions
if cmpTbl, ok := luacompleteTable.(*lua.LTable); ok {
cmpTbl.ForEach(func(key lua.LValue, value lua.LValue) {
val := value.String()
if strings.HasPrefix(val, query) {
completions = append(completions, val)
}
})
}
var items []string
itemDescriptions := make(map[string]string)
compItems.(*lua.LTable).ForEach(func(k lua.LValue, v lua.LValue) {
if k.Type() == lua.LTString {
// ['--flag'] = {'description', '--flag-alias'}
itm := v.(*lua.LTable)
items = append(items, k.String())
itemDescriptions[k.String()] = itm.RawGet(lua.LNumber(1)).String()
} else {
// throw lua error
// complete.cmdname: error message...
l.RaiseError("complete." + fields[0] + ": completion value is not a table or function")
items = append(items, v.String())
}
})
var dispType readline.TabDisplayType
switch compType.String() {
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(completions) == 0 {
if len(compGroup) == 0 {
completions = fileComplete(query, ctx, fields)
compGroup = append(compGroup, &readline.CompletionGroup{
TrimSlash: false,
NoSpace: true,
Suggestions: completions,
})
}
}
compGroup[0].Suggestions = completions
return "", compGroup
}
@ -240,7 +200,7 @@ func (lr *lineReader) SetPrompt(prompt string) {
}
func (lr *lineReader) AddHistory(cmd string) {
return
fileHist.Write(cmd)
}
func (lr *lineReader) ClearInput() {
@ -273,18 +233,35 @@ func (lr *lineReader) luaAddHistory(l *lua.LState) int {
return 0
}
func (lr *lineReader) luaSize(l *lua.LState) int {
func (lr *lineReader) luaSize(L *lua.LState) int {
L.Push(lua.LNumber(fileHist.Len()))
return 1
}
func (lr *lineReader) luaGetHistory(L *lua.LState) int {
idx := L.CheckInt(1)
cmd, _ := fileHist.GetLine(idx)
L.Push(lua.LString(cmd))
return 0
}
func (lr *lineReader) luaGetHistory(l *lua.LState) int {
return 0
}
func (lr *lineReader) luaAllHistory(L *lua.LState) int {
tbl := L.NewTable()
size := fileHist.Len()
for i := 1; i < size; i++ {
cmd, _ := fileHist.GetLine(i)
tbl.Append(lua.LString(cmd))
}
L.Push(tbl)
func (lr *lineReader) luaAllHistory(l *lua.LState) int {
return 0
}
func (lr *lineReader) luaClearHistory(l *lua.LState) int {
return 0
}

View File

@ -2,11 +2,12 @@ package main
// String vars that are free to be changed at compile time
var (
version = "v0.7.1"
version = "v1.0.0"
defaultConfDir = "" // ~ will be substituted for home, path for user's default config
defaultHistDir = ""
commonRequirePaths = "';./libs/?/init.lua;./?/init.lua;./?/?.lua'"
prompt string
multilinePrompt = "> "
)