mirror of
				https://github.com/sammy-ette/Hilbish
				synced 2025-08-10 02:52:03 +00:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
			59963add14
			...
			f3f49fc398
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | f3f49fc398 | ||
|  | 893d72a236 | ||
|  | 0637f2763b | ||
|  | 1cb536b1ac | ||
|  | 6740e012a5 | ||
|  | b1ad90443e | ||
|  | 76c94bfcce | ||
|  | 0ed365170c | ||
|  | 70724ec015 | ||
|  | f1dfd59c4c | ||
|  | a0dff5babf | ||
|  | 058d6ac456 | ||
|  | 9f206ebed0 | 
							
								
								
									
										29
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | |||||||
| # 🎀 Changelog | # 🎀 Changelog | ||||||
| 
 | 
 | ||||||
| ## Unreleased | ## [1.0.0] - 2021-03-04 | ||||||
| ### Added | ### Added | ||||||
| - MacOS is now officialy supported, default compile time vars have been added | - MacOS is now officialy supported, default compile time vars have been added | ||||||
| for it | for it | ||||||
| @ -18,6 +18,7 @@ it finds the path to `binName` in $PATH | |||||||
|   (like it always was) or Vim via `hilbish.inputMode()` |   (like it always was) or Vim via `hilbish.inputMode()` | ||||||
|   - Changing Vim mode throws a `hilbish.vimMode` hook |   - Changing Vim mode throws a `hilbish.vimMode` hook | ||||||
|   - The current Vim mode is also accessible with the `hilbish.vimMode` property |   - The current Vim mode is also accessible with the `hilbish.vimMode` property | ||||||
|  | - Print errors in `hilbish.timeout()` and `hilbish.goro()` callbacks | ||||||
| 
 | 
 | ||||||
| ### Fixed | ### Fixed | ||||||
| - Tab completion for executables | - Tab completion for executables | ||||||
| @ -31,6 +32,7 @@ it finds the path to `binName` in $PATH | |||||||
| - Alias expansion with quotes | - Alias expansion with quotes | ||||||
| - Add full command to history in the case of incomplete input | - Add full command to history in the case of incomplete input | ||||||
| - `hilbish.exec()` now has a windows substitute | - `hilbish.exec()` now has a windows substitute | ||||||
|  | - Fixed case of successful command after prompted for more input not writing to history | ||||||
| 
 | 
 | ||||||
| ### Changed | ### Changed | ||||||
| - The minimal config is truly minimal now | - The minimal config is truly minimal now | ||||||
| @ -53,6 +55,30 @@ as it functions the same but is OS agnostic | |||||||
| - `hilbish.flag()` has been removed | - `hilbish.flag()` has been removed | ||||||
| - `~/.hprofile.lua` has been removed, instead check in your config if `hilbish.login` | - `~/.hprofile.lua` has been removed, instead check in your config if `hilbish.login` | ||||||
| is true | 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 | ## [0.7.1] - 2021-11-22 | ||||||
| ### Fixed | ### Fixed | ||||||
| @ -341,6 +367,7 @@ This input for example will prompt for more input to complete: | |||||||
| 
 | 
 | ||||||
| First "stable" release of Hilbish. | 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.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.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 | [0.6.1]: https://github.com/Rosettea/Hilbish/compare/v0.6.0...v0.6.1 | ||||||
|  | |||||||
							
								
								
									
										62
									
								
								api.go
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								api.go
									
									
									
									
									
								
							| @ -23,6 +23,7 @@ import ( | |||||||
| var exports = map[string]lua.LGFunction { | var exports = map[string]lua.LGFunction { | ||||||
| 	"alias": hlalias, | 	"alias": hlalias, | ||||||
| 	"appendPath": hlappendPath, | 	"appendPath": hlappendPath, | ||||||
|  | 	"complete": hlcomplete, | ||||||
| 	"cwd": hlcwd, | 	"cwd": hlcwd, | ||||||
| 	"exec": hlexec, | 	"exec": hlexec, | ||||||
| 	"goro": hlgoro, | 	"goro": hlgoro, | ||||||
| @ -74,6 +75,7 @@ The nice lil shell for {blue}Lua{reset} fanatics! | |||||||
| 	util.Document(L, hshuser, "User directories to store configs and/or modules.") | 	util.Document(L, hshuser, "User directories to store configs and/or modules.") | ||||||
| 	L.SetField(mod, "userDir", hshuser) | 	L.SetField(mod, "userDir", hshuser) | ||||||
| 
 | 
 | ||||||
|  | 	// hilbish.os table | ||||||
| 	hshos := L.NewTable() | 	hshos := L.NewTable() | ||||||
| 	info, _ := osinfo.GetOSInfo() | 	info, _ := osinfo.GetOSInfo() | ||||||
| 
 | 
 | ||||||
| @ -94,11 +96,63 @@ The nice lil shell for {blue}Lua{reset} fanatics! | |||||||
| 	util.Document(L, historyModule, "History interface for Hilbish.") | 	util.Document(L, historyModule, "History interface for Hilbish.") | ||||||
| 	L.SetField(mod, "history", historyModule) | 	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) | 	L.Push(mod) | ||||||
| 
 | 
 | ||||||
| 	return 1 | 	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) { | func setVimMode(mode string) { | ||||||
| 	hooks.Em.Emit("hilbish.vimMode", mode) | 	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)") | 	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 | --- @param str string | ||||||
| */ | */ | ||||||
| func hlprompt(L *lua.LState) int { | func hlprompt(L *lua.LState) int { | ||||||
| 	prompt := L.CheckString(1) | 	prompt = L.CheckString(1) | ||||||
| 	lr.SetPrompt(fmtPrompt(prompt)) | 	lr.SetPrompt(fmtPrompt(prompt)) | ||||||
| 
 | 
 | ||||||
| 	return 0 | 	return 0 | ||||||
| @ -346,8 +400,10 @@ func hlinterval(L *lua.LState) int { | |||||||
| // Registers a completion handler for `scope`. | // Registers a completion handler for `scope`. | ||||||
| // A `scope` is currently only expected to be `command.<cmd>`, | // A `scope` is currently only expected to be `command.<cmd>`, | ||||||
| // replacing <cmd> with the name of the command (for example `command.git`). | // 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. | // `cb` must be a function that returns a table of "completion groups." | ||||||
| // Nested tables will be used as sub-completions. | // 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 scope string | ||||||
| // --- @param cb function | // --- @param cb function | ||||||
| func hlcomplete(L *lua.LState) int { | func hlcomplete(L *lua.LState) int { | ||||||
|  | |||||||
							
								
								
									
										49
									
								
								complete.go
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								complete.go
									
									
									
									
									
								
							| @ -23,6 +23,55 @@ func fileComplete(query, ctx string, fields []string) []string { | |||||||
| 	return completions | 	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) { | func matchPath(path, pref string) ([]string, error) { | ||||||
| 	var entries []string | 	var entries []string | ||||||
| 	matches, err := filepath.Glob(path + "*") | 	matches, err := filepath.Glob(path + "*") | ||||||
|  | |||||||
| @ -5,8 +5,10 @@ appendPath(dir) > Appends `dir` to $PATH | |||||||
| complete(scope, cb) > Registers a completion handler for `scope`. | complete(scope, cb) > Registers a completion handler for `scope`. | ||||||
| A `scope` is currently only expected to be `command.<cmd>`, | A `scope` is currently only expected to be `command.<cmd>`, | ||||||
| replacing <cmd> with the name of the command (for example `command.git`). | 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. | `cb` must be a function that returns a table of "completion groups." | ||||||
| Nested tables will be used as sub-completions. | 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 | cwd() > Returns the current directory of the shell | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -14,8 +14,10 @@ function hilbish.appendPath(dir) end | |||||||
| --- Registers a completion handler for `scope`. | --- Registers a completion handler for `scope`. | ||||||
| --- A `scope` is currently only expected to be `command.<cmd>`, | --- A `scope` is currently only expected to be `command.<cmd>`, | ||||||
| --- replacing <cmd> with the name of the command (for example `command.git`). | --- 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. | --- `cb` must be a function that returns a table of "completion groups." | ||||||
| --- Nested tables will be used as sub-completions. | --- 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 scope string | ||||||
| --- @param cb function | --- @param cb function | ||||||
| function hilbish.complete(scope, cb) end | function hilbish.complete(scope, cb) end | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								exec.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								exec.go
									
									
									
									
									
								
							| @ -61,6 +61,8 @@ func runInput(input, origInput string) { | |||||||
| 				} else if err != nil { | 				} else if err != nil { | ||||||
| 					fmt.Fprintln(os.Stderr, err) | 					fmt.Fprintln(os.Stderr, err) | ||||||
| 					cmdFinish(1, cmdString, origInput) | 					cmdFinish(1, cmdString, origInput) | ||||||
|  | 				} else { | ||||||
|  | 					cmdFinish(0, cmdString, origInput) | ||||||
| 				} | 				} | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @ -16,6 +16,6 @@ require ( | |||||||
| 
 | 
 | ||||||
| replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20211022004519-f67a49cb50f5 | 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 | replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10 | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							| @ -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-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 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-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 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.20211207003625-341c7985ad7d/go.mod h1:QiUAvbhg8PzCA4hlafCUl0bKD/0VmcocM4AjqtszAJs= | ||||||
| github.com/Rosettea/readline-1 v0.1.0-beta.0.20220228022904-61f5e4493011 h1:+a61iNamZiO3Xru+l/1qtpKqqltVfWEm2r/rxH9hXxY= | github.com/Rosettea/readline-1 v0.1.0-beta.0.20220228022904-61f5e4493011 h1:+a61iNamZiO3Xru+l/1qtpKqqltVfWEm2r/rxH9hXxY= | ||||||
|  | |||||||
| @ -24,12 +24,11 @@ func newFileHistory() (*fileHistory, error) { | |||||||
| 	lines := strings.Split(string(data), "\n") | 	lines := strings.Split(string(data), "\n") | ||||||
| 	for i, l := range lines { | 	for i, l := range lines { | ||||||
| 		if i == len(lines) - 1 { | 		if i == len(lines) - 1 { | ||||||
| 			println(i, l) |  | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		itms = append(itms, l) | 		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 { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -72,5 +71,5 @@ func (h *fileHistory) Len() int { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (h *fileHistory) Dump() interface{} { | func (h *fileHistory) Dump() interface{} { | ||||||
| 	return nil | 	return h.items | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								main.go
									
									
									
									
									
								
							| @ -165,6 +165,7 @@ func main() { | |||||||
| 
 | 
 | ||||||
| input: | input: | ||||||
| 	for interactive { | 	for interactive { | ||||||
|  | 		lr.SetPrompt(fmtPrompt(prompt)) | ||||||
| 		running = false | 		running = false | ||||||
| 
 | 
 | ||||||
| 		input, err := lr.Read() | 		input, err := lr.Read() | ||||||
|  | |||||||
							
								
								
									
										229
									
								
								rl.go
									
									
									
									
									
								
							
							
						
						
									
										229
									
								
								rl.go
									
									
									
									
									
								
							| @ -3,9 +3,7 @@ package main | |||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"path/filepath" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"os" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/maxlandon/readline" | 	"github.com/maxlandon/readline" | ||||||
| 	"github.com/yuin/gopher-lua" | 	"github.com/yuin/gopher-lua" | ||||||
| @ -14,15 +12,18 @@ import ( | |||||||
| type lineReader struct { | type lineReader struct { | ||||||
| 	rl *readline.Instance | 	rl *readline.Instance | ||||||
| } | } | ||||||
|  | var fileHist *fileHistory | ||||||
| 
 | 
 | ||||||
| // other gophers might hate this naming but this is local, shut up | // other gophers might hate this naming but this is local, shut up | ||||||
| func newLineReader(prompt string) *lineReader { | func newLineReader(prompt string) *lineReader { | ||||||
| 	rl := readline.NewInstance() | 	rl := readline.NewInstance() | ||||||
| 	fileHist, err := newFileHistory() | 	fh, err := newFileHistory() | ||||||
|  | 	fileHist = fh // go stupid | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		panic(err) | 		panic(err) | ||||||
| 	} | 	} | ||||||
| 	rl.SetHistoryCtrlR("file", fileHist) | 	rl.SetHistoryCtrlR("file", fileHist) | ||||||
|  | 	rl.HistoryAutoWrite = false | ||||||
| 	rl.ShowVimMode = false | 	rl.ShowVimMode = false | ||||||
| 	rl.ViModeCallback = func(mode readline.ViMode) { | 	rl.ViModeCallback = func(mode readline.ViMode) { | ||||||
| 		modeStr := "" | 		modeStr := "" | ||||||
| @ -39,12 +40,7 @@ func newLineReader(prompt string) *lineReader { | |||||||
| 		ctx := string(line) | 		ctx := string(line) | ||||||
| 		var completions []string | 		var completions []string | ||||||
| 
 | 
 | ||||||
| 		compGroup := []*readline.CompletionGroup{ | 		var compGroup []*readline.CompletionGroup | ||||||
| 			&readline.CompletionGroup{ |  | ||||||
| 				TrimSlash: false, |  | ||||||
| 				NoSpace: true, |  | ||||||
| 			}, |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		ctx = strings.TrimLeft(ctx, " ") | 		ctx = strings.TrimLeft(ctx, " ") | ||||||
| 		if len(ctx) == 0 { | 		if len(ctx) == 0 { | ||||||
| @ -60,60 +56,26 @@ func newLineReader(prompt string) *lineReader { | |||||||
| 		ctx = aliases.Resolve(ctx) | 		ctx = aliases.Resolve(ctx) | ||||||
| 
 | 
 | ||||||
| 		if len(fields) == 1 { | 		if len(fields) == 1 { | ||||||
| 			prefixes := []string{"./", "../", "/", "~/"} | 			completions, prefix := binaryComplete(query, ctx, fields) | ||||||
| 			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 | 			compGroup = append(compGroup, &readline.CompletionGroup{ | ||||||
| 			for _, dir := range filepath.SplitList(os.Getenv("PATH")) { | 				TrimSlash: false, | ||||||
| 				// print dir to stderr for debugging | 				NoSpace: true, | ||||||
| 				// search for an executable which matches our query string | 				Suggestions: completions, | ||||||
| 				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 | 			return prefix, compGroup | ||||||
| 			for cmdName := range commands { |  | ||||||
| 				if strings.HasPrefix(cmdName, query) { |  | ||||||
| 					completions = append(completions, cmdName) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			 |  | ||||||
| 			compGroup[0].Suggestions = completions |  | ||||||
| 			return query, compGroup |  | ||||||
| 		} else { | 		} else { | ||||||
| 			if completecb, ok := luaCompletions["command." + fields[0]]; ok { | 			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{ | 				err := l.CallByParam(lua.P{ | ||||||
| 					Fn: completecb, | 					Fn: completecb, | ||||||
| 					NRet: 1, | 					NRet: 1, | ||||||
| 					Protect: true, | 					Protect: true, | ||||||
| 				}) | 				}, lua.LString(query), lua.LString(ctx), luaFields) | ||||||
| 
 | 
 | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					return "", compGroup | 					return "", compGroup | ||||||
| @ -122,88 +84,86 @@ func newLineReader(prompt string) *lineReader { | |||||||
| 				luacompleteTable := l.Get(-1) | 				luacompleteTable := l.Get(-1) | ||||||
| 				l.Pop(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 { | 				if cmpTbl, ok := luacompleteTable.(*lua.LTable); ok { | ||||||
| 					cmpTbl.ForEach(func(key lua.LValue, value lua.LValue) { | 					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 key.Type() == lua.LTNumber { | ||||||
| 							// if we have only 2 fields then this is fine | 							// completion group | ||||||
| 							if len(fields) == 2 { | 							if value.Type() == lua.LTTable { | ||||||
| 								if strings.HasPrefix(value.String(), fields[1]) { | 								luaCmpGroup := value.(*lua.LTable) | ||||||
| 									completions = append(completions, value.String()) | 								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()) | ||||||
| 								} | 								} | ||||||
| 							} | 								if compItems.Type() != lua.LTTable { | ||||||
| 						} else if key.Type() == lua.LTString { | 									l.RaiseError("bad items for completion (expected table, got %v)", compItems.Type().String()) | ||||||
| 							if len(fields) == 2 { |  | ||||||
| 								if strings.HasPrefix(key.String(), fields[1]) { |  | ||||||
| 									completions = append(completions, key.String()) |  | ||||||
| 								} | 								} | ||||||
| 							} else { | 								var items []string | ||||||
| 								// if we have more than 2 fields, we need to check if the key matches | 								itemDescriptions := make(map[string]string) | ||||||
| 								// the current field and if it does, we need to check if the value is a string | 								compItems.(*lua.LTable).ForEach(func(k lua.LValue, v lua.LValue) { | ||||||
| 								// or table (nested sub completions) | 									if k.Type() == lua.LTString { | ||||||
| 								if key.String() == fields[1] { | 										// ['--flag'] = {'description', '--flag-alias'} | ||||||
| 									// if value is a table, we need to iterate over it | 										itm := v.(*lua.LTable) | ||||||
| 									// and add each value to completions | 										items = append(items, k.String()) | ||||||
| 									// check if value is either a table or function | 										itemDescriptions[k.String()] = itm.RawGet(lua.LNumber(1)).String() | ||||||
| 									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) |  | ||||||
| 												} |  | ||||||
| 											}) |  | ||||||
| 										} |  | ||||||
| 									} else { | 									} else { | ||||||
| 										// throw lua error | 										items = append(items, v.String()) | ||||||
| 										// complete.cmdname: error message... |  | ||||||
| 										l.RaiseError("complete." + fields[0] + ": completion value is not a table or function") |  | ||||||
| 									} | 									} | ||||||
|  | 								}) | ||||||
|  | 
 | ||||||
|  | 								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) | 				completions = fileComplete(query, ctx, fields) | ||||||
|  | 				compGroup = append(compGroup, &readline.CompletionGroup{ | ||||||
|  | 					TrimSlash: false, | ||||||
|  | 					NoSpace: true, | ||||||
|  | 					Suggestions: completions, | ||||||
|  | 				}) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| 		compGroup[0].Suggestions = completions |  | ||||||
| 		return "", compGroup | 		return "", compGroup | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -240,7 +200,7 @@ func (lr *lineReader) SetPrompt(prompt string) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (lr *lineReader) AddHistory(cmd string) { | func (lr *lineReader) AddHistory(cmd string) { | ||||||
| 	return | 	fileHist.Write(cmd) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (lr *lineReader) ClearInput() { | func (lr *lineReader) ClearInput() { | ||||||
| @ -273,18 +233,35 @@ func (lr *lineReader) luaAddHistory(l *lua.LState) int { | |||||||
| 	return 0 | 	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 | 	return 0 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (lr *lineReader) luaGetHistory(l *lua.LState) int { | func (lr *lineReader) luaAllHistory(L *lua.LState) int { | ||||||
| 	return 0 | 	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 | 	return 0 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (lr *lineReader) luaClearHistory(l *lua.LState) int { | func (lr *lineReader) luaClearHistory(l *lua.LState) int { | ||||||
| 	return 0 | 	return 0 | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								vars.go
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								vars.go
									
									
									
									
									
								
							| @ -2,11 +2,12 @@ package main | |||||||
| 
 | 
 | ||||||
| // String vars that are free to be changed at compile time | // String vars that are free to be changed at compile time | ||||||
| var ( | var ( | ||||||
| 	version = "v0.7.1" | 	version = "v1.0.0" | ||||||
| 	defaultConfDir = "" // ~ will be substituted for home, path for user's default config | 	defaultConfDir = "" // ~ will be substituted for home, path for user's default config | ||||||
| 	defaultHistDir = "" | 	defaultHistDir = "" | ||||||
| 	commonRequirePaths = "';./libs/?/init.lua;./?/init.lua;./?/?.lua'" | 	commonRequirePaths = "';./libs/?/init.lua;./?/init.lua;./?/?.lua'" | ||||||
| 
 | 
 | ||||||
|  | 	prompt string | ||||||
| 	multilinePrompt = "> " | 	multilinePrompt = "> " | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user