Hilbish/main.go

352 lines
7.6 KiB
Go
Raw Normal View History

2021-03-19 19:11:59 +00:00
package main
import (
"bufio"
2024-04-06 22:37:16 +00:00
"errors"
2021-03-19 19:11:59 +00:00
"fmt"
2021-04-19 02:09:27 +00:00
"io"
2021-03-19 19:11:59 +00:00
"os"
"os/exec"
2021-04-19 02:09:27 +00:00
"os/user"
"path/filepath"
"runtime"
"strings"
2024-04-06 22:37:16 +00:00
"syscall"
refactor!: support lua 5.4 (#129) major rewrite which changes the library hilbish uses for it's lua vm this one implements lua 5.4, and since that's a major version bump, it's a breaking change. introduced here also is a fix for `hilbish.login` not being the right value * refactor: start work on lua 5.4 lots of commented out code ive found a go lua library which implements lua 5.4 and found an opportunity to start working on it. this commit basically removes everything and just leaves enough for the shell to be "usable" and able to start. there are no builtins or libraries (besides the `hilbish` global) * fix: call cont next in prompt function this continues execution of lua, very obvious fixes an issue with code stopping at the prompt function * fix: handle errors in user config * fix: handle panic in lua input if it is incorrect * feat: implement bait * refactor: use util funcs to run lua where possible * refactor: move arg handle function to util * feat: implement commander * feat: implement fs * feat: add hilbish module functions used by prelude * chore: use custom fork of golua * fix: make sure args to setenv are strings in prelude * feat: implement completions * chore: remove comment * feat: implement terminal * feat: implement hilbish.interval * chore: update lunacolors * chore: update golua * feat: implement aliases * feat: add input mode * feat: implement runner mode * style: use comma separated cases instead of fallthrough * feat: implement syntax highlight and hints * chore: add comments to document util functions * chore: fix dofile comment doc * refactor: make loader functions for go modules unexported * feat: implement job management * feat: add hilbish properties * feat: implement all hilbish module functions * feat: implement history interface * feat: add completion interface * feat: add module description docs * feat: implement os interface * refactor: use hlalias for add function in hilbish.alias interface * feat: make it so hilbish.run can return command output * fix: set hilbish.exitCode to last command exit code * fix(ansikit): flush on io.write * fix: deregister commander if return isnt number * feat: run script when provided path * fix: read file manually in DoFile to avoid shebang * chore: add comment for reason of unreading byte * fix: remove prelude error printing * fix: add names at chunk load for context in errors * fix: add newline at the beginning of file buffer when there is shebang this makes the line count in error messages line up properly * fix: remove extra newline after error
2022-04-04 10:40:02 +00:00
"hilbish/util"
"hilbish/golibs/bait"
"hilbish/golibs/commander"
"hilbish/moonlight"
refactor!: support lua 5.4 (#129) major rewrite which changes the library hilbish uses for it's lua vm this one implements lua 5.4, and since that's a major version bump, it's a breaking change. introduced here also is a fix for `hilbish.login` not being the right value * refactor: start work on lua 5.4 lots of commented out code ive found a go lua library which implements lua 5.4 and found an opportunity to start working on it. this commit basically removes everything and just leaves enough for the shell to be "usable" and able to start. there are no builtins or libraries (besides the `hilbish` global) * fix: call cont next in prompt function this continues execution of lua, very obvious fixes an issue with code stopping at the prompt function * fix: handle errors in user config * fix: handle panic in lua input if it is incorrect * feat: implement bait * refactor: use util funcs to run lua where possible * refactor: move arg handle function to util * feat: implement commander * feat: implement fs * feat: add hilbish module functions used by prelude * chore: use custom fork of golua * fix: make sure args to setenv are strings in prelude * feat: implement completions * chore: remove comment * feat: implement terminal * feat: implement hilbish.interval * chore: update lunacolors * chore: update golua * feat: implement aliases * feat: add input mode * feat: implement runner mode * style: use comma separated cases instead of fallthrough * feat: implement syntax highlight and hints * chore: add comments to document util functions * chore: fix dofile comment doc * refactor: make loader functions for go modules unexported * feat: implement job management * feat: add hilbish properties * feat: implement all hilbish module functions * feat: implement history interface * feat: add completion interface * feat: add module description docs * feat: implement os interface * refactor: use hlalias for add function in hilbish.alias interface * feat: make it so hilbish.run can return command output * fix: set hilbish.exitCode to last command exit code * fix(ansikit): flush on io.write * fix: deregister commander if return isnt number * feat: run script when provided path * fix: read file manually in DoFile to avoid shebang * chore: add comment for reason of unreading byte * fix: remove prelude error printing * fix: add names at chunk load for context in errors * fix: add newline at the beginning of file buffer when there is shebang this makes the line count in error messages line up properly * fix: remove extra newline after error
2022-04-04 10:40:02 +00:00
rt "github.com/arnodel/golua/runtime"
"github.com/pborman/getopt"
"github.com/maxlandon/readline"
"golang.org/x/term"
"mvdan.cc/sh/v3/interp"
2021-03-19 19:11:59 +00:00
)
var (
l *moonlight.Runtime
lr *lineReader
2021-04-19 02:09:27 +00:00
refactor!: support lua 5.4 (#129) major rewrite which changes the library hilbish uses for it's lua vm this one implements lua 5.4, and since that's a major version bump, it's a breaking change. introduced here also is a fix for `hilbish.login` not being the right value * refactor: start work on lua 5.4 lots of commented out code ive found a go lua library which implements lua 5.4 and found an opportunity to start working on it. this commit basically removes everything and just leaves enough for the shell to be "usable" and able to start. there are no builtins or libraries (besides the `hilbish` global) * fix: call cont next in prompt function this continues execution of lua, very obvious fixes an issue with code stopping at the prompt function * fix: handle errors in user config * fix: handle panic in lua input if it is incorrect * feat: implement bait * refactor: use util funcs to run lua where possible * refactor: move arg handle function to util * feat: implement commander * feat: implement fs * feat: add hilbish module functions used by prelude * chore: use custom fork of golua * fix: make sure args to setenv are strings in prelude * feat: implement completions * chore: remove comment * feat: implement terminal * feat: implement hilbish.interval * chore: update lunacolors * chore: update golua * feat: implement aliases * feat: add input mode * feat: implement runner mode * style: use comma separated cases instead of fallthrough * feat: implement syntax highlight and hints * chore: add comments to document util functions * chore: fix dofile comment doc * refactor: make loader functions for go modules unexported * feat: implement job management * feat: add hilbish properties * feat: implement all hilbish module functions * feat: implement history interface * feat: add completion interface * feat: add module description docs * feat: implement os interface * refactor: use hlalias for add function in hilbish.alias interface * feat: make it so hilbish.run can return command output * fix: set hilbish.exitCode to last command exit code * fix(ansikit): flush on io.write * fix: deregister commander if return isnt number * feat: run script when provided path * fix: read file manually in DoFile to avoid shebang * chore: add comment for reason of unreading byte * fix: remove prelude error printing * fix: add names at chunk load for context in errors * fix: add newline at the beginning of file buffer when there is shebang this makes the line count in error messages line up properly * fix: remove extra newline after error
2022-04-04 10:40:02 +00:00
luaCompletions = map[string]*rt.Closure{}
2021-04-19 02:09:27 +00:00
confDir string
userDataDir string
curuser *user.User
hooks *bait.Bait
cmds *commander.Commander
defaultConfPath string
defaultHistPath string
runner *interp.Runner
2021-04-20 21:51:12 +00:00
)
2021-03-19 23:03:11 +00:00
2021-03-19 19:11:59 +00:00
func main() {
runner, _ = interp.New()
curuser, _ = user.Current()
homedir := curuser.HomeDir
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", "darwin":
userDataDir = getenv("XDG_DATA_HOME", curuser.HomeDir + "/.local/share")
default:
// this is fine on windows, dont know about others
userDataDir = confDir
}
if defaultConfDir == "" {
// we'll add *our* default if its empty (wont be if its changed comptime)
defaultConfDir = filepath.Join(confDir, "hilbish")
} else {
// else do ~ substitution
defaultConfDir = filepath.Join(util.ExpandHome(defaultConfDir), "hilbish")
}
defaultConfPath = filepath.Join(defaultConfDir, "init.lua")
if defaultHistDir == "" {
defaultHistDir = filepath.Join(userDataDir, "hilbish")
} else {
defaultHistDir = filepath.Join(util.ExpandHome(defaultHistDir), "hilbish")
}
defaultHistPath = filepath.Join(defaultHistDir, ".hilbish-history")
2021-06-12 14:48:57 +00:00
helpflag := getopt.BoolLong("help", 'h', "Prints Hilbish flags")
verflag := getopt.BoolLong("version", 'v', "Prints Hilbish version")
setshflag := getopt.BoolLong("setshellenv", 'S', "Sets $SHELL to Hilbish's executed path")
2021-05-11 22:19:53 +00:00
cmdflag := getopt.StringLong("command", 'c', "", "Executes a command on startup")
configflag := getopt.StringLong("config", 'C', defaultConfPath, "Sets the path to Hilbish's config")
2021-06-12 14:49:22 +00:00
getopt.BoolLong("login", 'l', "Force Hilbish to be a login shell")
2021-05-08 13:12:21 +00:00
getopt.BoolLong("interactive", 'i', "Force Hilbish to be an interactive shell")
2021-05-08 13:30:32 +00:00
getopt.BoolLong("noexec", 'n', "Don't execute and only report Lua syntax errors")
getopt.Parse()
loginshflag := getopt.Lookup('l').Seen()
interactiveflag := getopt.Lookup('i').Seen()
2021-05-08 12:56:24 +00:00
noexecflag := getopt.Lookup('n').Seen()
2021-06-12 14:48:57 +00:00
if *helpflag {
getopt.PrintUsage(os.Stdout)
os.Exit(0)
}
if *cmdflag == "" || interactiveflag {
interactive = true
}
2024-04-06 22:37:16 +00:00
if fileInfo, _ := os.Stdin.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 || !term.IsTerminal(int(os.Stdin.Fd())) {
interactive = false
}
if getopt.NArgs() > 0 {
interactive = false
}
2021-05-08 12:56:24 +00:00
if noexecflag {
noexecute = true
}
// first arg, first character
if loginshflag || os.Args[0][0] == '-' {
login = true
}
if *verflag {
fmt.Printf("Hilbish %s\nCompiled with %s\n", getVersion(), runtime.Version())
os.Exit(0)
}
2021-03-28 22:58:58 +00:00
// Set $SHELL if the user wants to
2021-04-19 02:09:27 +00:00
if *setshflag {
os.Setenv("SHELL", "hilbish")
path, err := exec.LookPath("hilbish")
if err == nil {
os.Setenv("SHELL", path)
}
2021-04-19 02:09:27 +00:00
}
2021-03-21 07:51:44 +00:00
2022-03-06 18:38:27 +00:00
lr = newLineReader("", false)
luaInit()
go handleSignals()
2021-03-28 22:58:58 +00:00
// If user's config doesn't exixt,
if _, err := os.Stat(defaultConfPath); os.IsNotExist(err) && *configflag == defaultConfPath {
2021-03-30 23:47:02 +00:00
// Read default from current directory
// (this is assuming the current dir is Hilbish's git)
_, err := os.ReadFile(".hilbishrc.lua")
confpath := ".hilbishrc.lua"
2021-03-30 23:47:02 +00:00
if err != nil {
// If it wasnt found, go to the real sample conf
_, err = os.ReadFile(sampleConfPath)
confpath = sampleConfPath
2021-03-30 23:47:02 +00:00
if err != nil {
fmt.Println("could not find .hilbishrc.lua or", sampleConfPath)
2021-03-30 23:47:02 +00:00
return
}
}
runConfig(confpath)
} else {
runConfig(*configflag)
}
hooks.Emit("hilbish.init")
2021-03-21 07:51:44 +00:00
if fileInfo, _ := os.Stdin.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 {
scanner := bufio.NewScanner(bufio.NewReader(os.Stdin))
for scanner.Scan() {
text := scanner.Text()
runInput(text, true)
}
exit(0)
}
if *cmdflag != "" {
runInput(*cmdflag, true)
}
if getopt.NArgs() > 0 {
2024-07-21 15:37:08 +00:00
luaArgs := moonlight.NewTable()
refactor!: support lua 5.4 (#129) major rewrite which changes the library hilbish uses for it's lua vm this one implements lua 5.4, and since that's a major version bump, it's a breaking change. introduced here also is a fix for `hilbish.login` not being the right value * refactor: start work on lua 5.4 lots of commented out code ive found a go lua library which implements lua 5.4 and found an opportunity to start working on it. this commit basically removes everything and just leaves enough for the shell to be "usable" and able to start. there are no builtins or libraries (besides the `hilbish` global) * fix: call cont next in prompt function this continues execution of lua, very obvious fixes an issue with code stopping at the prompt function * fix: handle errors in user config * fix: handle panic in lua input if it is incorrect * feat: implement bait * refactor: use util funcs to run lua where possible * refactor: move arg handle function to util * feat: implement commander * feat: implement fs * feat: add hilbish module functions used by prelude * chore: use custom fork of golua * fix: make sure args to setenv are strings in prelude * feat: implement completions * chore: remove comment * feat: implement terminal * feat: implement hilbish.interval * chore: update lunacolors * chore: update golua * feat: implement aliases * feat: add input mode * feat: implement runner mode * style: use comma separated cases instead of fallthrough * feat: implement syntax highlight and hints * chore: add comments to document util functions * chore: fix dofile comment doc * refactor: make loader functions for go modules unexported * feat: implement job management * feat: add hilbish properties * feat: implement all hilbish module functions * feat: implement history interface * feat: add completion interface * feat: add module description docs * feat: implement os interface * refactor: use hlalias for add function in hilbish.alias interface * feat: make it so hilbish.run can return command output * fix: set hilbish.exitCode to last command exit code * fix(ansikit): flush on io.write * fix: deregister commander if return isnt number * feat: run script when provided path * fix: read file manually in DoFile to avoid shebang * chore: add comment for reason of unreading byte * fix: remove prelude error printing * fix: add names at chunk load for context in errors * fix: add newline at the beginning of file buffer when there is shebang this makes the line count in error messages line up properly * fix: remove extra newline after error
2022-04-04 10:40:02 +00:00
for i, arg := range getopt.Args() {
2024-07-21 15:37:08 +00:00
luaArgs.Set(moonlight.IntValue(int64(i)), moonlight.StringValue(arg))
}
2024-07-21 15:37:08 +00:00
l.GlobalTable().SetField("args", moonlight.TableValue(luaArgs))
err := l.DoFile(getopt.Arg(0))
if err != nil {
fmt.Fprintln(os.Stderr, err)
exit(1)
}
exit(0)
}
2022-03-06 21:20:41 +00:00
initialized = true
input:
for interactive {
2021-04-14 17:20:03 +00:00
running = false
2021-04-16 14:27:11 +00:00
input, err := lr.Read()
2021-04-16 14:27:11 +00:00
2021-03-20 16:57:18 +00:00
if err == io.EOF {
2021-03-28 22:58:58 +00:00
// Exit if user presses ^D (ctrl + d)
hooks.Emit("hilbish.exit")
2021-03-20 16:57:18 +00:00
break
}
2021-03-19 19:11:59 +00:00
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)
2024-04-06 22:37:16 +00:00
if errors.Is(err, syscall.ENOTTY) {
// what are we even doing here?
panic("not a tty")
}
2024-04-06 22:52:55 +00:00
<-make(chan struct{})
}
2024-04-06 22:52:55 +00:00
continue
2021-03-19 19:11:59 +00:00
}
var priv bool
if strings.HasPrefix(input, " ") {
priv = true
}
2021-03-28 22:58:58 +00:00
2021-04-05 00:30:03 +00:00
input = strings.TrimSpace(input)
if len(input) == 0 {
running = true
hooks.Emit("command.exit", 0)
continue
}
2021-04-05 00:30:03 +00:00
if strings.HasSuffix(input, "\\") {
for {
input, err = continuePrompt(input)
if err != nil {
running = true
lr.SetPrompt(fmtPrompt(prompt))
goto input // continue inside nested loop
}
if !strings.HasSuffix(input, "\\") {
2021-04-19 02:09:27 +00:00
break
}
}
}
runInput(input, priv)
termwidth, _, err := term.GetSize(0)
2021-04-19 02:09:27 +00:00
if err != nil {
continue
}
2021-04-24 03:44:59 +00:00
fmt.Printf("\u001b[7m∆\u001b[0m" + strings.Repeat(" ", termwidth - 1) + "\r")
}
exit(0)
}
func continuePrompt(prev string) (string, error) {
hooks.Emit("multiline", nil)
lr.SetPrompt(multilinePrompt)
cont, err := lr.Read()
if err != nil {
return "", err
2021-03-19 19:11:59 +00:00
}
cont = strings.TrimSpace(cont)
return prev + strings.TrimSuffix(cont, "\n"), nil
2021-03-19 19:11:59 +00:00
}
2021-03-28 22:58:58 +00:00
// This semi cursed function formats our prompt (obviously)
func fmtPrompt(prompt string) string {
host, _ := os.Hostname()
cwd, _ := os.Getwd()
cwd = util.AbbrevHome(cwd)
username := curuser.Username
// this will be baked into binary since GOOS is a constant
if runtime.GOOS == "windows" {
username = strings.Split(username, "\\")[1] // for some reason Username includes the hostname on windows
}
2021-03-28 22:58:58 +00:00
args := []string{
2021-03-28 22:58:58 +00:00
"d", cwd,
"D", filepath.Base(cwd),
2021-03-28 22:58:58 +00:00
"h", host,
"u", username,
}
2021-03-28 22:58:58 +00:00
for i, v := range args {
if i % 2 == 0 {
args[i] = "%" + v
}
}
2021-03-28 22:58:58 +00:00
r := strings.NewReplacer(args...)
nprompt := r.Replace(prompt)
2021-03-28 22:58:58 +00:00
return nprompt
2021-03-26 05:06:14 +00:00
}
func removeDupes(slice []string) []string {
all := make(map[string]bool)
newSlice := []string{}
for _, item := range slice {
if _, val := all[item]; !val {
all[item] = true
newSlice = append(newSlice, item)
}
}
return newSlice
}
func contains(s []string, e string) bool {
for _, a := range s {
if strings.ToLower(a) == strings.ToLower(e) {
return true
}
}
return false
}
func exit(code int) {
jobs.stopAll()
// wait for all timers to finish before exiting.
// only do that when not interactive
if !interactive {
timers.wait()
}
os.Exit(code)
}
2022-05-16 23:36:34 +00:00
func getVersion() string {
v := strings.Builder{}
v.WriteString(ver)
if gitBranch != "" && gitBranch != "HEAD" {
v.WriteString("-" + gitBranch)
}
if gitCommit != "" {
v.WriteString("." + gitCommit)
}
2022-05-17 10:39:05 +00:00
v.WriteString(" (" + releaseName + ")")
if moonlight.IsMidnight() {
v.WriteString(" (Midnight Edition)")
}
2022-05-17 10:39:05 +00:00
2022-05-16 23:36:34 +00:00
return v.String()
}
func cut(slice []string, idx int) []string {
return append(slice[:idx], slice[idx + 1:]...)
}