mirror of
				https://github.com/sammy-ette/Hilbish
				synced 2025-08-10 02: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 user Username of the user | ||||||
| // #field host Hostname of the machine | // #field host Hostname of the machine | ||||||
| // #field dataDir Directory for Hilbish data files, including the docs and default modules | // #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 interactive Is Hilbish in an interactive shell? | ||||||
| // #field login Is Hilbish the login 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 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 Exit code of the last executed command | ||||||
|  | // #field exitCode If Hilbish is currently running any interactive input | ||||||
| package main | package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| @ -44,10 +47,8 @@ var exports = map[string]util.LuaExport{ | |||||||
| 	"hinter":      {hlhinter, 1, false}, | 	"hinter":      {hlhinter, 1, false}, | ||||||
| 	"multiprompt": {hlmultiprompt, 1, false}, | 	"multiprompt": {hlmultiprompt, 1, false}, | ||||||
| 	"prependPath": {hlprependPath, 1, false}, | 	"prependPath": {hlprependPath, 1, false}, | ||||||
| 	"prompt":      {hlprompt, 1, true}, |  | ||||||
| 	"inputMode":   {hlinputMode, 1, false}, | 	"inputMode":   {hlinputMode, 1, false}, | ||||||
| 	"interval":    {hlinterval, 2, false}, | 	"interval":    {hlinterval, 2, false}, | ||||||
| 	"read":        {hlread, 1, false}, |  | ||||||
| 	"timeout":     {hltimeout, 2, false}, | 	"timeout":     {hltimeout, 2, false}, | ||||||
| 	"which":       {hlwhich, 1, 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, "host", rt.StringValue(host)) | ||||||
| 	util.SetField(rtm, mod, "home", rt.StringValue(curuser.HomeDir)) | 	util.SetField(rtm, mod, "home", rt.StringValue(curuser.HomeDir)) | ||||||
| 	util.SetField(rtm, mod, "dataDir", rt.StringValue(dataDir)) | 	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, "interactive", rt.BoolValue(interactive)) | ||||||
| 	util.SetField(rtm, mod, "login", rt.BoolValue(login)) | 	util.SetField(rtm, mod, "login", rt.BoolValue(login)) | ||||||
| 	util.SetField(rtm, mod, "vimMode", rt.NilValue) | 	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 | 	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) | // multiprompt(str) | ||||||
| // Changes the text prompt when Hilbish asks for more input. | // Changes the text prompt when Hilbish asks for more input. | ||||||
| // This will show up when text is incomplete, like a missing quote | // This will show up when text is incomplete, like a missing quote | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								exec.go
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								exec.go
									
									
									
									
									
								
							| @ -1,7 +1,6 @@ | |||||||
| package main | package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
| 	"strings" | 	"strings" | ||||||
| @ -10,10 +9,6 @@ import ( | |||||||
| 	//"github.com/yuin/gopher-lua/parse" | 	//"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) { | func runInput(input string, priv bool) { | ||||||
| 	running = true | 	running = true | ||||||
| 	runnerRun := hshMod.Get(rt.StringValue("runner")).AsTable().Get(rt.StringValue("run")) | 	runnerRun := hshMod.Get(rt.StringValue("runner")).AsTable().Get(rt.StringValue("run")) | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ package readline | |||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"hilbish/util" | 	"hilbish/util" | ||||||
| 
 | 
 | ||||||
| @ -28,6 +29,8 @@ func (rl *Readline) luaLoader(rtm *rt.Runtime) (rt.Value, func()) { | |||||||
| 		"readChar":       {rlReadChar, 1, false}, | 		"readChar":       {rlReadChar, 1, false}, | ||||||
| 		"setVimRegister": {rlSetRegister, 3, false}, | 		"setVimRegister": {rlSetRegister, 3, false}, | ||||||
| 		"log":            {rlLog, 2, false}, | 		"log":            {rlLog, 2, false}, | ||||||
|  | 		"prompt":         {rlPrompt, 2, false}, | ||||||
|  | 		"refreshPrompt":  {rlRefreshPrompt, 1, false}, | ||||||
| 	} | 	} | ||||||
| 	util.SetExports(rtm, rlMethods, rlMethodss) | 	util.SetExports(rtm, rlMethods, rlMethodss) | ||||||
| 
 | 
 | ||||||
| @ -251,6 +254,53 @@ func rlLog(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	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) { | func rlArg(c *rt.GoCont, arg int) (*Readline, error) { | ||||||
| 	j, ok := valueToRl(c.Arg(arg)) | 	j, ok := valueToRl(c.Arg(arg)) | ||||||
| 	if !ok { | 	if !ok { | ||||||
|  | |||||||
							
								
								
									
										21
									
								
								lua.go
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								lua.go
									
									
									
									
									
								
							| @ -16,14 +16,19 @@ import ( | |||||||
| 	"github.com/arnodel/golua/lib" | 	"github.com/arnodel/golua/lib" | ||||||
| 	"github.com/arnodel/golua/lib/debuglib" | 	"github.com/arnodel/golua/lib/debuglib" | ||||||
| 	rt "github.com/arnodel/golua/runtime" | 	rt "github.com/arnodel/golua/runtime" | ||||||
|  | 	"github.com/pborman/getopt" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var minimalconf = `hilbish.prompt '& '` |  | ||||||
| 
 |  | ||||||
| func luaInit() { | func luaInit() { | ||||||
| 	l = rt.New(os.Stdout) | 	l = rt.New(os.Stdout) | ||||||
| 
 | 
 | ||||||
| 	loadLibs(l) | 	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) | 	yarnPool := yarn.New(yarnloadLibs) | ||||||
| 	lib.LoadLibs(l, yarnPool.Loader) | 	lib.LoadLibs(l, yarnPool.Loader) | ||||||
| @ -36,6 +41,7 @@ func luaInit() { | |||||||
| 
 | 
 | ||||||
| 	err1 := util.DoFile(l, "nature/init.lua") | 	err1 := util.DoFile(l, "nature/init.lua") | ||||||
| 	if err1 != nil { | 	if err1 != nil { | ||||||
|  | 		fmt.Println(err1) | ||||||
| 		err2 := util.DoFile(l, filepath.Join(dataDir, "nature", "init.lua")) | 		err2 := util.DoFile(l, filepath.Join(dataDir, "nature", "init.lua")) | ||||||
| 		if err2 != nil { | 		if err2 != nil { | ||||||
| 			fmt.Fprintln(os.Stderr, "Missing nature module, some functionality and builtins will be missing.") | 			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) | 	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) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | |||||||
							
								
								
									
										59
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								main.go
									
									
									
									
									
								
							| @ -2,24 +2,20 @@ package main | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bufio" | 	"bufio" | ||||||
| 	"errors" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" |  | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/exec" | 	"os/exec" | ||||||
| 	"os/user" | 	"os/user" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"runtime" | 	"runtime" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"syscall" |  | ||||||
| 
 | 
 | ||||||
| 	"hilbish/util" |  | ||||||
| 	"hilbish/golibs/bait" | 	"hilbish/golibs/bait" | ||||||
| 	"hilbish/golibs/commander" | 	"hilbish/golibs/commander" | ||||||
|  | 	"hilbish/util" | ||||||
| 
 | 
 | ||||||
| 	rt "github.com/arnodel/golua/runtime" | 	rt "github.com/arnodel/golua/runtime" | ||||||
| 	"github.com/pborman/getopt" | 	"github.com/pborman/getopt" | ||||||
| 	"github.com/maxlandon/readline" |  | ||||||
| 	"golang.org/x/term" | 	"golang.org/x/term" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @ -35,7 +31,7 @@ var ( | |||||||
| 
 | 
 | ||||||
| 	hooks           *bait.Bait | 	hooks           *bait.Bait | ||||||
| 	cmds            *commander.Commander | 	cmds            *commander.Commander | ||||||
| 	defaultConfPath string | 	confPath        string | ||||||
| 	defaultHistPath string | 	defaultHistPath string | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @ -62,7 +58,7 @@ func main() { | |||||||
| 	// i honestly dont know what directories to use for this | 	// i honestly dont know what directories to use for this | ||||||
| 	switch runtime.GOOS { | 	switch runtime.GOOS { | ||||||
| 	case "linux", "darwin": | 	case "linux", "darwin": | ||||||
| 		userDataDir = getenv("XDG_DATA_HOME", curuser.HomeDir + "/.local/share") | 		userDataDir = getenv("XDG_DATA_HOME", curuser.HomeDir+"/.local/share") | ||||||
| 	default: | 	default: | ||||||
| 		// this is fine on windows, dont know about others | 		// this is fine on windows, dont know about others | ||||||
| 		userDataDir = confDir | 		userDataDir = confDir | ||||||
| @ -75,7 +71,7 @@ func main() { | |||||||
| 		// else do ~ substitution | 		// else do ~ substitution | ||||||
| 		defaultConfDir = filepath.Join(util.ExpandHome(defaultConfDir), "hilbish") | 		defaultConfDir = filepath.Join(util.ExpandHome(defaultConfDir), "hilbish") | ||||||
| 	} | 	} | ||||||
| 	defaultConfPath = filepath.Join(defaultConfDir, "init.lua") | 	defaultConfPath := filepath.Join(defaultConfDir, "init.lua") | ||||||
| 	if defaultHistDir == "" { | 	if defaultHistDir == "" { | ||||||
| 		defaultHistDir = filepath.Join(userDataDir, "hilbish") | 		defaultHistDir = filepath.Join(userDataDir, "hilbish") | ||||||
| 	} else { | 	} else { | ||||||
| @ -95,6 +91,7 @@ func main() { | |||||||
| 	loginshflag := getopt.Lookup('l').Seen() | 	loginshflag := getopt.Lookup('l').Seen() | ||||||
| 	interactiveflag := getopt.Lookup('i').Seen() | 	interactiveflag := getopt.Lookup('i').Seen() | ||||||
| 	noexecflag := getopt.Lookup('n').Seen() | 	noexecflag := getopt.Lookup('n').Seen() | ||||||
|  | 	confPath = *configflag | ||||||
| 
 | 
 | ||||||
| 	if *helpflag { | 	if *helpflag { | ||||||
| 		getopt.PrintUsage(os.Stdout) | 		getopt.PrintUsage(os.Stdout) | ||||||
| @ -105,7 +102,7 @@ func main() { | |||||||
| 		interactive = true | 		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 | 		interactive = false | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -143,29 +140,6 @@ func main() { | |||||||
| 
 | 
 | ||||||
| 	go handleSignals() | 	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 { | 	if fileInfo, _ := os.Stdin.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 { | ||||||
| 		scanner := bufio.NewScanner(bufio.NewReader(os.Stdin)) | 		scanner := bufio.NewScanner(bufio.NewReader(os.Stdin)) | ||||||
| 		for scanner.Scan() { | 		for scanner.Scan() { | ||||||
| @ -180,12 +154,6 @@ func main() { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if getopt.NArgs() > 0 { | 	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)) | 		err := util.DoFile(l, getopt.Arg(0)) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			fmt.Fprintln(os.Stderr, err) | 			fmt.Fprintln(os.Stderr, err) | ||||||
| @ -195,7 +163,8 @@ func main() { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	initialized = true | 	initialized = true | ||||||
| input: | 	/* | ||||||
|  | 		input: | ||||||
| 			for interactive { | 			for interactive { | ||||||
| 				running = false | 				running = false | ||||||
| 
 | 
 | ||||||
| @ -236,7 +205,7 @@ input: | |||||||
| 				if strings.HasSuffix(input, "\\") { | 				if strings.HasSuffix(input, "\\") { | ||||||
| 					print("\n") | 					print("\n") | ||||||
| 					for { | 					for { | ||||||
| 				input, err = continuePrompt(strings.TrimSuffix(input, "\\") + "\n", false) | 						input, err = continuePrompt(strings.TrimSuffix(input, "\\")+"\n", false) | ||||||
| 						if err != nil { | 						if err != nil { | ||||||
| 							running = true | 							running = true | ||||||
| 							lr.SetPrompt(fmtPrompt(prompt)) | 							lr.SetPrompt(fmtPrompt(prompt)) | ||||||
| @ -254,12 +223,13 @@ input: | |||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					continue | 					continue | ||||||
| 				} | 				} | ||||||
| 		fmt.Printf("\u001b[7m∆\u001b[0m" + strings.Repeat(" ", termwidth - 1) + "\r") | 				fmt.Printf("\u001b[7m∆\u001b[0m" + strings.Repeat(" ", termwidth-1) + "\r") | ||||||
| 			} | 			} | ||||||
| 
 | 	*/ | ||||||
| 	exit(0) | 	exit(0) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* | ||||||
| func continuePrompt(prev string, newline bool) (string, error) { | func continuePrompt(prev string, newline bool) (string, error) { | ||||||
| 	hooks.Emit("multiline", nil) | 	hooks.Emit("multiline", nil) | ||||||
| 	lr.SetPrompt(multilinePrompt) | 	lr.SetPrompt(multilinePrompt) | ||||||
| @ -279,6 +249,7 @@ func continuePrompt(prev string, newline bool) (string, error) { | |||||||
| 
 | 
 | ||||||
| 	return prev + cont, nil | 	return prev + cont, nil | ||||||
| } | } | ||||||
|  | */ | ||||||
| 
 | 
 | ||||||
| // This semi cursed function formats our prompt (obviously) | // This semi cursed function formats our prompt (obviously) | ||||||
| func fmtPrompt(prompt string) string { | func fmtPrompt(prompt string) string { | ||||||
| @ -300,7 +271,7 @@ func fmtPrompt(prompt string) string { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for i, v := range args { | 	for i, v := range args { | ||||||
| 		if i % 2 == 0 { | 		if i%2 == 0 { | ||||||
| 			args[i] = "%" + v | 			args[i] = "%" + v | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @ -354,5 +325,5 @@ func getVersion() string { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func cut(slice []string, idx int) []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 |     end | ||||||
| 
 | 
 | ||||||
| 	return function(...) | 	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 | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| -- @module hilbish | -- @module hilbish | ||||||
| local bait = require 'bait' | local bait = require 'bait' | ||||||
|  | local fs = require 'fs' | ||||||
|  | local readline = require 'readline' | ||||||
| local snail = require 'snail' | local snail = require 'snail' | ||||||
| 
 | 
 | ||||||
| hilbish.snail = snail.new() | hilbish.snail = snail.new() | ||||||
| @ -7,6 +9,81 @@ hilbish.snail:run 'true' -- to "initialize" snail | |||||||
| bait.catch('hilbish.cd', function(path) | bait.catch('hilbish.cd', function(path) | ||||||
| 	hilbish.snail:dir(path) | 	hilbish.snail:dir(path) | ||||||
| end) | 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. | --- Runs `cmd` in Hilbish's shell script interpreter. | ||||||
| --- The `streams` parameter specifies the output and input streams the command should use. | --- The `streams` parameter specifies the output and input streams the command should use. | ||||||
| --- For example, to write command output to a sink. | --- 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 | 	return function() return hilbish.module.load(path) end, path | ||||||
| end) | end) | ||||||
| 
 | 
 | ||||||
|  | require 'nature.editor' | ||||||
| require 'nature.hilbish' | require 'nature.hilbish' | ||||||
| require 'nature.processors' | require 'nature.processors' | ||||||
| 
 | 
 | ||||||
| @ -28,7 +29,6 @@ require 'nature.vim' | |||||||
| require 'nature.runner' | require 'nature.runner' | ||||||
| require 'nature.hummingbird' | require 'nature.hummingbird' | ||||||
| require 'nature.abbr' | require 'nature.abbr' | ||||||
| require 'nature.editor' |  | ||||||
| 
 | 
 | ||||||
| local shlvl = tonumber(os.getenv 'SHLVL') | local shlvl = tonumber(os.getenv 'SHLVL') | ||||||
| if shlvl ~= nil then | if shlvl ~= nil then | ||||||
| @ -95,3 +95,81 @@ end) | |||||||
| bait.catch('command.not-executable', function(cmd) | bait.catch('command.not-executable', function(cmd) | ||||||
| 	print(string.format('hilbish: %s: not executable', cmd)) | 	print(string.format('hilbish: %s: not executable', cmd)) | ||||||
| end) | 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 ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"strings" |  | ||||||
| 
 | 
 | ||||||
| 	"hilbish/util" | 	"hilbish/util" | ||||||
| 
 | 
 | ||||||
| @ -232,29 +231,6 @@ func (lr *lineReader) Read() (string, error) { | |||||||
| 	return s, err // might get another 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) { | func (lr *lineReader) AddHistory(cmd string) { | ||||||
| 	lr.fileHist.Write(cmd) | 	lr.fileHist.Write(cmd) | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user