diff --git a/commands/core/lookup.go b/commands/core/lookup.go index 17139a1..7142c6d 100644 --- a/commands/core/lookup.go +++ b/commands/core/lookup.go @@ -8,6 +8,11 @@ import ( var shellAliases = make(map[string]string) +func GetAlias(name string) (string, bool) { + target, ok := shellAliases[name] + return target, ok +} + // Lookup finds matching suggestions for your input by looking at how many words you typed // it changes aliases to real commands, finds subcommands inside others, and runs generators for more suggestions // e.g. Lookup("git che") -> suggests "git checkout" @@ -18,15 +23,13 @@ func Lookup(input string) []Suggestion { } else { shellAliases = make(map[string]string) } - // if input is empty, we show nothing + if input == "" { return nil } - // convert raw input into tokens to find out how many words you typed tokens := tokenize(input) - // if the only word is empty, we show nothing if len(tokens) == 1 && tokens[0] == "" { return nil } @@ -49,7 +52,6 @@ func Lookup(input string) []Suggestion { } } - // if you only typed one word, we show root commands and their first subcommands if there is a space if len(tokens) == 1 { query := tokens[0] results := topLevelSuggestions(query) @@ -79,14 +81,12 @@ func Lookup(input string) []Suggestion { return results } - // find the first command name and check if it is in our registry rootCmdName := tokens[0] spec, exists := registry[rootCmdName] if !exists { return nil } - // look deep into subcommands to find exactly where you are typing right now currentSubs, currentOpts, currentGen := spec.Subcommands, spec.Options, spec.Generator depth := 1 @@ -125,7 +125,6 @@ func Lookup(input string) []Suggestion { results := []Suggestion{} - // find out how many words this command is allowed to have currentLimit := spec.MaxArgs tempSubs := spec.Subcommands for i := 1; i < depth; i++ { @@ -139,7 +138,6 @@ func Lookup(input string) []Suggestion { } } - // count how many words you already typed so we can follow the command rules argCount := 0 for i := depth; i < len(tokens)-1; i++ { t := tokens[i] @@ -154,7 +152,6 @@ func Lookup(input string) []Suggestion { debugLog("[core] query tokens: %v (partial: '%s')", tokens, partial) debugLog("[core] depth: %d, argCount: %d, limit: %d, allowMore: %v", depth, argCount, currentLimit, allowMoreArgs) - // make the start of the command line to show it in the suggestion menu prefixBuilder := strings.Builder{} for i := 0; i < depth; i++ { if i > 0 { @@ -173,7 +170,6 @@ func Lookup(input string) []Suggestion { } linePrefix := linePrefixBuilder.String() - // run special code to find things like git branch names or file names if currentGen != nil && allowMoreArgs { genResults := currentGen(tokens, prefix, partial) @@ -188,7 +184,6 @@ func Lookup(input string) []Suggestion { finalCmd = cleanLinePrefix + " " + g.Cmd } - // do not suggest words that you already typed to avoid repeating the same word newTokens := tokenize(finalCmd) if len(newTokens) > 0 { lastToken := newTokens[len(newTokens)-1] @@ -212,7 +207,6 @@ func Lookup(input string) []Suggestion { } } - // add normal subcommands that match the letters you are typing if allowMoreArgs { for _, sub := range currentSubs { if partial == "" || hasPrefix(sub.Name, partial) { @@ -223,7 +217,6 @@ func Lookup(input string) []Suggestion { } } - // show flags or options only if you start typing with a dash character if len(partial) > 0 && partial[0] == '-' { usedOpts := make(map[string]bool) for _, t := range tokens { @@ -243,11 +236,9 @@ func Lookup(input string) []Suggestion { return results } -// topLevelSuggestions looks for matches in your aliases, iris specs, and system commands func topLevelSuggestions(query string) []Suggestion { results, seen := []Suggestion{}, make(map[string]bool) - // check the shell aliases first because they are the most important for name, target := range shellAliases { if !seen[name] && (query == "" || hasPrefix(name, query)) { results = append(results, Suggestion{ @@ -257,7 +248,6 @@ func topLevelSuggestions(query string) []Suggestion { } } - // we search for iris commands that were registered in the code for name, spec := range registry { if seen[name] { continue @@ -279,7 +269,6 @@ func topLevelSuggestions(query string) []Suggestion { } } - // if we still find nothing, we look for system commands in your path folders for name := range pathCmds { if !seen[name] && (query == "" || hasPrefix(name, query)) { results = append(results, Suggestion{ diff --git a/root/shell.go b/root/shell.go index 7d4f9c5..ec9bd61 100644 --- a/root/shell.go +++ b/root/shell.go @@ -14,7 +14,6 @@ type procInfo struct { comm string } -// detectShell identifies the shell type by scanning parent processes func detectShell() string { pid := os.Getppid() for i := 0; i < 5 && pid > 1; i++ { @@ -55,8 +54,6 @@ func detectShell() string { return "bash" } -// getActiveInnerShell retrieves the most recently started shell in the process tree -// example: getActiveInnerShell(1234, "bash") func getActiveInnerShell(rootPid int, defaultShell string) string { cmd := exec.Command("ps", "-e", "-o", "pid,ppid,comm") out, err := cmd.Output() diff --git a/root/wrapper.go b/root/wrapper.go index 343bfe8..f82554d 100644 --- a/root/wrapper.go +++ b/root/wrapper.go @@ -47,7 +47,7 @@ func runWrapper() { c.ExtraFiles[10] = w c.Env = adapter.GetEnv(10, os.Getpid()) - ptmx, err := pty.Start(c) // start shell in a pseudo-terminal + ptmx, err := pty.Start(c) if err != nil { fmt.Fprintf(os.Stderr, "[IRIS] failed to start PTY: %v\n", err) return @@ -287,6 +287,8 @@ func runWrapper() { if !intercepted { ptmx.Write([]byte{b}) + // we handle line editing keys manually to keep naiveBuffer in sync + // since terminal is in raw mode, we must update our state for every change switch b { case 127, 0x08: // backspace: remove last character from buffer if len(naiveBuffer) > 0 { @@ -309,6 +311,17 @@ func runWrapper() { default: // track normal printable characters in the buffer for matching if b >= 32 && b <= 126 { + // if user presses space, check if the current word is an alias + if b == ' ' && naiveBuffer != "" && !strings.Contains(naiveBuffer, " ") { + if target, ok := core.GetAlias(naiveBuffer); ok { + // clear the current alias and replace it with the full command + ptmx.Write([]byte{0x15}) // ctrl+u to clear the current input line + ptmx.Write([]byte(target + " ")) + naiveBuffer = target + " " + shouldOverlayDraw = true + continue + } + } naiveBuffer += string(b) shouldOverlayDraw = true }