mirror of
https://github.com/Hilbis/Hilbish
synced 2025-07-01 16:52:03 +00:00
refactor: rewrite parts of hilbish in lua
exec code, config running code is now written in lua.
This commit is contained in:
parent
49f2bae9e1
commit
e87136de7a
89
api.go
89
api.go
@ -6,10 +6,13 @@
|
||||
// #field user Username of the user
|
||||
// #field host Hostname of the machine
|
||||
// #field dataDir Directory for Hilbish data files, including the docs and default modules
|
||||
// #field defaultConfDir Default directory Hilbish runs its config file from
|
||||
// #field confFile File to run as Hilbish config, this is only set with the -C flag
|
||||
// #field interactive Is Hilbish in an interactive shell?
|
||||
// #field login Is Hilbish the login shell?
|
||||
// #field vimMode Current Vim input mode of Hilbish (will be nil if not in Vim input mode)
|
||||
// #field exitCode Exit code of the last executed command
|
||||
// #field exitCode If Hilbish is currently running any interactive input
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -44,10 +47,8 @@ var exports = map[string]util.LuaExport{
|
||||
"hinter": {hlhinter, 1, false},
|
||||
"multiprompt": {hlmultiprompt, 1, false},
|
||||
"prependPath": {hlprependPath, 1, false},
|
||||
"prompt": {hlprompt, 1, true},
|
||||
"inputMode": {hlinputMode, 1, false},
|
||||
"interval": {hlinterval, 2, false},
|
||||
"read": {hlread, 1, false},
|
||||
"timeout": {hltimeout, 2, false},
|
||||
"which": {hlwhich, 1, false},
|
||||
}
|
||||
@ -79,6 +80,8 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
||||
util.SetField(rtm, mod, "host", rt.StringValue(host))
|
||||
util.SetField(rtm, mod, "home", rt.StringValue(curuser.HomeDir))
|
||||
util.SetField(rtm, mod, "dataDir", rt.StringValue(dataDir))
|
||||
util.SetField(rtm, mod, "defaultConfDir", rt.StringValue(defaultConfDir))
|
||||
util.SetField(rtm, mod, "confFile", rt.StringValue(confPath))
|
||||
util.SetField(rtm, mod, "interactive", rt.BoolValue(interactive))
|
||||
util.SetField(rtm, mod, "login", rt.BoolValue(login))
|
||||
util.SetField(rtm, mod, "vimMode", rt.NilValue)
|
||||
@ -194,88 +197,6 @@ func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
return c.PushingNext1(t.Runtime, rt.StringValue(cwd)), nil
|
||||
}
|
||||
|
||||
// read(prompt) -> input (string)
|
||||
// Read input from the user, using Hilbish's line editor/input reader.
|
||||
// This is a separate instance from the one Hilbish actually uses.
|
||||
// Returns `input`, will be nil if Ctrl-D is pressed, or an error occurs.
|
||||
// #param prompt? string Text to print before input, can be empty.
|
||||
// #returns string|nil
|
||||
func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
luaprompt := c.Arg(0)
|
||||
if typ := luaprompt.Type(); typ != rt.StringType && typ != rt.NilType {
|
||||
return nil, errors.New("expected #1 to be a string")
|
||||
}
|
||||
prompt, ok := luaprompt.TryString()
|
||||
if !ok {
|
||||
// if we are here and `luaprompt` is not a string, it's nil
|
||||
// substitute with an empty string
|
||||
prompt = ""
|
||||
}
|
||||
|
||||
lualr := &lineReader{
|
||||
rl: readline.NewInstance(),
|
||||
}
|
||||
lualr.SetPrompt(prompt)
|
||||
|
||||
input, err := lualr.Read()
|
||||
if err != nil {
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.StringValue(input)), nil
|
||||
}
|
||||
|
||||
/*
|
||||
prompt(str, typ)
|
||||
Changes the shell prompt to the provided string.
|
||||
There are a few verbs that can be used in the prompt text.
|
||||
These will be formatted and replaced with the appropriate values.
|
||||
`%d` - Current working directory
|
||||
`%u` - Name of current user
|
||||
`%h` - Hostname of device
|
||||
#param str string
|
||||
#param typ? string Type of prompt, being left or right. Left by default.
|
||||
#example
|
||||
-- the default hilbish prompt without color
|
||||
hilbish.prompt '%u %d ∆'
|
||||
-- or something of old:
|
||||
hilbish.prompt '%u@%h :%d $'
|
||||
-- prompt: user@hostname: ~/directory $
|
||||
#example
|
||||
*/
|
||||
func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
err := c.Check1Arg()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typ := "left"
|
||||
// optional 2nd arg
|
||||
if len(c.Etc()) != 0 {
|
||||
ltyp := c.Etc()[0]
|
||||
var ok bool
|
||||
typ, ok = ltyp.TryString()
|
||||
if !ok {
|
||||
return nil, errors.New("bad argument to run (expected string, got " + ltyp.TypeName() + ")")
|
||||
}
|
||||
}
|
||||
|
||||
switch typ {
|
||||
case "left":
|
||||
prompt = p
|
||||
lr.SetPrompt(fmtPrompt(prompt))
|
||||
case "right":
|
||||
lr.SetRightPrompt(fmtPrompt(p))
|
||||
default:
|
||||
return nil, errors.New("expected prompt type to be right or left, got " + typ)
|
||||
}
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// multiprompt(str)
|
||||
// Changes the text prompt when Hilbish asks for more input.
|
||||
// This will show up when text is incomplete, like a missing quote
|
||||
|
5
exec.go
5
exec.go
@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
@ -10,10 +9,6 @@ import (
|
||||
//"github.com/yuin/gopher-lua/parse"
|
||||
)
|
||||
|
||||
var errNotExec = errors.New("not executable")
|
||||
var errNotFound = errors.New("not found")
|
||||
var runnerMode rt.Value = rt.NilValue
|
||||
|
||||
func runInput(input string, priv bool) {
|
||||
running = true
|
||||
runnerRun := hshMod.Get(rt.StringValue("runner")).AsTable().Get(rt.StringValue("run"))
|
||||
|
@ -9,6 +9,7 @@ package readline
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"hilbish/util"
|
||||
|
||||
@ -28,6 +29,8 @@ func (rl *Readline) luaLoader(rtm *rt.Runtime) (rt.Value, func()) {
|
||||
"readChar": {rlReadChar, 1, false},
|
||||
"setVimRegister": {rlSetRegister, 3, false},
|
||||
"log": {rlLog, 2, false},
|
||||
"prompt": {rlPrompt, 2, false},
|
||||
"refreshPrompt": {rlRefreshPrompt, 1, false},
|
||||
}
|
||||
util.SetExports(rtm, rlMethods, rlMethodss)
|
||||
|
||||
@ -251,6 +254,53 @@ func rlLog(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// #member
|
||||
// prompt(text)
|
||||
// Sets the prompt of the line reader. This is the text that shows up before user input.
|
||||
func rlPrompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.CheckNArgs(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rl, err := rlArg(c, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := c.StringArg(1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println(p)
|
||||
|
||||
halfPrompt := strings.Split(p, "\n")
|
||||
if len(halfPrompt) > 1 {
|
||||
rl.Multiline = true
|
||||
rl.SetPrompt(strings.Join(halfPrompt[:len(halfPrompt)-1], "\n"))
|
||||
rl.MultilinePrompt = halfPrompt[len(halfPrompt)-1:][0]
|
||||
} else {
|
||||
rl.Multiline = false
|
||||
rl.MultilinePrompt = ""
|
||||
rl.SetPrompt(p)
|
||||
}
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
func rlRefreshPrompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rl, err := rlArg(c, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rl.RefreshPromptInPlace("")
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
func rlArg(c *rt.GoCont, arg int) (*Readline, error) {
|
||||
j, ok := valueToRl(c.Arg(arg))
|
||||
if !ok {
|
||||
|
21
lua.go
21
lua.go
@ -16,14 +16,19 @@ import (
|
||||
"github.com/arnodel/golua/lib"
|
||||
"github.com/arnodel/golua/lib/debuglib"
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
"github.com/pborman/getopt"
|
||||
)
|
||||
|
||||
var minimalconf = `hilbish.prompt '& '`
|
||||
|
||||
func luaInit() {
|
||||
l = rt.New(os.Stdout)
|
||||
|
||||
loadLibs(l)
|
||||
luaArgs := rt.NewTable()
|
||||
for i, arg := range getopt.Args() {
|
||||
luaArgs.Set(rt.IntValue(int64(i)), rt.StringValue(arg))
|
||||
}
|
||||
|
||||
l.GlobalEnv().Set(rt.StringValue("args"), rt.TableValue(luaArgs))
|
||||
|
||||
yarnPool := yarn.New(yarnloadLibs)
|
||||
lib.LoadLibs(l, yarnPool.Loader)
|
||||
@ -36,6 +41,7 @@ func luaInit() {
|
||||
|
||||
err1 := util.DoFile(l, "nature/init.lua")
|
||||
if err1 != nil {
|
||||
fmt.Println(err1)
|
||||
err2 := util.DoFile(l, filepath.Join(dataDir, "nature", "init.lua"))
|
||||
if err2 != nil {
|
||||
fmt.Fprintln(os.Stderr, "Missing nature module, some functionality and builtins will be missing.")
|
||||
@ -98,14 +104,3 @@ func yarnloadLibs(r *rt.Runtime) {
|
||||
lib.LoadLibs(l, lr.rl.Loader)
|
||||
|
||||
}
|
||||
|
||||
func runConfig(confpath string) {
|
||||
if !interactive {
|
||||
return
|
||||
}
|
||||
err := util.DoFile(l, confpath)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err, "\nAn error has occured while loading your config! Falling back to minimal default config.")
|
||||
util.DoString(l, minimalconf)
|
||||
}
|
||||
}
|
||||
|
175
main.go
175
main.go
@ -2,40 +2,36 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"hilbish/util"
|
||||
"hilbish/golibs/bait"
|
||||
"hilbish/golibs/commander"
|
||||
"hilbish/util"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
"github.com/pborman/getopt"
|
||||
"github.com/maxlandon/readline"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
var (
|
||||
l *rt.Runtime
|
||||
l *rt.Runtime
|
||||
lr *lineReader
|
||||
|
||||
luaCompletions = map[string]*rt.Closure{}
|
||||
|
||||
confDir string
|
||||
confDir string
|
||||
userDataDir string
|
||||
curuser *user.User
|
||||
curuser *user.User
|
||||
|
||||
hooks *bait.Bait
|
||||
cmds *commander.Commander
|
||||
defaultConfPath string
|
||||
hooks *bait.Bait
|
||||
cmds *commander.Commander
|
||||
confPath string
|
||||
defaultHistPath string
|
||||
)
|
||||
|
||||
@ -62,7 +58,7 @@ func main() {
|
||||
// i honestly dont know what directories to use for this
|
||||
switch runtime.GOOS {
|
||||
case "linux", "darwin":
|
||||
userDataDir = getenv("XDG_DATA_HOME", curuser.HomeDir + "/.local/share")
|
||||
userDataDir = getenv("XDG_DATA_HOME", curuser.HomeDir+"/.local/share")
|
||||
default:
|
||||
// this is fine on windows, dont know about others
|
||||
userDataDir = confDir
|
||||
@ -75,7 +71,7 @@ func main() {
|
||||
// else do ~ substitution
|
||||
defaultConfDir = filepath.Join(util.ExpandHome(defaultConfDir), "hilbish")
|
||||
}
|
||||
defaultConfPath = filepath.Join(defaultConfDir, "init.lua")
|
||||
defaultConfPath := filepath.Join(defaultConfDir, "init.lua")
|
||||
if defaultHistDir == "" {
|
||||
defaultHistDir = filepath.Join(userDataDir, "hilbish")
|
||||
} else {
|
||||
@ -95,6 +91,7 @@ func main() {
|
||||
loginshflag := getopt.Lookup('l').Seen()
|
||||
interactiveflag := getopt.Lookup('i').Seen()
|
||||
noexecflag := getopt.Lookup('n').Seen()
|
||||
confPath = *configflag
|
||||
|
||||
if *helpflag {
|
||||
getopt.PrintUsage(os.Stdout)
|
||||
@ -105,7 +102,7 @@ func main() {
|
||||
interactive = true
|
||||
}
|
||||
|
||||
if fileInfo, _ := os.Stdin.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 || !term.IsTerminal(int(os.Stdin.Fd())) {
|
||||
if fileInfo, _ := os.Stdin.Stat(); (fileInfo.Mode()&os.ModeCharDevice) == 0 || !term.IsTerminal(int(os.Stdin.Fd())) {
|
||||
interactive = false
|
||||
}
|
||||
|
||||
@ -143,29 +140,6 @@ func main() {
|
||||
|
||||
go handleSignals()
|
||||
|
||||
// If user's config doesn't exixt,
|
||||
if _, err := os.Stat(defaultConfPath); os.IsNotExist(err) && *configflag == defaultConfPath {
|
||||
// Read default from current directory
|
||||
// (this is assuming the current dir is Hilbish's git)
|
||||
_, err := os.ReadFile(".hilbishrc.lua")
|
||||
confpath := ".hilbishrc.lua"
|
||||
if err != nil {
|
||||
// If it wasnt found, go to the real sample conf
|
||||
sampleConfigPath := filepath.Join(dataDir, ".hilbishrc.lua")
|
||||
_, err = os.ReadFile(sampleConfigPath)
|
||||
confpath = sampleConfigPath
|
||||
if err != nil {
|
||||
fmt.Println("could not find .hilbishrc.lua or", sampleConfigPath)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
runConfig(confpath)
|
||||
} else {
|
||||
runConfig(*configflag)
|
||||
}
|
||||
hooks.Emit("hilbish.init")
|
||||
|
||||
if fileInfo, _ := os.Stdin.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 {
|
||||
scanner := bufio.NewScanner(bufio.NewReader(os.Stdin))
|
||||
for scanner.Scan() {
|
||||
@ -180,12 +154,6 @@ func main() {
|
||||
}
|
||||
|
||||
if getopt.NArgs() > 0 {
|
||||
luaArgs := rt.NewTable()
|
||||
for i, arg := range getopt.Args() {
|
||||
luaArgs.Set(rt.IntValue(int64(i)), rt.StringValue(arg))
|
||||
}
|
||||
|
||||
l.GlobalEnv().Set(rt.StringValue("args"), rt.TableValue(luaArgs))
|
||||
err := util.DoFile(l, getopt.Arg(0))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
@ -195,71 +163,73 @@ func main() {
|
||||
}
|
||||
|
||||
initialized = true
|
||||
input:
|
||||
for interactive {
|
||||
running = false
|
||||
/*
|
||||
input:
|
||||
for interactive {
|
||||
running = false
|
||||
|
||||
input, err := lr.Read()
|
||||
input, err := lr.Read()
|
||||
|
||||
if err == io.EOF {
|
||||
// Exit if user presses ^D (ctrl + d)
|
||||
hooks.Emit("hilbish.exit")
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
if err == readline.CtrlC {
|
||||
fmt.Println("^C")
|
||||
hooks.Emit("hilbish.cancel")
|
||||
} else {
|
||||
// If we get a completely random error, print
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
if errors.Is(err, syscall.ENOTTY) {
|
||||
// what are we even doing here?
|
||||
panic("not a tty")
|
||||
}
|
||||
<-make(chan struct{})
|
||||
}
|
||||
continue
|
||||
}
|
||||
var priv bool
|
||||
if strings.HasPrefix(input, " ") {
|
||||
priv = true
|
||||
}
|
||||
|
||||
input = strings.TrimSpace(input)
|
||||
if len(input) == 0 {
|
||||
running = true
|
||||
hooks.Emit("command.exit", 0)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(input, "\\") {
|
||||
print("\n")
|
||||
for {
|
||||
input, err = continuePrompt(strings.TrimSuffix(input, "\\") + "\n", false)
|
||||
if err != nil {
|
||||
running = true
|
||||
lr.SetPrompt(fmtPrompt(prompt))
|
||||
goto input // continue inside nested loop
|
||||
}
|
||||
if !strings.HasSuffix(input, "\\") {
|
||||
if err == io.EOF {
|
||||
// Exit if user presses ^D (ctrl + d)
|
||||
hooks.Emit("hilbish.exit")
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
if err == readline.CtrlC {
|
||||
fmt.Println("^C")
|
||||
hooks.Emit("hilbish.cancel")
|
||||
} else {
|
||||
// If we get a completely random error, print
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
if errors.Is(err, syscall.ENOTTY) {
|
||||
// what are we even doing here?
|
||||
panic("not a tty")
|
||||
}
|
||||
<-make(chan struct{})
|
||||
}
|
||||
continue
|
||||
}
|
||||
var priv bool
|
||||
if strings.HasPrefix(input, " ") {
|
||||
priv = true
|
||||
}
|
||||
|
||||
input = strings.TrimSpace(input)
|
||||
if len(input) == 0 {
|
||||
running = true
|
||||
hooks.Emit("command.exit", 0)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(input, "\\") {
|
||||
print("\n")
|
||||
for {
|
||||
input, err = continuePrompt(strings.TrimSuffix(input, "\\")+"\n", false)
|
||||
if err != nil {
|
||||
running = true
|
||||
lr.SetPrompt(fmtPrompt(prompt))
|
||||
goto input // continue inside nested loop
|
||||
}
|
||||
if !strings.HasSuffix(input, "\\") {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runInput(input, priv)
|
||||
|
||||
termwidth, _, err := term.GetSize(0)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\u001b[7m∆\u001b[0m" + strings.Repeat(" ", termwidth-1) + "\r")
|
||||
}
|
||||
}
|
||||
|
||||
runInput(input, priv)
|
||||
|
||||
termwidth, _, err := term.GetSize(0)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\u001b[7m∆\u001b[0m" + strings.Repeat(" ", termwidth - 1) + "\r")
|
||||
}
|
||||
|
||||
*/
|
||||
exit(0)
|
||||
}
|
||||
|
||||
/*
|
||||
func continuePrompt(prev string, newline bool) (string, error) {
|
||||
hooks.Emit("multiline", nil)
|
||||
lr.SetPrompt(multilinePrompt)
|
||||
@ -279,6 +249,7 @@ func continuePrompt(prev string, newline bool) (string, error) {
|
||||
|
||||
return prev + cont, nil
|
||||
}
|
||||
*/
|
||||
|
||||
// This semi cursed function formats our prompt (obviously)
|
||||
func fmtPrompt(prompt string) string {
|
||||
@ -300,7 +271,7 @@ func fmtPrompt(prompt string) string {
|
||||
}
|
||||
|
||||
for i, v := range args {
|
||||
if i % 2 == 0 {
|
||||
if i%2 == 0 {
|
||||
args[i] = "%" + v
|
||||
}
|
||||
}
|
||||
@ -354,5 +325,5 @@ func getVersion() string {
|
||||
}
|
||||
|
||||
func cut(slice []string, idx int) []string {
|
||||
return append(slice[:idx], slice[idx + 1:]...)
|
||||
return append(slice[:idx], slice[idx+1:]...)
|
||||
}
|
||||
|
@ -21,7 +21,11 @@ function editorMt.__index(_, key)
|
||||
end
|
||||
|
||||
return function(...)
|
||||
return editor[key](editor, ...)
|
||||
local args = {...}
|
||||
if args[1] == hilbish.editor then
|
||||
table.remove(args, 1)
|
||||
end
|
||||
return editor[key](editor, table.unpack(args))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
-- @module hilbish
|
||||
local bait = require 'bait'
|
||||
local fs = require 'fs'
|
||||
local readline = require 'readline'
|
||||
local snail = require 'snail'
|
||||
|
||||
hilbish.snail = snail.new()
|
||||
@ -7,6 +9,81 @@ hilbish.snail:run 'true' -- to "initialize" snail
|
||||
bait.catch('hilbish.cd', function(path)
|
||||
hilbish.snail:dir(path)
|
||||
end)
|
||||
|
||||
local function abbrevHome(path)
|
||||
if path:sub(1, hilbish.home:len()) == hilbish.home then
|
||||
return fs.join('~', path:sub(hilbish.home:len() + 1))
|
||||
end
|
||||
end
|
||||
|
||||
local function fmtPrompt(p)
|
||||
return p:gsub('%%(%w)', function(c)
|
||||
if c == 'd' then
|
||||
return abbrevHome(hilbish.cwd())
|
||||
elseif c == 'u' then
|
||||
return hilbish.user
|
||||
elseif c == 'h' then
|
||||
return hilbish.host
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- prompt(str, typ)
|
||||
--- Changes the shell prompt to the provided string.
|
||||
--- There are a few verbs that can be used in the prompt text.
|
||||
--- These will be formatted and replaced with the appropriate values.
|
||||
--- `%d` - Current working directory
|
||||
--- `%u` - Name of current user
|
||||
--- `%h` - Hostname of device
|
||||
--- #param str string
|
||||
--- #param typ? string Type of prompt, being left or right. Left by default.
|
||||
--- #example
|
||||
--- -- the default hilbish prompt without color
|
||||
--- hilbish.prompt '%u %d ∆'
|
||||
--- -- or something of old:
|
||||
--- hilbish.prompt '%u@%h :%d $'
|
||||
--- -- prompt: user@hostname: ~/directory $
|
||||
--- #example
|
||||
-- @param p string
|
||||
-- @param typ string Type of prompt, either left or right
|
||||
function hilbish.prompt(p, typ)
|
||||
if type(p) ~= 'string' then
|
||||
error('expected #1 to be string, got ' .. type(p))
|
||||
end
|
||||
|
||||
if not typ or typ == 'left' then
|
||||
hilbish.editor:prompt(fmtPrompt(p))
|
||||
if not hilbish.running then
|
||||
hilbish.editor:refreshPrompt()
|
||||
end
|
||||
elseif typ == 'right' then
|
||||
hilbish.editor:rightPrompt(fmtPrompt(p))
|
||||
if not hilbish.running then
|
||||
hilbish.editor:refreshPrompt()
|
||||
end
|
||||
else
|
||||
error('expected prompt type to be right or left, got ' .. tostring(typ))
|
||||
end
|
||||
end
|
||||
|
||||
--- read(prompt) -> input (string)
|
||||
--- Read input from the user, using Hilbish's line editor/input reader.
|
||||
--- This is a separate instance from the one Hilbish actually uses.
|
||||
--- Returns `input`, will be nil if Ctrl-D is pressed, or an error occurs.
|
||||
-- @param prompt? string Text to print before input, can be empty.
|
||||
-- @returns string|nil
|
||||
function hilbish.read(prompt)
|
||||
prompt = prompt or ''
|
||||
if type(prompt) ~= 'string' then
|
||||
error 'expected #1 to be a string'
|
||||
end
|
||||
|
||||
local rl = readline.new()
|
||||
rl:prompt(prompt)
|
||||
|
||||
return rl:read()
|
||||
end
|
||||
|
||||
--- Runs `cmd` in Hilbish's shell script interpreter.
|
||||
--- The `streams` parameter specifies the output and input streams the command should use.
|
||||
--- For example, to write command output to a sink.
|
||||
|
@ -18,6 +18,7 @@ table.insert(package.searchers, function(module)
|
||||
return function() return hilbish.module.load(path) end, path
|
||||
end)
|
||||
|
||||
require 'nature.editor'
|
||||
require 'nature.hilbish'
|
||||
require 'nature.processors'
|
||||
|
||||
@ -28,7 +29,6 @@ require 'nature.vim'
|
||||
require 'nature.runner'
|
||||
require 'nature.hummingbird'
|
||||
require 'nature.abbr'
|
||||
require 'nature.editor'
|
||||
|
||||
local shlvl = tonumber(os.getenv 'SHLVL')
|
||||
if shlvl ~= nil then
|
||||
@ -95,3 +95,81 @@ end)
|
||||
bait.catch('command.not-executable', function(cmd)
|
||||
print(string.format('hilbish: %s: not executable', cmd))
|
||||
end)
|
||||
|
||||
local function runConfig(path)
|
||||
if not hilbish.interactive then return end
|
||||
|
||||
local _, err = pcall(dofile, path)
|
||||
if err then
|
||||
print(err)
|
||||
print 'An error has occured while loading your config!\n'
|
||||
hilbish.prompt '& '
|
||||
else
|
||||
bait.throw 'hilbish.init'
|
||||
end
|
||||
end
|
||||
|
||||
local _, err = pcall(fs.stat, hilbish.confFile)
|
||||
if err and tostring(err):match 'no such file' and hilbish.confFile == fs.join(hilbish.defaultConfDir, 'init.lua') then
|
||||
-- Run config from current directory (assuming this is Hilbish's git)
|
||||
local _, err = pcall(fs.stat, '.hilbishrc.lua')
|
||||
local confpath = '.hilbishrc.lua'
|
||||
|
||||
if err then
|
||||
-- If it wasnt found go to system sample config
|
||||
confpath = fs.join(hilbish.dataDir, confpath)
|
||||
local _, err = pcall(fs.stat, confpath)
|
||||
if err then
|
||||
print('could not find .hilbishrc.lua or ' .. confpath)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
runConfig(confpath)
|
||||
else
|
||||
runConfig(hilbish.confFile)
|
||||
end
|
||||
|
||||
-- TODO: hilbish.exit function, stop jobs and timers.
|
||||
local function exit(code)
|
||||
os.exit(code)
|
||||
end
|
||||
|
||||
while hilbish.interactive do
|
||||
hilbish.running = false
|
||||
|
||||
local ok, res = pcall(function() return hilbish.editor:read() end)
|
||||
if not ok and tostring(res):lower():match 'eof' then
|
||||
bait.throw 'hilbish.exit'
|
||||
exit(0)
|
||||
end
|
||||
if not ok then
|
||||
if tostring(res):lower():match 'ctrl%+c' then
|
||||
print '^C'
|
||||
bait.throw 'hilbish.cancel'
|
||||
else
|
||||
error(res)
|
||||
io.read()
|
||||
end
|
||||
goto continue
|
||||
end
|
||||
--- @type string
|
||||
local input = res
|
||||
|
||||
local priv = false
|
||||
if res:sub(1, 1) == ' ' then
|
||||
priv = true
|
||||
end
|
||||
input = input:gsub('%s+', '')
|
||||
|
||||
if input:len() == 0 then
|
||||
hilbish.running = true
|
||||
bait.throw('command.exit', 0 )
|
||||
goto continue
|
||||
end
|
||||
|
||||
hilbish.running = true
|
||||
hilbish.runner.run(input, priv)
|
||||
|
||||
::continue::
|
||||
end
|
||||
|
24
rl.go
24
rl.go
@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"hilbish/util"
|
||||
|
||||
@ -232,29 +231,6 @@ func (lr *lineReader) Read() (string, error) {
|
||||
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)
|
||||
}
|
||||
|
13
vars.go
13
vars.go
@ -2,16 +2,16 @@ package main
|
||||
|
||||
// String vars that are free to be changed at compile time
|
||||
var (
|
||||
defaultHistDir = ""
|
||||
defaultHistDir = ""
|
||||
commonRequirePaths = "';./libs/?/init.lua;./?/init.lua;./?/?.lua'"
|
||||
|
||||
prompt string
|
||||
prompt string
|
||||
multilinePrompt = "> "
|
||||
)
|
||||
|
||||
// Version info
|
||||
var (
|
||||
ver = "v2.4.0"
|
||||
ver = "v2.4.0"
|
||||
releaseName = "Moonflower"
|
||||
|
||||
gitCommit string
|
||||
@ -20,10 +20,9 @@ var (
|
||||
|
||||
// Flags
|
||||
var (
|
||||
running bool // Is a command currently running
|
||||
running bool // Is a command currently running
|
||||
interactive bool
|
||||
login bool // Are we the login shell?
|
||||
noexecute bool // Should we run Lua or only report syntax errors
|
||||
login bool // Are we the login shell?
|
||||
noexecute bool // Should we run Lua or only report syntax errors
|
||||
initialized bool
|
||||
)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user