Repository: TomWright/dasel
Pull request #155 (2021-08-14 12:34:41)

Add --colour --color flags to enable colourised output in selected command


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5747073..29204be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
-- Nothing yet.
+### Added
+
+- `--colour`,`--color` flag to enable colourised output in select command.
 
 ## [v1.18.0] - 2021-08-11
 
diff --git a/go.mod b/go.mod
index a98f456..efa61e6 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/tomwright/dasel
 go 1.16
 
 require (
+	github.com/alecthomas/chroma v0.9.2
 	github.com/clbanning/mxj/v2 v2.3.3-0.20201214204241-e937bdee5a3e
 	github.com/pelletier/go-toml v1.8.1
 	github.com/spf13/cobra v1.0.0
diff --git a/go.sum b/go.sum
index 297f70c..c723c68 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,15 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
+github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
+github.com/alecthomas/chroma v0.9.2 h1:yU1sE2+TZbLIQPMk30SolL2Hn53SR/Pv750f7qZ/XMs=
+github.com/alecthomas/chroma v0.9.2/go.mod h1:eMuEnpA18XbG/WhOWtCzJHS7WqEtDAI+HxdwoW0nVSk=
+github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
+github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
+github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
+github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=
+github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
@@ -16,10 +25,15 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
+github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
+github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@@ -54,6 +68,9 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@@ -63,6 +80,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
 github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
 github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
@@ -75,6 +94,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -87,8 +108,10 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6
 github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
@@ -115,6 +138,10 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY=
+golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/internal/command/select.go b/internal/command/select.go
index 1b4fd35..09ee8c0 100644
--- a/internal/command/select.go
+++ b/internal/command/select.go
@@ -23,6 +23,7 @@ type selectOptions struct {
 	DisplayLength       bool
 	MergeInputDocuments bool
 	FormatTemplate      string
+	Colourise           bool
 }
 
 func outputNodeLength(writer io.Writer, nodes ...*dasel.Node) error {
@@ -118,6 +119,9 @@ func runSelectCommand(opts selectOptions, cmd *cobra.Command) error {
 	if opts.Compact {
 		writeOptions = append(writeOptions, storage.PrettyPrintOption(false))
 	}
+	if opts.Colourise {
+		writeOptions = append(writeOptions, storage.ColouriseOption(true))
+	}
 
 	if opts.Multi {
 		return runSelectMultiCommand(cmd, rootNode, opts, writeParser, writeOptions)
@@ -166,7 +170,7 @@ func runSelectCommand(opts selectOptions, cmd *cobra.Command) error {
 
 func selectCommand() *cobra.Command {
 	var fileFlag, selectorFlag, parserFlag, readParserFlag, writeParserFlag, formatTemplateFlag string
-	var plainFlag, multiFlag, nullValueNotFoundFlag, compactFlag, lengthFlag, mergeInputDocumentsFlag bool
+	var plainFlag, multiFlag, nullValueNotFoundFlag, compactFlag, lengthFlag, mergeInputDocumentsFlag, colourFlag, colorFlag bool
 
 	cmd := &cobra.Command{
 		Use:   "select -f <file> -p <json,yaml> -s <selector>",
@@ -191,6 +195,7 @@ func selectCommand() *cobra.Command {
 				DisplayLength:       lengthFlag,
 				MergeInputDocuments: mergeInputDocumentsFlag,
 				FormatTemplate:      formatTemplateFlag,
+				Colourise:           colourFlag || colorFlag,
 			}, cmd)
 		},
 	}
@@ -207,6 +212,8 @@ func selectCommand() *cobra.Command {
 	cmd.Flags().BoolVar(&mergeInputDocumentsFlag, "merge-input-documents", false, "Merge multiple input documents into an array.")
 	cmd.Flags().BoolVarP(&compactFlag, "compact", "c", false, "Compact the output by removing all pretty-printing where possible.")
 	cmd.Flags().StringVar(&formatTemplateFlag, "format", "", "Formatting template to use when writing results.")
+	cmd.Flags().BoolVar(&colourFlag, "colour", false, "Print colourised output.")
+	cmd.Flags().BoolVar(&colorFlag, "color", false, "Alias of --colour.")
 
 	_ = cmd.MarkFlagFilename("file")
 
diff --git a/internal/storage/colourise.go b/internal/storage/colourise.go
new file mode 100644
index 0000000..dd0b7a4
--- /dev/null
+++ b/internal/storage/colourise.go
@@ -0,0 +1,25 @@
+package storage
+
+import (
+	"bytes"
+	"github.com/alecthomas/chroma/quick"
+)
+
+// ColouriseStyle is the style used when colourising output.
+const ColouriseStyle = "solarized-dark256"
+
+// ColouriseFormatter is the formatter used when colourising output.
+const ColouriseFormatter = "terminal"
+
+// ColouriseBuffer colourises the given buffer in-place.
+func ColouriseBuffer(content *bytes.Buffer, lexer string) error {
+	contentString := content.String()
+	content.Reset()
+	return quick.Highlight(content, contentString, lexer, ColouriseFormatter, ColouriseStyle)
+}
+
+// Colourise colourises the given string.
+func Colourise(content string, lexer string) (*bytes.Buffer, error) {
+	buf := new(bytes.Buffer)
+	return buf, quick.Highlight(buf, content, lexer, ColouriseFormatter, ColouriseStyle)
+}
diff --git a/internal/storage/json.go b/internal/storage/json.go
index abedfea..903e1e8 100644
--- a/internal/storage/json.go
+++ b/internal/storage/json.go
@@ -50,6 +50,7 @@ func (p *JSONParser) ToBytes(value interface{}, options ...ReadWriteOption) ([]b
 
 	indent := "  "
 	prettyPrint := true
+	colourise := false
 
 	for _, o := range options {
 		switch o.Key {
@@ -61,6 +62,10 @@ func (p *JSONParser) ToBytes(value interface{}, options ...ReadWriteOption) ([]b
 			if value, ok := o.Value.(bool); ok {
 				prettyPrint = value
 			}
+		case OptionColourise:
+			if value, ok := o.Value.(bool); ok {
+				colourise = value
+			}
 		}
 	}
 
@@ -85,5 +90,12 @@ func (p *JSONParser) ToBytes(value interface{}, options ...ReadWriteOption) ([]b
 			return nil, fmt.Errorf("could not encode default document type: %w", err)
 		}
 	}
+
+	if colourise {
+		if err := ColouriseBuffer(buffer, "json"); err != nil {
+			return nil, fmt.Errorf("could not colourise output: %w", err)
+		}
+	}
+
 	return buffer.Bytes(), nil
 }
diff --git a/internal/storage/option.go b/internal/storage/option.go
index 0fb4c6e..65b6285 100644
--- a/internal/storage/option.go
+++ b/internal/storage/option.go
@@ -16,6 +16,14 @@ func PrettyPrintOption(enabled bool) ReadWriteOption {
 	}
 }
 
+// ColouriseOption returns an option that enables or disables colourised output.
+func ColouriseOption(enabled bool) ReadWriteOption {
+	return ReadWriteOption{
+		Key:   OptionColourise,
+		Value: enabled,
+	}
+}
+
 // OptionKey is a defined type for keys within a ReadWriteOption.
 type OptionKey string
 
@@ -24,6 +32,8 @@ const (
 	OptionIndent OptionKey = "indent"
 	// OptionPrettyPrint is the key used with PrettyPrintOption.
 	OptionPrettyPrint OptionKey = "prettyPrint"
+	// OptionColourise is the key used with ColouriseOption.
+	OptionColourise OptionKey = "colourise"
 )
 
 // ReadWriteOption is an option to be used when writing.
diff --git a/internal/storage/toml.go b/internal/storage/toml.go
index a60a0f8..6f86ee5 100644
--- a/internal/storage/toml.go
+++ b/internal/storage/toml.go
@@ -32,12 +32,18 @@ func (p *TOMLParser) ToBytes(value interface{}, options ...ReadWriteOption) ([]b
 
 	enc := toml.NewEncoder(buf)
 
+	colourise := false
+
 	for _, o := range options {
 		switch o.Key {
 		case OptionIndent:
 			if indent, ok := o.Value.(string); ok {
 				enc.Indentation(indent)
 			}
+		case OptionColourise:
+			if value, ok := o.Value.(bool); ok {
+				colourise = value
+			}
 		}
 	}
 
@@ -70,5 +76,11 @@ func (p *TOMLParser) ToBytes(value interface{}, options ...ReadWriteOption) ([]b
 		}
 	}
 
+	if colourise {
+		if err := ColouriseBuffer(buf, "toml"); err != nil {
+			return nil, fmt.Errorf("could not colourise output: %w", err)
+		}
+	}
+
 	return buf.Bytes(), nil
 }
diff --git a/internal/storage/xml.go b/internal/storage/xml.go
index af69a7c..dced28a 100644
--- a/internal/storage/xml.go
+++ b/internal/storage/xml.go
@@ -40,6 +40,17 @@ func (p *XMLParser) FromBytes(byteData []byte) (interface{}, error) {
 func (p *XMLParser) ToBytes(value interface{}, options ...ReadWriteOption) ([]byte, error) {
 	buf := new(bytes.Buffer)
 
+	colourise := false
+
+	for _, o := range options {
+		switch o.Key {
+		case OptionColourise:
+			if value, ok := o.Value.(bool); ok {
+				colourise = value
+			}
+		}
+	}
+
 	writeMap := func(val interface{}) error {
 		if m, ok := val.(map[string]interface{}); ok {
 			mv := mxj.New()
@@ -75,5 +86,11 @@ func (p *XMLParser) ToBytes(value interface{}, options ...ReadWriteOption) ([]by
 		}
 	}
 
+	if colourise {
+		if err := ColouriseBuffer(buf, "xml"); err != nil {
+			return nil, fmt.Errorf("could not colourise output: %w", err)
+		}
+	}
+
 	return buf.Bytes(), nil
 }
diff --git a/internal/storage/yaml.go b/internal/storage/yaml.go
index 508dec4..b87ccbc 100644
--- a/internal/storage/yaml.go
+++ b/internal/storage/yaml.go
@@ -81,6 +81,17 @@ func (p *YAMLParser) ToBytes(value interface{}, options ...ReadWriteOption) ([]b
 	encoder := yaml.NewEncoder(buffer)
 	defer encoder.Close()
 
+	colourise := false
+
+	for _, o := range options {
+		switch o.Key {
+		case OptionColourise:
+			if value, ok := o.Value.(bool); ok {
+				colourise = value
+			}
+		}
+	}
+
 	switch v := value.(type) {
 	case SingleDocument:
 		if err := encoder.Encode(v.Document()); err != nil {
@@ -97,5 +108,12 @@ func (p *YAMLParser) ToBytes(value interface{}, options ...ReadWriteOption) ([]b
 			return nil, fmt.Errorf("could not encode default document type: %w", err)
 		}
 	}
+
+	if colourise {
+		if err := ColouriseBuffer(buffer, "yaml"); err != nil {
+			return nil, fmt.Errorf("could not colourise output: %w", err)
+		}
+	}
+
 	return buffer.Bytes(), nil
 }


Test patch:
diff --git a/internal/storage/json_test.go b/internal/storage/json_test.go
index 9f1f3fa..e8b6841 100644
--- a/internal/storage/json_test.go
+++ b/internal/storage/json_test.go
@@ -109,6 +109,22 @@ func TestJSONParser_ToBytes(t *testing.T) {
 		}
 	})
 
+	t.Run("ValidSingleColourise", func(t *testing.T) {
+		got, err := (&storage.JSONParser{}).ToBytes(&storage.BasicSingleDocument{Value: jsonMap}, storage.ColouriseOption(true))
+		if err != nil {
+			t.Errorf("unexpected error: %s", err)
+			return
+		}
+		expBuf, _ := storage.Colourise(`{
+  "name": "Tom"
+}
+`, "json")
+		exp := expBuf.Bytes()
+		if !reflect.DeepEqual(exp, got) {
+			t.Errorf("expected %v, got %v", exp, got)
+		}
+	})
+
 	t.Run("ValidSingleCustomIndent", func(t *testing.T) {
 		res, err := (&storage.JSONParser{}).ToBytes(&storage.BasicSingleDocument{Value: jsonMap}, storage.IndentOption("   "))
 		if err != nil {
diff --git a/internal/storage/toml_test.go b/internal/storage/toml_test.go
index 5a6c675..545e6f3 100644
--- a/internal/storage/toml_test.go
+++ b/internal/storage/toml_test.go
@@ -60,6 +60,19 @@ func TestTOMLParser_ToBytes(t *testing.T) {
 			t.Errorf("expected:\n%s\ngot:\n%s", tomlBytes, got)
 		}
 	})
+	t.Run("SingleDocumentColourise", func(t *testing.T) {
+		got, err := (&storage.TOMLParser{}).ToBytes(&storage.BasicSingleDocument{Value: tomlMap}, storage.ColouriseOption(true))
+		if err != nil {
+			t.Errorf("unexpected error: %s", err)
+			return
+		}
+
+		expBuf, _ := storage.Colourise(string(tomlBytes), "toml")
+		exp := expBuf.Bytes()
+		if !reflect.DeepEqual(exp, got) {
+			t.Errorf("expected %v, got %v", exp, got)
+		}
+	})
 	t.Run("SingleDocumentCustomIndent", func(t *testing.T) {
 		res, err := (&storage.TOMLParser{}).ToBytes(&storage.BasicSingleDocument{Value: tomlMap}, storage.IndentOption("   "))
 		if err != nil {
diff --git a/internal/storage/xml_test.go b/internal/storage/xml_test.go
index e60b3d8..3505de0 100644
--- a/internal/storage/xml_test.go
+++ b/internal/storage/xml_test.go
@@ -71,6 +71,19 @@ func TestXMLParser_ToBytes_SingleDocument(t *testing.T) {
 		t.Errorf("expected %v, got %v", string(xmlBytes), string(got))
 	}
 }
+func TestXMLParser_ToBytes_SingleDocument_Colourise(t *testing.T) {
+	got, err := (&storage.XMLParser{}).ToBytes(&storage.BasicSingleDocument{Value: xmlMap}, storage.ColouriseOption(true))
+	if err != nil {
+		t.Errorf("unexpected error: %s", err)
+		return
+	}
+
+	expBuf, _ := storage.Colourise(string(xmlBytes), "xml")
+	exp := expBuf.Bytes()
+	if !reflect.DeepEqual(exp, got) {
+		t.Errorf("expected %v, got %v", exp, got)
+	}
+}
 func TestXMLParser_ToBytes_MultiDocument(t *testing.T) {
 	got, err := (&storage.XMLParser{}).ToBytes(&storage.BasicMultiDocument{Values: []interface{}{xmlMap, xmlMap}})
 	if err != nil {
diff --git a/internal/storage/yaml_test.go b/internal/storage/yaml_test.go
index 9df82a1..c3c0850 100644
--- a/internal/storage/yaml_test.go
+++ b/internal/storage/yaml_test.go
@@ -97,6 +97,18 @@ func TestYAMLParser_ToBytes(t *testing.T) {
 			t.Errorf("expected %s, got %s", yamlBytes, got)
 		}
 	})
+	t.Run("ValidSingleColourise", func(t *testing.T) {
+		got, err := (&storage.YAMLParser{}).ToBytes(&storage.BasicSingleDocument{Value: yamlMap}, storage.ColouriseOption(true))
+		if err != nil {
+			t.Errorf("unexpected error: %s", err)
+			return
+		}
+		expBuf, _ := storage.Colourise(string(yamlBytes), "yaml")
+		exp := expBuf.Bytes()
+		if !reflect.DeepEqual(exp, got) {
+			t.Errorf("expected %v, got %v", exp, got)
+		}
+	})
 	t.Run("ValidMulti", func(t *testing.T) {
 		got, err := (&storage.YAMLParser{}).ToBytes(&storage.BasicMultiDocument{Values: yamlMapMulti})
 		if err != nil {