Skip to content

Commit

Permalink
config: option for overrides to chroma styles
Browse files Browse the repository at this point in the history
User facing changes:
- Remove file option from config
- Move theme option to theme.name
- Add theme.chromaStyleOverrides map for style overrides

Code changes:
- Copy code from quick.Highlight to use chroma.Style object instead of
  looking up by name
- Pass chroma.Style pointer instead of object as chroma Format requires
  style pointer

Fix: #27
Fix: #30
  • Loading branch information
ktprograms committed Oct 28, 2022
1 parent 968696b commit b6f3043
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 32 deletions.
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Flags:
-v, --version version for jqp
```

`jqp` also supports input from STDIN.
`jqp` also supports input from STDIN. STDIN takes precedence over the command line flag.

```
➜ curl "https://api.github.com/repos/stedolan/jq/issues?per_page=2" | jqp
Expand Down Expand Up @@ -97,20 +97,36 @@ If a configuration option is present in both the configuration file and the comm
### Available Configuration Options

```yaml
theme: "nord" # controls the color scheme
file: "/path/to/input/file.json" # stdin takes precedence over command line flag and this option
theme:
name: "nord" # controls the color scheme
chromaStyleOverrides: # override parts of the chroma style
kc: "#009900 underline" # keys use the chroma short names
```
## Themes
Themes can be specified on the command line via the `-t/--theme <themeName>` flag. You can also set a theme in your [configuration file](#configuration).

```yaml
theme: "monokai"
theme:
name: "monokai"
```

<img width="1624" alt="Screen Shot 2022-10-02 at 5 31 40 PM" src="https://user-images.githubusercontent.com/23270779/193477383-db5ca769-12bf-4fd0-b826-b1fd4086eac3.png">

### Chroma Style Overrides

Overrides to the chroma styles used for a theme can be configured in your [configuration file](#configuration).

For the list of short keys, see [`chroma.StandardTypes`](https://github.com/alecthomas/chroma/blob/d38b87110b078027006bc34aa27a065fa22295a1/types.go#L210-L308). To see which token to use for a value, see the [JSON lexer](https://github.com/alecthomas/chroma/blob/master/lexers/embedded/json.xml) (look for `<token>` tags). To see the color and what's used in the style you're using, look for your style in the chroma [styles directory](https://github.com/alecthomas/chroma/tree/master/styles).

```yaml
theme:
name: "monokai" # name is required to know which theme to override
chromaStyleOverrides:
kc: "#009900 underline"
```

Themes are broken up into [light](#light-themes) and [dark](#dark-themes) themes. Light themes work best in terminals with a light background and dark themes work best in a terminal with a dark background. If no theme is specified or a non-existant theme is provided, the default theme is used, which was created to work with both terminals with a light and dark background.

### Light Themes
Expand Down
52 changes: 37 additions & 15 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import (
"fmt"
"os"

"github.com/alecthomas/chroma/v2"
"github.com/charmbracelet/bubbletea"
"github.com/noahgorstein/jqp/tui/bubbles/jqplayground"
"github.com/noahgorstein/jqp/tui/theme"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)

Expand All @@ -20,14 +20,33 @@ var rootCmd = &cobra.Command{
Long: `jqp is a TUI to explore the jq command line utility`,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
configTheme := viper.GetString(configKeysName.themeName)
if !cmd.Flags().Changed(flagsName.theme) {
flags.theme = configTheme
}
themeOverrides := viper.GetStringMapString(configKeysName.themeOverrides)

jqtheme, defaultTheme := theme.GetTheme(flags.theme)
// If not using the default theme,
// and if theme specified is the same as in the config,
// which happens if the theme flag was used,
// apply chroma style overrides.
if !defaultTheme && configTheme == flags.theme && len(themeOverrides) > 0 {
// Reverse chroma.StandardTypes to be keyed by short string
chromaTypes := make(map[string]chroma.TokenType)
for tokenType, short := range chroma.StandardTypes {
chromaTypes[short] = tokenType
}

cmd.Flags().VisitAll(func(f *pflag.Flag) {
// Apply the viper config value to the flag when the flag is not set and viper has a value
if !f.Changed && viper.IsSet(f.Name) {
val := viper.Get(f.Name)
cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val))
builder := jqtheme.ChromaStyle.Builder()
for k, v := range themeOverrides {
builder.Add(chromaTypes[k], v)
}
})
style, err := builder.Build()
if err == nil {
jqtheme.ChromaStyle = style
}
}

if isInputFromPipe() {
stdin := streamToBytes(os.Stdin)
Expand All @@ -37,7 +56,7 @@ var rootCmd = &cobra.Command{
return errors.New("JSON is not valid")
}

bubble := jqplayground.New(stdin, "STDIN", theme.GetTheme(flags.theme))
bubble := jqplayground.New(stdin, "STDIN", jqtheme)
p := tea.NewProgram(bubble, tea.WithAltScreen())
if err := p.Start(); err != nil {
return err
Expand Down Expand Up @@ -69,7 +88,7 @@ var rootCmd = &cobra.Command{
return err
}

bubble := jqplayground.New(data, fi.Name(), theme.GetTheme(flags.theme))
bubble := jqplayground.New(data, fi.Name(), jqtheme)
p := tea.NewProgram(bubble, tea.WithAltScreen())

if err := p.Start(); err != nil {
Expand Down Expand Up @@ -107,22 +126,25 @@ func initConfig() {
}

var flags struct {
filepath string
theme string
filepath, theme string
}

var flagsName = struct {
file string
fileShort string
theme string
themeShort string
file, fileShort, theme, themeShort string
}{
file: "file",
fileShort: "f",
theme: "theme",
themeShort: "t",
}

var configKeysName = struct {
themeName, themeOverrides string
}{
themeName: "theme.name",
themeOverrides: "theme.chromaStyleOverrides",
}

var cfgFile string

func Execute() {
Expand Down
7 changes: 3 additions & 4 deletions tui/bubbles/inputdata/inputdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"strings"

"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/quick"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
Expand All @@ -34,7 +33,7 @@ func New(inputJson []byte, filename string, theme theme.Theme) Bubble {
Styles: styles,
viewport: v,
inputJson: inputJson,
highlightedJson: highlightInputJson(inputJson, *theme.ChromaStyle),
highlightedJson: highlightInputJson(inputJson, theme.ChromaStyle),
filename: filename,
}
return b
Expand All @@ -45,7 +44,7 @@ func (b *Bubble) SetBorderColor(color lipgloss.TerminalColor) {
b.Styles.infoStyle.BorderForeground(color)
}

func highlightInputJson(inputJson []byte, chromaStyle chroma.Style) *bytes.Buffer {
func highlightInputJson(inputJson []byte, chromaStyle *chroma.Style) *bytes.Buffer {
var f interface{}
// TODO: error handling
json.Unmarshal(inputJson, &f)
Expand All @@ -55,7 +54,7 @@ func highlightInputJson(inputJson []byte, chromaStyle chroma.Style) *bytes.Buffe
json.Indent(&prettyJSON, []byte(inputJson), "", " ")

buf := new(bytes.Buffer)
quick.Highlight(buf, prettyJSON.String(), "json", utils.GetTerminalColorSupport(), chromaStyle.Name)
utils.HighlightJson(buf, prettyJSON.String(), chromaStyle)

return buf
}
Expand Down
2 changes: 1 addition & 1 deletion tui/bubbles/jqplayground/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (b *Bubble) executeQuery(ctx context.Context) tea.Cmd {
results.WriteString(fmt.Sprintf("%s\n", string(r)))
}

highlightedOutput := highlightJson([]byte(results.String()), *b.theme.ChromaStyle)
highlightedOutput := highlightJson([]byte(results.String()), b.theme.ChromaStyle)
return queryResultMsg{
rawResults: results.String(),
highlightedResults: highlightedOutput.String(),
Expand Down
7 changes: 3 additions & 4 deletions tui/bubbles/jqplayground/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/json"

"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/quick"
"github.com/noahgorstein/jqp/tui/utils"
)

Expand All @@ -14,18 +13,18 @@ func isValidJson(input []byte) bool {
return json.Unmarshal(input, &js) == nil
}

func highlightJson(input []byte, chromaStyle chroma.Style) *bytes.Buffer {
func highlightJson(input []byte, chromaStyle *chroma.Style) *bytes.Buffer {

if isValidJson(input) {
var f interface{}
json.Unmarshal(input, &f)
var prettyJSON bytes.Buffer
json.Indent(&prettyJSON, []byte(input), "", " ")
buf := new(bytes.Buffer)
quick.Highlight(buf, prettyJSON.String(), "json", utils.GetTerminalColorSupport(), chromaStyle.Name)
utils.HighlightJson(buf, prettyJSON.String(), chromaStyle)
return buf
}
buf := new(bytes.Buffer)
quick.Highlight(buf, string(input), "json", utils.GetTerminalColorSupport(), chromaStyle.Name)
utils.HighlightJson(buf, string(input), chromaStyle)
return buf
}
7 changes: 4 additions & 3 deletions tui/theme/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,11 +427,12 @@ var themeMap = map[string]Theme{
},
}

func GetTheme(theme string) Theme {
// returns a theme by name, and true if default theme was returned
func GetTheme(theme string) (Theme, bool) {
lowercasedTheme := strings.ToLower(strings.TrimSpace(theme))
if value, ok := themeMap[lowercasedTheme]; ok {
return value
return value, false
} else {
return getDefaultTheme()
return getDefaultTheme(), true
}
}
31 changes: 30 additions & 1 deletion tui/utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
package utils

import (
"io"

"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/formatters"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"
"github.com/charmbracelet/lipgloss"
"github.com/muesli/termenv"
)

func HighlightJson(w io.Writer, source string, style *chroma.Style) error {
l := lexers.Get("json")
if l == nil {
l = lexers.Fallback
}
l = chroma.Coalesce(l)

f := formatters.Get(getTerminalColorSupport())
if f == nil {
f = formatters.Fallback
}

if style == nil {
style = styles.Fallback
}

it, err := l.Tokenise(nil, source)
if err != nil {
return err
}
return f.Format(w, style, it)
}

// returns a string used for chroma syntax highlighting
func GetTerminalColorSupport() string {
func getTerminalColorSupport() string {
switch lipgloss.ColorProfile() {
case termenv.Ascii:
return "terminal"
Expand Down

0 comments on commit b6f3043

Please sign in to comment.