From 77313dfb48b1585996d9e095d092c80bb9ded209 Mon Sep 17 00:00:00 2001 From: David Dworken Date: Sun, 31 Dec 2023 13:00:56 -0800 Subject: [PATCH] Add better color support detection, for #134 --- client/cmd/query.go | 24 +++++++++++++++ client/lib/config.fish | 4 +++ client/lib/config.sh | 4 +++ client/lib/config.zsh | 4 +++ client/tui/tui.go | 69 ++++++++++++++++++++++++++++++------------ 5 files changed, 86 insertions(+), 19 deletions(-) diff --git a/client/cmd/query.go b/client/cmd/query.go index d4b0bbb..8634ae1 100644 --- a/client/cmd/query.go +++ b/client/cmd/query.go @@ -4,11 +4,13 @@ import ( "context" "fmt" "math/rand" + "os" "strings" "github.com/ddworken/hishtory/client/hctx" "github.com/ddworken/hishtory/client/lib" "github.com/ddworken/hishtory/client/tui" + "github.com/muesli/termenv" "github.com/spf13/cobra" ) @@ -62,6 +64,27 @@ var exportCmd = &cobra.Command{ }, } +var getColorSupportCmd = &cobra.Command{ + Use: "getColorSupport", + Hidden: true, + Short: "[Internal-only] Get the supported color format (returned as an exit code)", + GroupID: GROUP_ID_QUERYING, + Run: func(cmd *cobra.Command, args []string) { + switch termenv.ColorProfile() { + case termenv.TrueColor: + os.Exit(1) + case termenv.ANSI256: + os.Exit(2) + case termenv.ANSI: + os.Exit(3) + case termenv.Ascii: + os.Exit(4) + default: + os.Exit(0) + } + }, +} + var updateLocalDbFromRemoteCmd = &cobra.Command{ Use: "updateLocalDbFromRemote", Hidden: true, @@ -147,4 +170,5 @@ func init() { rootCmd.AddCommand(tqueryCmd) rootCmd.AddCommand(exportCmd) rootCmd.AddCommand(updateLocalDbFromRemoteCmd) + rootCmd.AddCommand(getColorSupportCmd) } diff --git a/client/lib/config.fish b/client/lib/config.fish index 4f5b632..d5bcedc 100644 --- a/client/lib/config.fish +++ b/client/lib/config.fish @@ -1,3 +1,7 @@ +# For detecting color rendering support for this terminal, see #134 +hishtory getColorSupport +export _hishtory_tui_color=$status + function _hishtory_post_exec --on-event fish_preexec # Runs after , but before the command is executed set --global _hishtory_command $argv diff --git a/client/lib/config.sh b/client/lib/config.sh index 289f9eb..efce036 100644 --- a/client/lib/config.sh +++ b/client/lib/config.sh @@ -5,6 +5,10 @@ if [ -n "$__hishtory_bash_config_sourced" ]; then return; fi __hishtory_bash_config_sourced=`date` +# For detecting color rendering support for this terminal, see #134 +hishtory getColorSupport +export _hishtory_tui_color=$? + # Implementation of running before/after every command based on https://jichu4n.com/posts/debug-trap-and-prompt_command-in-bash/ function __hishtory_precommand() { if [ -z "${HISHTORY_AT_PROMPT:-}" ]; then diff --git a/client/lib/config.zsh b/client/lib/config.zsh index 168d357..c203f1c 100644 --- a/client/lib/config.zsh +++ b/client/lib/config.zsh @@ -4,6 +4,10 @@ add-zsh-hook precmd _hishtory_precmd _hishtory_first_prompt=1 +# For detecting color rendering support for this terminal, see #134 +hishtory getColorSupport +export _hishtory_tui_color=$? + function _hishtory_add() { # Runs after , but before the command is executed # $1 contains the command that was run diff --git a/client/tui/tui.go b/client/tui/tui.go index ab1b7e4..b18428c 100644 --- a/client/tui/tui.go +++ b/client/tui/tui.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "regexp" + "strconv" "strings" "time" @@ -800,37 +801,67 @@ func deleteHistoryEntry(ctx context.Context, entry data.HistoryEntry) error { return lib.SendDeletionRequest(ctx, dr) } -func TuiQuery(ctx context.Context, initialQuery string) error { +func configureColorProfile(ctx context.Context) { if hctx.GetConf(ctx).ColorScheme == hctx.GetDefaultColorScheme() { // Set termenv.ANSI for the default color scheme, so that we preserve // the true default color scheme of hishtory which was initially // configured with termenv.ANSI (even though we want to support // full colors) for custom color schemes. lipgloss.SetColorProfile(termenv.ANSI) - } else if os.Getenv("HISHTORY_TEST") != "" { + return + } + if os.Getenv("HISHTORY_TEST") != "" { // We also set termenv.ANSI for tests so as to ensure that all our // test environments behave the same (by default, github actions // ubuntu and macos have different termenv support). lipgloss.SetColorProfile(termenv.ANSI) - } else { - // When the shell launches control-R it isn't hooked up to the main TTY, - // which means that termenv isn't able to accurately detect color support - // in the current terminal. This means we have to guess the right option, - // where we risk either: - // * Choosing too high of a color support, and breaking hishtory colors - // in certain terminals - // * Choosing too low of a color support, and ending up with truncating - // customized colors - // - // This is a tough situation with no right answer (as far as I can tell). - // The default terminal app on MacOS only supports termenv.ANSI256 (8 bit - // colors), which means we likely shouldn't default to TrueColor. From - // my own digging, I can't find any modern terminals that don't support - // termenv.ANSI256, so it seems like a reasonable default here. - // - // TODO: In the long term, we may want to make this configurable. + return + } + // When the shell launches control-R it isn't hooked up to the main TTY, + // which means that termenv isn't able to accurately detect color support + // in the current terminal. We set the environment variable _hishtory_tui_color + // to an int representing the termenv. If it is unset or set to 0, then we don't + // know the current color support, and we have to guess it. This means we + // risk either: + // * Choosing too high of a color support, and breaking hishtory colors + // in certain terminals + // * Choosing too low of a color support, and ending up with truncating + // customized colors + // + // The default terminal app on MacOS only supports termenv.ANSI256 (8 bit + // colors), which means we likely shouldn't default to TrueColor. From + // my own digging, I can't find any modern terminals that don't support + // termenv.ANSI256, so it seems like a reasonable default here. + colorProfileStr := os.Getenv("_hishtory_tui_color") + if colorProfileStr == "" { + // Fall back to the default + lipgloss.SetColorProfile(termenv.ANSI256) + return + } + colorProfileInt, err := strconv.Atoi(colorProfileStr) + if err != nil { + colorProfileInt = 0 + } + // The int mappings for this are defined in query.go + switch colorProfileInt { + case 1: + lipgloss.SetColorProfile(termenv.TrueColor) + case 2: + lipgloss.SetColorProfile(termenv.ANSI256) + case 3: + lipgloss.SetColorProfile(termenv.ANSI) + case 4: + lipgloss.SetColorProfile(termenv.Ascii) + default: + fallthrough + case 0: + // Unknown, so fall back to the default lipgloss.SetColorProfile(termenv.ANSI256) } +} + +func TuiQuery(ctx context.Context, initialQuery string) error { + configureColorProfile(ctx) p := tea.NewProgram(initialModel(ctx, initialQuery), tea.WithOutput(os.Stderr)) // Async: Get the initial set of rows go func() {