package input_parser import ( "bbash/environment" "bufio" "fmt" "os" "strings" "golang.org/x/term" ) var prevGhostString string type Input struct { // The instruction a.k.a first argument Instruction string // The args, currently just the string after the instruction Args []string Args_raw string Flags []string } func Parse(env *environment.Env) Input { input := input_str(env) return Parse_input(input) } func Parse_input(input string) Input { split := strings.Split(strings.TrimSpace(string(input)), " ") instruction := strings.TrimSpace(split[0]) args_raw := "" if len(split) > 1 { args_raw = split[1] } var flags []string var args []string for _, arg := range split[1:] { if len(arg) == 1 { if arg[0] == '-' { continue } _ = append(args, string(arg[0])) } else if arg[0:2] == "--" { var result = strings.TrimSpace(arg) flags = append(flags, string(result[2:])) continue } else if arg[0] == '-' { var result = strings.TrimSpace(arg) for _, c := range result[1:] { flags = append(flags, string(c)) } continue } else { args = append(args, strings.TrimSpace(arg)) } } return Input{ Instruction: instruction, Args: args, Flags: flags, Args_raw: args_raw, } } func input_str(env *environment.Env) string { std_fd := os.Stdin.Fd() term_restore, err := term.MakeRaw(int(std_fd)) if err != nil { fmt.Println(fmt.Sprintf("Error opening terminal: %s", err.Error())) panic(err.Error()) } reader := bufio.NewReader(os.Stdin) var input string history_index := len(env.History) // not -1 since we start at the new one env.History = append(env.History, input) cursor := 0 env.History[len(env.History)-1] = input fmt.Print("\r\033[K") fmt.Print(env.Path) fmt.Print(" > ") fmt.Print(input) for { if cursor > len(input) { cursor = len(input) } if cursor < 0 { cursor = 0 } r_rune, _, err := reader.ReadRune() if err != nil { fmt.Print(fmt.Sprintf("Error reading user input: %s", err.Error())) } switch r_rune { case 3: // ^C fmt.Println("^C") cursor = 0 input = "" case 4: // ^D input = "exit" goto loop_exit case 8: // input_at := input[:cursor] input_after := input[cursor:] last_index := strings.LastIndex(input_at, " ") if last_index == -1 { input_at = "" cursor = 0 } else { input_at = input_at[:last_index] cursor = len(input_at) } input = input_at + input_after case 9: // TAB complete := AutoComplete(input[:cursor], *env) input = complete + input[cursor:] cursor = len(complete) case 27: // Escape input, history_index, cursor = Escape_handler(reader, history_index, input, *env, cursor) case 127: //packspace input_at := input[:cursor] input_after := input[cursor:] if len(input) > 0 { input_at = input_at[:len(input_at)-1] cursor-- } input = input_at + input_after case 13: // Enter goto loop_exit default: input_at := input[:cursor] input_after := input[cursor:] input_at += string(r_rune) input = input_at + input_after cursor++ } ghost_input := AutoCompleteHistory(input, *env) env.History[len(env.History)-1] = input fmt.Print("\r") fmt.Print(env.Path) fmt.Print(" > \033[K") fmt.Print(input) fmt.Print("\033[2m", ghost_input, "\033[0m") prevGhostString = "" backwardSteps := len(ghost_input) if backwardSteps != 0 { fmt.Printf("\033[%dD", backwardSteps) prevGhostString = ghost_input } fmt.Print("\r") fmt.Print(env.Path) fmt.Print(" > ") for i := 0; i < cursor; i++ { fmt.Print("\033[C") } } loop_exit: term.Restore(int(std_fd), term_restore) fmt.Println() return input }