From e72ef668ea96a431e8d42b85444fe16101267ace Mon Sep 17 00:00:00 2001 From: David Dworken Date: Fri, 11 Nov 2022 17:17:54 -0800 Subject: [PATCH] Add custom timestamp format as requested in the original HN thread --- README.md | 5 +++ client/client_test.go | 33 +++++++++++++++++++ client/hctx/hctx.go | 5 +++ client/lib/goldens/TestTimestampFormat-query | 3 ++ client/lib/goldens/TestTimestampFormat-tquery | 30 +++++++++++++++++ client/lib/lib.go | 2 +- hishtory.go | 8 ++--- 7 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 client/lib/goldens/TestTimestampFormat-query create mode 100644 client/lib/goldens/TestTimestampFormat-tquery diff --git a/README.md b/README.md index 776ea68..368bc43 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,11 @@ But if you'd like to self-host the hishtory backend, you can! The backend is a s hiSHtory imports your existing shell history by default. If for some reason this didn't work (e.g. you had your shell history in a non-standard file), you can import it by piping it into `hishtory import` (e.g. `cat ~/.my_history | hishtory import`). +
+Custom timestamp formats +You can configure a custom timestamp format for hiSHtory via `hishtory config-set timestamp-format '2006/Jan/2 15:04'`. The timestamp format string should be in [the format used by Go's `time.Format(...)`](https://pkg.go.dev/time#Time.Format). +
+
Uninstalling If you'd like to uninstall hishtory, just run `hishtory uninstall`. Note that this deletes the SQLite DB storing your history, so consider running a `hishtory export` first. diff --git a/client/client_test.go b/client/client_test.go index 2ca949f..25bc19e 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -2098,6 +2098,39 @@ echo bar`) } } +func TestTimestampFormat(t *testing.T) { + // Setup + tester := zshTester{} + defer testutils.BackupAndRestore(t)() + userSecret := installHishtory(t, tester, "") + + // Add some entries with fixed timestamps + tmz, err := time.LoadLocation("America/Los_Angeles") + if err != nil { + t.Fatalf("failed to load timezone: %v", err) + } + entry1 := testutils.MakeFakeHistoryEntry("table_cmd1") + entry1.StartTime = time.Unix(1650096186, 0).In(tmz) + entry1.EndTime = time.Unix(1650096190, 0).In(tmz) + manuallySubmitHistoryEntry(t, userSecret, entry1) + entry2 := testutils.MakeFakeHistoryEntry("table_cmd2") + entry2.StartTime = time.Unix(1650096196, 0).In(tmz) + entry2.EndTime = time.Unix(1650096220, 0).In(tmz) + entry2.CurrentWorkingDirectory = "~/foo/" + entry2.ExitCode = 3 + manuallySubmitHistoryEntry(t, userSecret, entry2) + + // Set a custom timestamp format + tester.RunInteractiveShell(t, ` hishtory config-set timestamp-format '2006/Jan/2 15:04'`) + + // And check that it is displayed in both the tui and the classic view + out := hishtoryQuery(t, tester, "-pipefail") + compareGoldens(t, out, "TestTimestampFormat-query") + out = captureTerminalOutput(t, tester, []string{"hishtory SPACE tquery SPACE -pipefail ENTER"}) + out = strings.TrimSpace(strings.Split(out, "hishtory tquery")[1]) + compareGoldens(t, out, "TestTimestampFormat-tquery") +} + func TestRemoveDuplicateRows(t *testing.T) { // Setup tester := zshTester{} diff --git a/client/hctx/hctx.go b/client/hctx/hctx.go index 17a960d..81576fc 100644 --- a/client/hctx/hctx.go +++ b/client/hctx/hctx.go @@ -174,6 +174,8 @@ type ClientConfig struct { IsOffline bool `json:"is_offline"` // Whether duplicate commands should be displayed FilterDuplicateCommands bool `json:"filter_duplicate_commands"` + // A format string for the timestamp + TimestampFormat string `json:"timestamp_format"` } type CustomColumnDefinition struct { @@ -215,6 +217,9 @@ func GetConfig() (ClientConfig, error) { if config.DisplayedColumns == nil || len(config.DisplayedColumns) == 0 { config.DisplayedColumns = []string{"Hostname", "CWD", "Timestamp", "Runtime", "Exit Code", "Command"} } + if config.TimestampFormat == "" { + config.TimestampFormat = "Jan 2 2006 15:04:05 MST" + } return config, nil } diff --git a/client/lib/goldens/TestTimestampFormat-query b/client/lib/goldens/TestTimestampFormat-query new file mode 100644 index 0000000..1ad1ec9 --- /dev/null +++ b/client/lib/goldens/TestTimestampFormat-query @@ -0,0 +1,3 @@ +Hostname CWD Timestamp Runtime Exit Code Command +localhost ~/foo/ 2022/Apr/16 01:03 24s 3 table_cmd2 +localhost /tmp/ 2022/Apr/16 01:03 4s 2 table_cmd1 diff --git a/client/lib/goldens/TestTimestampFormat-tquery b/client/lib/goldens/TestTimestampFormat-tquery new file mode 100644 index 0000000..181d3f9 --- /dev/null +++ b/client/lib/goldens/TestTimestampFormat-tquery @@ -0,0 +1,30 @@ +-pipefail + + + +Search Query: > -pipefail + +┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname CWD Timestamp Runtime Exit Code Command │ +│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│ +│ localhost ~/foo/ 2022/Apr/16 01:03 24s 3 table_cmd2 │ +│ localhost /tmp/ 2022/Apr/16 01:03 4s 2 table_cmd1 │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ \ No newline at end of file diff --git a/client/lib/lib.go b/client/lib/lib.go index 0194add..bfc9d3e 100644 --- a/client/lib/lib.go +++ b/client/lib/lib.go @@ -435,7 +435,7 @@ func buildTableRow(ctx *context.Context, columnNames []string, entry data.Histor case "CWD": row = append(row, entry.CurrentWorkingDirectory) case "Timestamp": - row = append(row, entry.StartTime.Format("Jan 2 2006 15:04:05 MST")) + row = append(row, entry.StartTime.Format(hctx.GetConf(ctx).TimestampFormat)) case "Runtime": row = append(row, entry.EndTime.Sub(entry.StartTime).Round(time.Millisecond).String()) case "Exit Code": diff --git a/hishtory.go b/hishtory.go index 20a06aa..f992bc6 100644 --- a/hishtory.go +++ b/hishtory.go @@ -184,6 +184,10 @@ func main() { vals := os.Args[3:] config.DisplayedColumns = vals lib.CheckFatalError(hctx.SetConfig(config)) + case "timestamp-format": + val := os.Args[3] + config.TimestampFormat = val + lib.CheckFatalError(hctx.SetConfig(config)) case "custom-columns": log.Fatalf("Please use config-add and config-delete to interact with custom-columns") default: @@ -257,7 +261,6 @@ func main() { default: log.Fatalf("Unrecognized config key: %s", key) } - case "reupload": // Purposefully undocumented since this command is generally not necessary to run lib.CheckFatalError(lib.Reupload(hctx.MakeContext())) @@ -476,6 +479,3 @@ func export(ctx *context.Context, query string) { } // TODO(feature): Add a session_id column that corresponds to the shell session the command was run in - -// TODO: add a config option for timestamp formatting -// TODO: handle control-c in the TUI