mirror of
https://github.com/ddworken/hishtory.git
synced 2025-02-02 11:39:24 +01:00
* Add support for configuring the TUI color scheme, for #134 * Add tests for getting and setting the custom color scheme, and support full colors where terminals support them * Add comments to document termenv.ANSI setting, and fix tests so they work uniformly
This commit is contained in:
parent
49fd540014
commit
8b7e54eab4
@ -1761,6 +1761,15 @@ func testTui_color(t *testing.T) {
|
||||
out = captureTerminalOutputComplex(t, TmuxCaptureConfig{tester: tester, complexCommands: []TmuxCommand{{Keys: "hishtory SPACE tquery ENTER"}, {Keys: "ech"}}, includeEscapeSequences: true})
|
||||
out = stripTuiCommandPrefix(t, out)
|
||||
testutils.CompareGoldens(t, out, "TestTui-ColoredOutputWithSearch-BetaMode")
|
||||
|
||||
// And one more time with customized colors
|
||||
testutils.CompareGoldens(t, tester.RunInteractiveShell(t, ` hishtory config-get color-scheme`), "TestTui-DefaultColorScheme")
|
||||
tester.RunInteractiveShell(t, ` hishtory config-set color-scheme selected-text #45f542`)
|
||||
tester.RunInteractiveShell(t, ` hishtory config-set color-scheme selected-background #4842f5`)
|
||||
tester.RunInteractiveShell(t, ` hishtory config-set color-scheme border-color #f54272`)
|
||||
out = captureTerminalOutputComplex(t, TmuxCaptureConfig{tester: tester, complexCommands: []TmuxCommand{{Keys: "hishtory SPACE tquery ENTER"}, {Keys: "ech"}}, includeEscapeSequences: true})
|
||||
out = stripTuiCommandPrefix(t, out)
|
||||
testutils.CompareGoldens(t, out, "TestTui-CustomColorScheme")
|
||||
}
|
||||
|
||||
func testTui_delete(t *testing.T) {
|
||||
|
@ -2,6 +2,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ddworken/hishtory/client/hctx"
|
||||
@ -15,6 +16,7 @@ var configAddCmd = &cobra.Command{
|
||||
GroupID: GROUP_ID_CONFIG,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lib.CheckFatalError(cmd.Help())
|
||||
os.Exit(1)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/ddworken/hishtory/client/hctx"
|
||||
"github.com/ddworken/hishtory/client/lib"
|
||||
@ -14,6 +15,7 @@ var configDeleteCmd = &cobra.Command{
|
||||
GroupID: GROUP_ID_CONFIG,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lib.CheckFatalError(cmd.Help())
|
||||
os.Exit(1)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ddworken/hishtory/client/hctx"
|
||||
@ -17,6 +18,7 @@ var configGetCmd = &cobra.Command{
|
||||
GroupID: GROUP_ID_CONFIG,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lib.CheckFatalError(cmd.Help())
|
||||
os.Exit(1)
|
||||
},
|
||||
}
|
||||
|
||||
@ -121,6 +123,18 @@ var getCustomColumnsCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var getColorScheme = &cobra.Command{
|
||||
Use: "color-scheme",
|
||||
Short: "Get the currently configured color scheme for selected text in the TUI",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx := hctx.MakeContext()
|
||||
config := hctx.GetConf(ctx)
|
||||
fmt.Println("selected-text: " + config.ColorScheme.SelectedText)
|
||||
fmt.Println("selected-background: " + config.ColorScheme.SelectedBackground)
|
||||
fmt.Println("border-color: " + config.ColorScheme.BorderColor)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(configGetCmd)
|
||||
configGetCmd.AddCommand(getEnableControlRCmd)
|
||||
@ -132,4 +146,5 @@ func init() {
|
||||
configGetCmd.AddCommand(getHighlightMatchesCmd)
|
||||
configGetCmd.AddCommand(getEnableAiCompletion)
|
||||
configGetCmd.AddCommand(getPresavingCmd)
|
||||
configGetCmd.AddCommand(getColorScheme)
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ddworken/hishtory/client/hctx"
|
||||
"github.com/ddworken/hishtory/client/lib"
|
||||
@ -15,6 +17,7 @@ var configSetCmd = &cobra.Command{
|
||||
GroupID: GROUP_ID_CONFIG,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lib.CheckFatalError(cmd.Help())
|
||||
os.Exit(1)
|
||||
},
|
||||
}
|
||||
|
||||
@ -146,6 +149,61 @@ var setTimestampFormatCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var setColorSchemeCmd = &cobra.Command{
|
||||
Use: "color-scheme",
|
||||
Short: "Set a custom color scheme",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lib.CheckFatalError(cmd.Help())
|
||||
os.Exit(1)
|
||||
},
|
||||
}
|
||||
|
||||
var setColorSchemeSelectedText = &cobra.Command{
|
||||
Use: "selected-text",
|
||||
Short: "Set the color of the selected text to the given hexadecimal color",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lib.CheckFatalError(validateColor(args[0]))
|
||||
ctx := hctx.MakeContext()
|
||||
config := hctx.GetConf(ctx)
|
||||
config.ColorScheme.SelectedText = args[0]
|
||||
lib.CheckFatalError(hctx.SetConfig(config))
|
||||
},
|
||||
}
|
||||
|
||||
var setColorSchemeSelectedBackground = &cobra.Command{
|
||||
Use: "selected-background",
|
||||
Short: "Set the background color of the selected row to the given hexadecimal color",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lib.CheckFatalError(validateColor(args[0]))
|
||||
ctx := hctx.MakeContext()
|
||||
config := hctx.GetConf(ctx)
|
||||
config.ColorScheme.SelectedBackground = args[0]
|
||||
lib.CheckFatalError(hctx.SetConfig(config))
|
||||
},
|
||||
}
|
||||
|
||||
var setColorSchemeBorderColor = &cobra.Command{
|
||||
Use: "border-color",
|
||||
Short: "Set the color of the table borders",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lib.CheckFatalError(validateColor(args[0]))
|
||||
ctx := hctx.MakeContext()
|
||||
config := hctx.GetConf(ctx)
|
||||
config.ColorScheme.BorderColor = args[0]
|
||||
lib.CheckFatalError(hctx.SetConfig(config))
|
||||
},
|
||||
}
|
||||
|
||||
func validateColor(color string) error {
|
||||
if !strings.HasPrefix(color, "#") || len(color) != 7 {
|
||||
return fmt.Errorf("color %q is invalid, it should be a hexadecimal color like #663399", color)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(configSetCmd)
|
||||
configSetCmd.AddCommand(setEnableControlRCmd)
|
||||
@ -156,4 +214,8 @@ func init() {
|
||||
configSetCmd.AddCommand(setHighlightMatchesCmd)
|
||||
configSetCmd.AddCommand(setEnableAiCompletionCmd)
|
||||
configSetCmd.AddCommand(setPresavingCmd)
|
||||
configSetCmd.AddCommand(setColorSchemeCmd)
|
||||
setColorSchemeCmd.AddCommand(setColorSchemeSelectedText)
|
||||
setColorSchemeCmd.AddCommand(setColorSchemeSelectedBackground)
|
||||
setColorSchemeCmd.AddCommand(setColorSchemeBorderColor)
|
||||
}
|
||||
|
@ -201,6 +201,14 @@ type ClientConfig struct {
|
||||
AiCompletion bool `json:"ai_completion"`
|
||||
// Whether to enable presaving
|
||||
EnablePresaving bool `json:"enable_presaving"`
|
||||
// The current color scheme for the TUI
|
||||
ColorScheme ColorScheme `json:"color_scheme"`
|
||||
}
|
||||
|
||||
type ColorScheme struct {
|
||||
SelectedText string
|
||||
SelectedBackground string
|
||||
BorderColor string
|
||||
}
|
||||
|
||||
type CustomColumnDefinition struct {
|
||||
@ -229,6 +237,14 @@ func GetConfigContents() ([]byte, error) {
|
||||
return dat, nil
|
||||
}
|
||||
|
||||
func GetDefaultColorScheme() ColorScheme {
|
||||
return ColorScheme{
|
||||
SelectedBackground: "#3300ff",
|
||||
SelectedText: "#ffff99",
|
||||
BorderColor: "#585858",
|
||||
}
|
||||
}
|
||||
|
||||
func GetConfig() (ClientConfig, error) {
|
||||
data, err := GetConfigContents()
|
||||
if err != nil {
|
||||
@ -245,6 +261,15 @@ func GetConfig() (ClientConfig, error) {
|
||||
if config.TimestampFormat == "" {
|
||||
config.TimestampFormat = "Jan 2 2006 15:04:05 MST"
|
||||
}
|
||||
if config.ColorScheme.SelectedBackground == "" {
|
||||
config.ColorScheme.SelectedBackground = GetDefaultColorScheme().SelectedBackground
|
||||
}
|
||||
if config.ColorScheme.SelectedText == "" {
|
||||
config.ColorScheme.SelectedText = GetDefaultColorScheme().SelectedText
|
||||
}
|
||||
if config.ColorScheme.BorderColor == "" {
|
||||
config.ColorScheme.BorderColor = GetDefaultColorScheme().BorderColor
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
|
27
client/lib/goldens/TestTui-CustomColorScheme
Normal file
27
client/lib/goldens/TestTui-CustomColorScheme
Normal file
@ -0,0 +1,27 @@
|
||||
Search Query: > ech[7m [0m[39m[49m
|
||||
|
||||
[91m┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│[39m Hostname CWD Timestamp Runtime Exit Code Command [91m│
|
||||
│────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│[92m[104m localhost [39m [92m/tmp/ [39m [92mOct 17 2022 21:43:21 PDT [39m [92m3s [39m [92m2 [39m [1m[92mech[0m[92m[104mo 'aaaaaa bbbb' [39m [91m[49m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
│[39m [91m│
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
[90mhiSHtory: Search your shell history[39m [90m • ctrl+h[39m [90mhelp
|
3
client/lib/goldens/TestTui-DefaultColorScheme
Normal file
3
client/lib/goldens/TestTui-DefaultColorScheme
Normal file
@ -0,0 +1,3 @@
|
||||
selected-text: #ffff99
|
||||
selected-background: #3300ff
|
||||
border-color: #585858
|
@ -41,10 +41,6 @@ var LAST_DISPATCHED_QUERY_ID = 0
|
||||
var LAST_DISPATCHED_QUERY_TIMESTAMP time.Time
|
||||
var LAST_PROCESSED_QUERY_ID = -1
|
||||
|
||||
var baseStyle = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.NormalBorder()).
|
||||
BorderForeground(lipgloss.Color("240"))
|
||||
|
||||
type keyMap struct {
|
||||
Up key.Binding
|
||||
Down key.Binding
|
||||
@ -456,11 +452,18 @@ func isCompactHeightMode() bool {
|
||||
return height < 25
|
||||
}
|
||||
|
||||
func getBaseStyle(config hctx.ClientConfig) lipgloss.Style {
|
||||
return lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.NormalBorder()).
|
||||
BorderForeground(lipgloss.Color(config.ColorScheme.BorderColor))
|
||||
}
|
||||
|
||||
func renderNullableTable(m model, helpText string) string {
|
||||
if m.table == nil {
|
||||
return strings.Repeat("\n", TABLE_HEIGHT+3)
|
||||
}
|
||||
helpTextLen := strings.Count(helpText, "\n")
|
||||
baseStyle := getBaseStyle(*hctx.GetConf(m.ctx))
|
||||
if isCompactHeightMode() && helpTextLen > 1 {
|
||||
// If the help text is expanded, and this is a small window, then we truncate the table so that the help text displays on top of it
|
||||
lines := strings.Split(baseStyle.Render(m.table.View()), "\n")
|
||||
@ -692,12 +695,12 @@ func makeTable(ctx context.Context, rows []table.Row) (table.Model, error) {
|
||||
s := table.DefaultStyles()
|
||||
s.Header = s.Header.
|
||||
BorderStyle(lipgloss.NormalBorder()).
|
||||
BorderForeground(lipgloss.Color("240")).
|
||||
BorderForeground(lipgloss.Color(config.ColorScheme.BorderColor)).
|
||||
BorderBottom(true).
|
||||
Bold(false)
|
||||
s.Selected = s.Selected.
|
||||
Foreground(lipgloss.Color("229")).
|
||||
Background(lipgloss.Color("57")).
|
||||
Foreground(lipgloss.Color(config.ColorScheme.SelectedText)).
|
||||
Background(lipgloss.Color(config.ColorScheme.SelectedBackground)).
|
||||
Bold(false)
|
||||
if config.HighlightMatches {
|
||||
MATCH_NOTHING_REGEXP := regexp.MustCompile("a^")
|
||||
@ -795,7 +798,18 @@ func deleteHistoryEntry(ctx context.Context, entry data.HistoryEntry) error {
|
||||
}
|
||||
|
||||
func TuiQuery(ctx context.Context, initialQuery string) error {
|
||||
lipgloss.SetColorProfile(termenv.ANSI)
|
||||
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") != "" {
|
||||
// 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)
|
||||
}
|
||||
p := tea.NewProgram(initialModel(ctx, initialQuery), tea.WithOutput(os.Stderr))
|
||||
// Async: Get the initial set of rows
|
||||
go func() {
|
||||
|
Loading…
Reference in New Issue
Block a user