mirror of
https://github.com/Hilbis/Hilbish
synced 2025-06-18 18:32:03 +00:00
changes the actual file format of docs to markup since that's basically what we have been using in the first place. the docgen command has been modified to write markdown headings with the function name and yaml metadata for easy consumption by hugo for the website. all other docs have been moved to markdown as well this is the main reason this is a "breaking" change users will have to reinstall hilbish (task uninstall and task install) to remove the old plaintext docs
310 lines
7.4 KiB
Go
310 lines
7.4 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"hilbish/util"
|
|
|
|
"github.com/maxlandon/readline"
|
|
rt "github.com/arnodel/golua/runtime"
|
|
)
|
|
|
|
type lineReader struct {
|
|
rl *readline.Instance
|
|
fileHist *fileHistory
|
|
}
|
|
var hinter *rt.Closure
|
|
var highlighter *rt.Closure
|
|
|
|
func newLineReader(prompt string, noHist bool) *lineReader {
|
|
rl := readline.NewInstance()
|
|
lr := &lineReader{
|
|
rl: rl,
|
|
}
|
|
|
|
// we don't mind hilbish.read rl instances having completion,
|
|
// but it cant have shared history
|
|
if !noHist {
|
|
lr.fileHist = newFileHistory(defaultHistPath)
|
|
rl.SetHistoryCtrlR("History", &luaHistory{})
|
|
rl.HistoryAutoWrite = false
|
|
}
|
|
rl.ShowVimMode = false
|
|
rl.ViModeCallback = func(mode readline.ViMode) {
|
|
modeStr := ""
|
|
switch mode {
|
|
case readline.VimKeys: modeStr = "normal"
|
|
case readline.VimInsert: modeStr = "insert"
|
|
case readline.VimDelete: modeStr = "delete"
|
|
case readline.VimReplaceOnce, readline.VimReplaceMany: modeStr = "replace"
|
|
}
|
|
setVimMode(modeStr)
|
|
}
|
|
rl.ViActionCallback = func(action readline.ViAction, args []string) {
|
|
actionStr := ""
|
|
switch action {
|
|
case readline.VimActionPaste: actionStr = "paste"
|
|
case readline.VimActionYank: actionStr = "yank"
|
|
}
|
|
hooks.Emit("hilbish.vimAction", actionStr, args)
|
|
}
|
|
rl.HintText = func(line []rune, pos int) []rune {
|
|
if hinter == nil {
|
|
return []rune{}
|
|
}
|
|
|
|
retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(hinter),
|
|
rt.StringValue(string(line)), rt.IntValue(int64(pos)))
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return []rune{}
|
|
}
|
|
|
|
hintText := ""
|
|
if luaStr, ok := retVal.TryString(); ok {
|
|
hintText = luaStr
|
|
}
|
|
|
|
return []rune(hintText)
|
|
}
|
|
rl.SyntaxHighlighter = func(line []rune) string {
|
|
if highlighter == nil {
|
|
return string(line)
|
|
}
|
|
retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(highlighter),
|
|
rt.StringValue(string(line)))
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return string(line)
|
|
}
|
|
|
|
highlighted := ""
|
|
if luaStr, ok := retVal.TryString(); ok {
|
|
highlighted = luaStr
|
|
}
|
|
|
|
return highlighted
|
|
}
|
|
rl.TabCompleter = func(line []rune, pos int, _ readline.DelayedTabContext) (string, []*readline.CompletionGroup) {
|
|
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 compGroups []*readline.CompletionGroup
|
|
if err != nil {
|
|
return "", compGroups
|
|
}
|
|
|
|
luaCompGroups := term.Get(0)
|
|
luaPrefix := term.Get(1)
|
|
|
|
if luaCompGroups.Type() != rt.TableType {
|
|
return "", compGroups
|
|
}
|
|
|
|
groups := luaCompGroups.AsTable()
|
|
// prefix is optional
|
|
pfx, _ := luaPrefix.TryString()
|
|
|
|
util.ForEach(groups, 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
|
|
}
|
|
|
|
compGroups = append(compGroups, &readline.CompletionGroup{
|
|
DisplayType: dispType,
|
|
Descriptions: itemDescriptions,
|
|
Suggestions: items,
|
|
TrimSlash: false,
|
|
NoSpace: true,
|
|
})
|
|
})
|
|
|
|
return pfx, compGroups
|
|
}
|
|
|
|
return lr
|
|
}
|
|
|
|
func (lr *lineReader) Read() (string, error) {
|
|
hooks.Emit("command.precmd", nil)
|
|
s, err := lr.rl.Readline()
|
|
// this is so dumb
|
|
if err == readline.EOF {
|
|
fmt.Println("")
|
|
return "", io.EOF
|
|
}
|
|
|
|
return s, err // might get another error
|
|
}
|
|
|
|
func (lr *lineReader) SetPrompt(p string) {
|
|
halfPrompt := strings.Split(p, "\n")
|
|
if len(halfPrompt) > 1 {
|
|
lr.rl.Multiline = true
|
|
lr.rl.SetPrompt(strings.Join(halfPrompt[:len(halfPrompt) - 1], "\n"))
|
|
lr.rl.MultilinePrompt = halfPrompt[len(halfPrompt) - 1:][0]
|
|
} else {
|
|
lr.rl.Multiline = false
|
|
lr.rl.MultilinePrompt = ""
|
|
lr.rl.SetPrompt(p)
|
|
}
|
|
if initialized && !running {
|
|
lr.rl.RefreshPromptInPlace("")
|
|
}
|
|
}
|
|
|
|
func (lr *lineReader) SetRightPrompt(p string) {
|
|
lr.rl.SetRightPrompt(p)
|
|
if initialized && !running {
|
|
lr.rl.RefreshPromptInPlace("")
|
|
}
|
|
}
|
|
|
|
func (lr *lineReader) AddHistory(cmd string) {
|
|
lr.fileHist.Write(cmd)
|
|
}
|
|
|
|
func (lr *lineReader) ClearInput() {
|
|
return
|
|
}
|
|
|
|
func (lr *lineReader) Resize() {
|
|
return
|
|
}
|
|
|
|
// #interface history
|
|
// command history
|
|
// The history interface deals with command history.
|
|
// This includes the ability to override functions to change the main
|
|
// method of saving history.
|
|
func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table {
|
|
lrLua := map[string]util.LuaExport{
|
|
"add": {lr.luaAddHistory, 1, false},
|
|
"all": {lr.luaAllHistory, 0, false},
|
|
"clear": {lr.luaClearHistory, 0, false},
|
|
"get": {lr.luaGetHistory, 1, false},
|
|
"size": {lr.luaSize, 0, false},
|
|
}
|
|
|
|
mod := rt.NewTable()
|
|
util.SetExports(rtm, mod, lrLua)
|
|
|
|
return mod
|
|
}
|
|
|
|
// #interface history
|
|
// add(cmd)
|
|
// Adds a command to the history.
|
|
// --- @param cmd string
|
|
func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
if err := c.Check1Arg(); err != nil {
|
|
return nil, err
|
|
}
|
|
cmd, err := c.StringArg(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
lr.AddHistory(cmd)
|
|
|
|
return c.Next(), nil
|
|
}
|
|
|
|
// #interface history
|
|
// size()
|
|
// Returns the amount of commands in the history.
|
|
// --- @returns number
|
|
func (lr *lineReader) luaSize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
return c.PushingNext1(t.Runtime, rt.IntValue(int64(lr.fileHist.Len()))), nil
|
|
}
|
|
|
|
// #interface history
|
|
// get(idx)
|
|
// Retrieves a command from the history based on the `idx`.
|
|
// --- @param idx number
|
|
func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
if err := c.Check1Arg(); err != nil {
|
|
return nil, err
|
|
}
|
|
idx, err := c.IntArg(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cmd, _ := lr.fileHist.GetLine(int(idx))
|
|
|
|
return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
|
|
}
|
|
|
|
func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
tbl := rt.NewTable()
|
|
size := lr.fileHist.Len()
|
|
|
|
for i := 1; i < size; i++ {
|
|
cmd, _ := lr.fileHist.GetLine(i)
|
|
tbl.Set(rt.IntValue(int64(i)), rt.StringValue(cmd))
|
|
}
|
|
|
|
return c.PushingNext1(t.Runtime, rt.TableValue(tbl)), nil
|
|
}
|
|
|
|
// #interface history
|
|
// clear()
|
|
// Deletes all commands from the history.
|
|
func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
lr.fileHist.clear()
|
|
return c.Next(), nil
|
|
}
|