From d0fb816ac39a95ba9bae951a089e54e6dde43061 Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Sun, 23 Feb 2020 13:06:29 +1300 Subject: [PATCH] Shell: Implement a "cd history" (cdh) builtin `cdh` with no arguments dumps the last 8 cd calls in history, and `cdh [index]` can be used to cd to re-run a specific index from that history. `cdh` itself it a thin wrapper of the `cd` builtin. There's definitely some improvements that can be made for this command, but this seems like a good starting point for getting a feel for it and ideas for changing it in the future. It's not entirely clear whether we should be storing the resolved path - or simply just the last argument passed to cd. For now we just use the last path passed into cd as this seemed like the better option for now. This means: * invalid paths will still be stored in history (potentially useful) * cdh's can be repeated for duplicate directory names * the history looks a little nicer on the eyes It might make sense to use resolved paths. Closes #397 --- Shell/GlobalState.h | 2 ++ Shell/main.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/Shell/GlobalState.h b/Shell/GlobalState.h index ede9c1990a..3a92ca34b6 100644 --- a/Shell/GlobalState.h +++ b/Shell/GlobalState.h @@ -28,6 +28,7 @@ #include #include +#include #include struct GlobalState { @@ -43,6 +44,7 @@ struct GlobalState { bool was_resized { false }; int last_return_code { 0 }; Vector directory_stack; + CircularQueue cd_history; // FIXME: have a configurable cd history length }; extern GlobalState g; diff --git a/Shell/main.cpp b/Shell/main.cpp index aa492d9843..ebfc0eba8b 100644 --- a/Shell/main.cpp +++ b/Shell/main.cpp @@ -191,7 +191,11 @@ static int sh_cd(int argc, const char** argv) if (argc == 1) { new_path = g.home; + if (g.cd_history.is_empty() || g.cd_history.last() != g.home) + g.cd_history.enqueue(g.home); } else { + if (g.cd_history.is_empty() || g.cd_history.last() != argv[1]) + g.cd_history.enqueue(argv[1]); if (strcmp(argv[1], "-") == 0) { char* oldpwd = getenv("OLDPWD"); if (oldpwd == nullptr) @@ -241,6 +245,37 @@ static int sh_cd(int argc, const char** argv) return 0; } +static int sh_cdh(int argc, const char** argv) +{ + if (argc > 2) { + fprintf(stderr, "usage: cdh [index]\n"); + return 1; + } + + if (argc == 1) { + if (g.cd_history.size() == 0) { + printf("cdh: no history available\n"); + return 0; + } + + for (int i = g.cd_history.size() - 1; i >= 0; --i) + printf("%lu: %s\n", g.cd_history.size() - i, g.cd_history.at(i).characters()); + return 0; + } + + bool ok; + size_t cd_history_index = String(argv[1]).to_uint(ok); + + if (!ok || cd_history_index < 1 || cd_history_index > g.cd_history.size()) { + fprintf(stderr, "usage: cdh [index]\n"); + return 1; + } + + const char* path = g.cd_history.at(g.cd_history.size() - cd_history_index).characters(); + const char* cd_args[] = { "cd", path }; + return sh_cd(2, cd_args); +} + static int sh_history(int, const char**) { for (size_t i = 0; i < editor.history().size(); ++i) { @@ -489,6 +524,10 @@ static bool handle_builtin(int argc, const char** argv, int& retval) retval = sh_cd(argc, argv); return true; } + if (!strcmp(argv[0], "cdh")) { + retval = sh_cdh(argc, argv); + return true; + } if (!strcmp(argv[0], "pwd")) { retval = sh_pwd(argc, argv); return true;