Display JSON in indented, colored format in api output
This commit is contained in:
parent
1036666266
commit
62549465a0
3 changed files with 133 additions and 6 deletions
|
|
@ -6,11 +6,13 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cli/cli/pkg/cmdutil"
|
||||
"github.com/cli/cli/pkg/iostreams"
|
||||
"github.com/cli/cli/pkg/jsoncolor"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
@ -109,9 +111,18 @@ func apiRun(opts *ApiOptions) error {
|
|||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
_, err = io.Copy(opts.IO.Out, resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
isJSON, _ := regexp.MatchString(`[/+]json(;|$)`, resp.Header.Get("Content-Type"))
|
||||
|
||||
if isJSON && opts.IO.ColorEnabled() {
|
||||
err = jsoncolor.Write(opts.IO.Out, resp.Body, " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
_, err = io.Copy(opts.IO.Out, resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: detect GraphQL errors
|
||||
|
|
|
|||
|
|
@ -5,19 +5,36 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
type IOStreams struct {
|
||||
In io.ReadCloser
|
||||
Out io.Writer
|
||||
ErrOut io.Writer
|
||||
|
||||
colorEnabled bool
|
||||
}
|
||||
|
||||
func (s *IOStreams) ColorEnabled() bool {
|
||||
return s.colorEnabled
|
||||
}
|
||||
|
||||
func System() *IOStreams {
|
||||
var out io.Writer = os.Stdout
|
||||
var colorEnabled bool
|
||||
if os.Getenv("NO_COLOR") == "" && isTerminal(os.Stdout) {
|
||||
out = colorable.NewColorable(os.Stdout)
|
||||
colorEnabled = true
|
||||
}
|
||||
|
||||
return &IOStreams{
|
||||
In: os.Stdin,
|
||||
Out: os.Stdout,
|
||||
ErrOut: os.Stderr,
|
||||
In: os.Stdin,
|
||||
Out: out,
|
||||
ErrOut: os.Stderr,
|
||||
colorEnabled: colorEnabled,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -31,3 +48,7 @@ func Test() (*IOStreams, *bytes.Buffer, *bytes.Buffer, *bytes.Buffer) {
|
|||
ErrOut: errOut,
|
||||
}, in, out, errOut
|
||||
}
|
||||
|
||||
func isTerminal(f *os.File) bool {
|
||||
return isatty.IsTerminal(f.Fd()) || isatty.IsCygwinTerminal(f.Fd())
|
||||
}
|
||||
|
|
|
|||
95
pkg/jsoncolor/jsoncolor.go
Normal file
95
pkg/jsoncolor/jsoncolor.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
package jsoncolor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
colorDelim = "1;38" // bright white
|
||||
colorKey = "1;34" // bright blue
|
||||
colorNull = "1;30" // gray
|
||||
colorString = "32" // green
|
||||
colorBool = "33" // yellow
|
||||
)
|
||||
|
||||
func Write(w io.Writer, r io.Reader, indent string) error {
|
||||
dec := json.NewDecoder(r)
|
||||
dec.UseNumber()
|
||||
|
||||
var idx int
|
||||
var stack []json.Delim
|
||||
|
||||
for {
|
||||
t, err := dec.Token()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch tt := t.(type) {
|
||||
case json.Delim:
|
||||
switch tt {
|
||||
case '{', '[':
|
||||
stack = append(stack, tt)
|
||||
idx = 0
|
||||
fmt.Fprintf(w, "\x1b[%sm%s\x1b[m", colorDelim, tt)
|
||||
if dec.More() {
|
||||
fmt.Fprint(w, "\n", strings.Repeat(indent, len(stack)))
|
||||
}
|
||||
continue
|
||||
case '}', ']':
|
||||
stack = stack[:len(stack)-1]
|
||||
idx = 0
|
||||
fmt.Fprintf(w, "\x1b[%sm%s\x1b[m", colorDelim, tt)
|
||||
}
|
||||
default:
|
||||
b, err := json.Marshal(tt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isKey := len(stack) > 0 && stack[len(stack)-1] == '{' && idx%2 == 0
|
||||
idx++
|
||||
|
||||
var color string
|
||||
if isKey {
|
||||
color = colorKey
|
||||
} else if tt == nil {
|
||||
color = colorNull
|
||||
} else {
|
||||
switch t.(type) {
|
||||
case string:
|
||||
color = colorString
|
||||
case bool:
|
||||
color = colorBool
|
||||
}
|
||||
}
|
||||
|
||||
if color == "" {
|
||||
_, _ = w.Write(b)
|
||||
} else {
|
||||
fmt.Fprintf(w, "\x1b[%sm%s\x1b[m", color, b)
|
||||
}
|
||||
|
||||
if isKey {
|
||||
fmt.Fprintf(w, "\x1b[%sm:\x1b[m ", colorDelim)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if dec.More() {
|
||||
fmt.Fprintf(w, "\x1b[%sm,\x1b[m\n%s", colorDelim, strings.Repeat(indent, len(stack)))
|
||||
} else if len(stack) > 0 {
|
||||
fmt.Fprint(w, "\n", strings.Repeat(indent, len(stack)-1))
|
||||
} else {
|
||||
fmt.Fprint(w, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue