New Upstream Release - jqp
Ready changes
Summary
Merged new upstream version: 0.4.0 (was: 0.3.0).
Diff
diff --git a/README.md b/README.md
index 980a715..3246af2 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,12 @@ brew install noahgorstein/tap/jqp
sudo port install jqp
```
+### Arch Linux
+Available through the Arch User Repository as [jqp-bin](https://aur.archlinux.org/packages/jqp-bin).
+```bash
+yay -S jqp-bin
+```
+
### Github releases
Download the relevant asset for your operating system from the latest Github release. Unpack it, then move the binary to somewhere accessible in your `PATH`, e.g. `mv ./jqp /usr/local/bin`.
@@ -45,7 +51,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
@@ -97,8 +103,10 @@ 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
@@ -106,11 +114,25 @@ file: "/path/to/input/file.json" # stdin takes precedence over command line flag
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
diff --git a/cmd/root.go b/cmd/root.go
index 4666a75..d51ffd4 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -5,29 +5,48 @@ 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"
)
var rootCmd = &cobra.Command{
- Version: "0.3.0",
+ Version: "0.4.0",
Use: "jqp",
Short: "jqp is a TUI to explore jq",
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)
@@ -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
@@ -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 {
@@ -107,15 +126,11 @@ 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",
@@ -123,6 +138,13 @@ var flagsName = struct {
themeShort: "t",
}
+var configKeysName = struct {
+ themeName, themeOverrides string
+}{
+ themeName: "theme.name",
+ themeOverrides: "theme.chromaStyleOverrides",
+}
+
var cfgFile string
func Execute() {
diff --git a/debian/changelog b/debian/changelog
index b14f1c0..0f62767 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+jqp (0.4.0-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Sun, 28 May 2023 20:34:36 -0000
+
jqp (0.3.0-1) unstable; urgency=medium
* New upstream release
diff --git a/tui/bubbles/inputdata/inputdata.go b/tui/bubbles/inputdata/inputdata.go
index e43574a..5b85e22 100644
--- a/tui/bubbles/inputdata/inputdata.go
+++ b/tui/bubbles/inputdata/inputdata.go
@@ -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"
@@ -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
@@ -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)
@@ -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
}
diff --git a/tui/bubbles/jqplayground/commands.go b/tui/bubbles/jqplayground/commands.go
index 99248b0..ccaac8e 100644
--- a/tui/bubbles/jqplayground/commands.go
+++ b/tui/bubbles/jqplayground/commands.go
@@ -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(),
diff --git a/tui/bubbles/jqplayground/update.go b/tui/bubbles/jqplayground/update.go
index e4c005f..3fffab4 100644
--- a/tui/bubbles/jqplayground/update.go
+++ b/tui/bubbles/jqplayground/update.go
@@ -90,6 +90,7 @@ func (b Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmd = b.writeOutputToFile()
cmds = append(cmds, cmd)
} else if b.state == state.Query {
+ b.queryinput.RotateHistory()
b.state = state.Running
var ctx context.Context
ctx, b.cancel = context.WithCancel(context.Background())
diff --git a/tui/bubbles/jqplayground/util.go b/tui/bubbles/jqplayground/util.go
index ac1e813..6f444d6 100644
--- a/tui/bubbles/jqplayground/util.go
+++ b/tui/bubbles/jqplayground/util.go
@@ -5,7 +5,6 @@ import (
"encoding/json"
"github.com/alecthomas/chroma/v2"
- "github.com/alecthomas/chroma/v2/quick"
"github.com/noahgorstein/jqp/tui/utils"
)
@@ -14,7 +13,7 @@ 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{}
@@ -22,10 +21,10 @@ func highlightJson(input []byte, chromaStyle chroma.Style) *bytes.Buffer {
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
}
diff --git a/tui/bubbles/queryinput/queryinput.go b/tui/bubbles/queryinput/queryinput.go
index bbe2201..ecd7ea7 100644
--- a/tui/bubbles/queryinput/queryinput.go
+++ b/tui/bubbles/queryinput/queryinput.go
@@ -1,6 +1,7 @@
package queryinput
import (
+ "container/list"
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
@@ -10,6 +11,10 @@ import (
type Bubble struct {
Styles Styles
textinput textinput.Model
+
+ history *list.List
+ historyMaxLen int
+ historySelected *list.Element
}
func New(theme theme.Theme) Bubble {
@@ -26,6 +31,9 @@ func New(theme theme.Theme) Bubble {
return Bubble{
Styles: s,
textinput: ti,
+
+ history: list.New(),
+ historyMaxLen: 512,
}
}
@@ -37,6 +45,14 @@ func (b Bubble) GetInputValue() string {
return b.textinput.Value()
}
+func (b *Bubble) RotateHistory() {
+ b.history.PushFront(b.textinput.Value())
+ b.historySelected = b.history.Front()
+ if b.history.Len() > b.historyMaxLen {
+ b.history.Remove(b.history.Back())
+ }
+}
+
func (b Bubble) Init() tea.Cmd {
return textinput.Blink
}
@@ -51,6 +67,32 @@ func (b Bubble) View() string {
}
func (b Bubble) Update(msg tea.Msg) (Bubble, tea.Cmd) {
+ if msg, ok := msg.(tea.KeyMsg); ok {
+ switch msg.Type {
+ case tea.KeyUp:
+ if b.history.Len() == 0 {
+ break
+ }
+ n := b.historySelected.Next()
+ if n != nil {
+ b.textinput.SetValue(n.Value.(string))
+ b.textinput.CursorEnd()
+ b.historySelected = n
+ }
+ case tea.KeyDown:
+ if b.history.Len() == 0 {
+ break
+ }
+ p := b.historySelected.Prev()
+ if p != nil {
+ b.textinput.SetValue(p.Value.(string))
+ b.textinput.CursorEnd()
+ b.historySelected = p
+ }
+ case tea.KeyEnter:
+ b.RotateHistory()
+ }
+ }
var (
cmd tea.Cmd
diff --git a/tui/theme/theme.go b/tui/theme/theme.go
index ffa6c98..2183466 100644
--- a/tui/theme/theme.go
+++ b/tui/theme/theme.go
@@ -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
}
}
diff --git a/tui/utils/utils.go b/tui/utils/utils.go
index 07bc16e..7be2ae5 100644
--- a/tui/utils/utils.go
+++ b/tui/utils/utils.go
@@ -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"
More details
Historical runs
- failed: src/github.com/rivo/uniseg/properties.go:137:20: error: expected ‘(’
- nothing-to-do: Last upstream version 0.2.0 already imported.
- worker-failure: breezy.errors.TransportError: Transport error: [Errno 28] No space left on device: '/tmp/janitor_ug9j3km/jqp/.git/objects/80' [Errno 28] No space left on device: '/tmp/janitor_ug9j3km/jqp/.git/objects/80'