mirror of
https://github.com/mikefarah/yq.git
synced 2026-03-10 15:54:26 +00:00
colorise output
This commit is contained in:
parent
5c166e3038
commit
87374edce5
@ -205,6 +205,7 @@ func configureEncoder() (yqlib.Encoder, error) {
|
||||
|
||||
yqlib.ConfiguredYamlPreferences.ColorsEnabled = colorsEnabled
|
||||
yqlib.ConfiguredJSONPreferences.ColorsEnabled = colorsEnabled
|
||||
yqlib.ConfiguredHclPreferences.ColorsEnabled = colorsEnabled
|
||||
|
||||
yqlib.ConfiguredYamlPreferences.PrintDocSeparators = !noDocSeparators
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
hclwrite "github.com/hashicorp/hcl/v2/hclwrite"
|
||||
@ -15,11 +16,12 @@ import (
|
||||
)
|
||||
|
||||
type hclEncoder struct {
|
||||
prefs HclPreferences
|
||||
}
|
||||
|
||||
// NewHclEncoder creates a new HCL encoder
|
||||
func NewHclEncoder() Encoder {
|
||||
return &hclEncoder{}
|
||||
func NewHclEncoder(prefs HclPreferences) Encoder {
|
||||
return &hclEncoder{prefs: prefs}
|
||||
}
|
||||
|
||||
func (he *hclEncoder) CanHandleAliases() bool {
|
||||
@ -55,6 +57,12 @@ func (he *hclEncoder) Encode(writer io.Writer, node *CandidateNode) error {
|
||||
// Inject comments back into the output
|
||||
finalOutput := he.injectComments(compactOutput, commentMap)
|
||||
|
||||
if he.prefs.ColorsEnabled {
|
||||
colorized := he.colorizeHcl(finalOutput)
|
||||
_, err := writer.Write(colorized)
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := writer.Write(finalOutput)
|
||||
return err
|
||||
}
|
||||
@ -151,6 +159,108 @@ func (he *hclEncoder) injectComments(output []byte, commentMap map[string]string
|
||||
return []byte(result)
|
||||
}
|
||||
|
||||
// colorizeHcl applies syntax highlighting to HCL output using fatih/color
|
||||
func (he *hclEncoder) colorizeHcl(input []byte) []byte {
|
||||
hcl := string(input)
|
||||
result := strings.Builder{}
|
||||
|
||||
// Create color functions for different token types
|
||||
commentColor := color.New(color.FgHiBlack).SprintFunc()
|
||||
stringColor := color.New(color.FgGreen).SprintFunc()
|
||||
numberColor := color.New(color.FgHiMagenta).SprintFunc()
|
||||
keyColor := color.New(color.FgCyan).SprintFunc()
|
||||
boolColor := color.New(color.FgHiMagenta).SprintFunc()
|
||||
|
||||
// Simple tokenization for HCL coloring
|
||||
i := 0
|
||||
for i < len(hcl) {
|
||||
ch := hcl[i]
|
||||
|
||||
// Comments - from # to end of line
|
||||
if ch == '#' {
|
||||
end := i
|
||||
for end < len(hcl) && hcl[end] != '\n' {
|
||||
end++
|
||||
}
|
||||
result.WriteString(commentColor(hcl[i:end]))
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// Strings - quoted text
|
||||
if ch == '"' || ch == '\'' {
|
||||
quote := ch
|
||||
end := i + 1
|
||||
for end < len(hcl) && hcl[end] != quote {
|
||||
if hcl[end] == '\\' {
|
||||
end++ // skip escaped char
|
||||
}
|
||||
end++
|
||||
}
|
||||
if end < len(hcl) {
|
||||
end++ // include closing quote
|
||||
}
|
||||
result.WriteString(stringColor(hcl[i:end]))
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// Numbers - sequences of digits, possibly with decimal point or minus
|
||||
if (ch >= '0' && ch <= '9') || (ch == '-' && i+1 < len(hcl) && hcl[i+1] >= '0' && hcl[i+1] <= '9') {
|
||||
end := i
|
||||
if ch == '-' {
|
||||
end++
|
||||
}
|
||||
for end < len(hcl) && ((hcl[end] >= '0' && hcl[end] <= '9') || hcl[end] == '.') {
|
||||
end++
|
||||
}
|
||||
result.WriteString(numberColor(hcl[i:end]))
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// Identifiers/keys - alphanumeric + underscore
|
||||
if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' {
|
||||
end := i
|
||||
for end < len(hcl) && ((hcl[end] >= 'a' && hcl[end] <= 'z') ||
|
||||
(hcl[end] >= 'A' && hcl[end] <= 'Z') ||
|
||||
(hcl[end] >= '0' && hcl[end] <= '9') ||
|
||||
hcl[end] == '_' || hcl[end] == '-') {
|
||||
end++
|
||||
}
|
||||
ident := hcl[i:end]
|
||||
|
||||
// Check if this is a keyword/reserved word
|
||||
switch ident {
|
||||
case "true", "false", "null":
|
||||
result.WriteString(boolColor(ident))
|
||||
default:
|
||||
// Check if followed by = (it's a key)
|
||||
j := end
|
||||
for j < len(hcl) && (hcl[j] == ' ' || hcl[j] == '\t') {
|
||||
j++
|
||||
}
|
||||
if j < len(hcl) && hcl[j] == '=' {
|
||||
result.WriteString(keyColor(ident))
|
||||
} else if j < len(hcl) && hcl[j] == '{' {
|
||||
// Block type
|
||||
result.WriteString(keyColor(ident))
|
||||
} else {
|
||||
result.WriteString(ident) // plain text for other identifiers
|
||||
}
|
||||
}
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// Everything else (whitespace, operators, brackets) - no color
|
||||
result.WriteByte(ch)
|
||||
i++
|
||||
}
|
||||
|
||||
return []byte(result.String())
|
||||
}
|
||||
|
||||
// Helper runes for unquoted identifiers
|
||||
func isHCLIdentifierStart(r rune) bool {
|
||||
return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || r == '_'
|
||||
|
||||
@ -68,7 +68,7 @@ var TomlFormat = &Format{"toml", []string{},
|
||||
}
|
||||
|
||||
var HclFormat = &Format{"hcl", []string{"h", "hcl"},
|
||||
func() Encoder { return NewHclEncoder() },
|
||||
func() Encoder { return NewHclEncoder(ConfiguredHclPreferences) },
|
||||
func() Decoder { return NewHclDecoder() },
|
||||
}
|
||||
|
||||
|
||||
15
pkg/yqlib/hcl.go
Normal file
15
pkg/yqlib/hcl.go
Normal file
@ -0,0 +1,15 @@
|
||||
package yqlib
|
||||
|
||||
type HclPreferences struct {
|
||||
ColorsEnabled bool
|
||||
}
|
||||
|
||||
func NewDefaultHclPreferences() HclPreferences {
|
||||
return HclPreferences{ColorsEnabled: false}
|
||||
}
|
||||
|
||||
func (p *HclPreferences) Copy() HclPreferences {
|
||||
return HclPreferences{ColorsEnabled: p.ColorsEnabled}
|
||||
}
|
||||
|
||||
var ConfiguredHclPreferences = NewDefaultHclPreferences()
|
||||
@ -281,7 +281,7 @@ func testHclScenario(t *testing.T, s formatScenario) {
|
||||
result := mustProcessFormatScenario(s, NewHclDecoder(), NewYamlEncoder(ConfiguredYamlPreferences))
|
||||
test.AssertResultWithContext(t, s.expected, result, s.description)
|
||||
case "roundtrip":
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewHclDecoder(), NewHclEncoder()), s.description)
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewHclDecoder(), NewHclEncoder(ConfiguredHclPreferences)), s.description)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,6 @@ func NewHclDecoder() Decoder {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewHclEncoder() Encoder {
|
||||
func NewHclEncoder(_ HclPreferences) Encoder {
|
||||
return nil
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user