diff --git a/cmd/cat.go b/cmd/cat.go index 9d380ec..3d73eed 100644 --- a/cmd/cat.go +++ b/cmd/cat.go @@ -2,7 +2,7 @@ package cmd import ( "gosh/types" - "gosh/util" + "gosh/utils" "os" "github.com/fatih/color" @@ -10,7 +10,7 @@ import ( func GoshCat(sh *types.Shell, file_path string) types.CmdOutput { - file_path = util.ExpandHome(file_path) + file_path = utils.ExpandHome(file_path) data, err := os.ReadFile(file_path) red := color.New(color.FgRed).SprintFunc() diff --git a/cmd/cd.go b/cmd/cd.go index 8c4ec04..bd7cb26 100644 --- a/cmd/cd.go +++ b/cmd/cd.go @@ -4,14 +4,14 @@ import ( "os" "gosh/types" - "gosh/util" + "gosh/utils" "github.com/fatih/color" ) func GoshCd(sh *types.Shell, dir string) types.CmdOutput { - dir = util.ExpandHome(dir) + dir = utils.ExpandHome(dir) err := os.Chdir(dir) red := color.New(color.FgRed).SprintFunc() diff --git a/cmd/ls.go b/cmd/ls.go index c9e9d17..2e9ca08 100644 --- a/cmd/ls.go +++ b/cmd/ls.go @@ -2,7 +2,7 @@ package cmd import ( "gosh/types" - "gosh/util" + "gosh/utils" "os" "strings" @@ -19,7 +19,7 @@ func GoshLs(sh *types.Shell, paths ...string) types.CmdOutput { dir = sh.Cd } - dir = util.ExpandHome(dir) + dir = utils.ExpandHome(dir) children, err := os.ReadDir(dir) diff --git a/cmd/pyapath.go b/cmd/pyapath.go index a1ee205..2dc52b2 100644 --- a/cmd/pyapath.go +++ b/cmd/pyapath.go @@ -2,7 +2,7 @@ package cmd import ( "gosh/types" - "gosh/util" + "gosh/utils" "path/filepath" "github.com/fatih/color" @@ -10,7 +10,7 @@ import ( func GolshPyapath(sh *types.Shell, path string) types.CmdOutput { - path = util.ExpandHome(path) + path = utils.ExpandHome(path) abs, err := filepath.Abs(path) diff --git a/go.mod b/go.mod index 60be4cd..f596bf5 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module gosh go 1.21.5 require ( + github.com/chzyer/readline v1.5.1 // indirect github.com/fatih/color v1.18.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/go.sum b/go.sum index 33148a4..85e5e14 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -5,6 +9,7 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= diff --git a/main.go b/main.go index 0901407..6a32b3c 100644 --- a/main.go +++ b/main.go @@ -1,15 +1,16 @@ package main import ( - "bufio" "fmt" "gosh/cmd" "gosh/types" + "gosh/utils" "os" "os/exec" "path/filepath" "strings" + "github.com/chzyer/readline" "github.com/fatih/color" ) @@ -163,10 +164,6 @@ func gosh_process_input(sh *types.Shell, input string) { } func main() { - reader := bufio.NewReader(os.Stdin) - - c := color.New(color.FgCyan) - dir, _ := os.Getwd() // Init shell @@ -181,18 +178,31 @@ func main() { _, err := os.Stat(golshrc_path) golshrc_exists := err == nil - fmt.Println(golshrc_exists) if golshrc_exists { - fmt.Println("exists") process_golshscript(&sh, golshrc_path) } + // Set up readline + rl, err := readline.NewEx(&readline.Config{ + Prompt: sh.Cd + "> ", + AutoComplete: &utils.AutoCompleter{Sh: &sh}, + InterruptPrompt: "^C", + EOFPrompt: "exit", + }) + + if err != nil { + panic(err) + } + + defer rl.Close() + + cd_path_color := color.New(color.FgBlue) + var should_exit = false for { - c.Print(sh.Cd) - fmt.Print("> ") + rl.SetPrompt(cd_path_color.Sprint("GOLSH "+sh.Cd+"❯") + " ") - input, err := reader.ReadString('\n') + input, err := rl.Readline() if err != nil { fmt.Println("Error reading input: ", err) diff --git a/utils/AutoCompleter.go b/utils/AutoCompleter.go new file mode 100644 index 0000000..33ffa2c --- /dev/null +++ b/utils/AutoCompleter.go @@ -0,0 +1,69 @@ +package utils + +import ( + "gosh/types" + "os" + "strings" +) + +type AutoCompleter struct { + Sh *types.Shell +} + +func (a *AutoCompleter) Do(line []rune, pos int) ([][]rune, int) { + cmds := []string{"ls", "cd", "pwd", "cat", "grep", "clear", "apath", "exit"} + potential_suggestions := []string{} + + s_line := string(line) + words := strings.Fields(s_line) + + last_word := "" + if len(words) > 0 { + last_word = words[len(words)-1] + } + + // If typing the command (first word), suggest commands + if len(words) == 0 || (len(words) == 1 && s_line[len(s_line)-1] != ' ') { + potential_suggestions = append(potential_suggestions, cmds...) + } else if words[0] == "cd" || words[0] == "ls" { + // Suggest directories for 'cd' + last_word := "" + if len(words) > 1 { + last_word = words[len(words)-1] + } + children, err := os.ReadDir(a.Sh.Cd) + if err == nil { + for _, child := range children { + if child.IsDir() && (last_word == "" || strings.HasPrefix(child.Name(), last_word)) { + potential_suggestions = append(potential_suggestions, child.Name()) + } + } + } + } else if words[0] == "cat" { + // Suggest directories for 'cat' + last_word := "" + if len(words) > 1 { + last_word = words[len(words)-1] + } + children, err := os.ReadDir(a.Sh.Cd) + if err == nil { + for _, child := range children { + if !child.IsDir() && (last_word == "" || strings.HasPrefix(child.Name(), last_word)) { + potential_suggestions = append(potential_suggestions, child.Name()) + } + } + } + } + + var suggestions [][]rune + for _, s := range potential_suggestions { + // Only append the part that should be completed (remove the prefix) + if last_word != "" && strings.HasPrefix(s, last_word) { + suggestions = append(suggestions, []rune(s[len(last_word):])) + } else { + suggestions = append(suggestions, []rune(s)) + } + } + + return suggestions, len([]rune(s_line)) +} diff --git a/util/util.go b/utils/util.go similarity index 94% rename from util/util.go rename to utils/util.go index 0ca574f..92d70c8 100644 --- a/util/util.go +++ b/utils/util.go @@ -1,4 +1,4 @@ -package util +package utils import ( "os"