Skip to content

Commit 516213e

Browse files
authored
Add console syntax coloring (#278)
* Use SQLCMDCOLORSCHEME variable to choose a color scheme. * Use `:list color` to see available schemes
1 parent c413c09 commit 516213e

File tree

12 files changed

+424
-40
lines changed

12 files changed

+424
-40
lines changed

NOTICE.md

Lines changed: 108 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,35 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
157157
SOFTWARE.
158158
159159
160+
```
161+
162+
## github.com/alecthomas/chroma/v2
163+
164+
* Name: github.com/alecthomas/chroma/v2
165+
* Version: v2.5.0
166+
* License: [MIT](https://github.com/alecthomas/chroma/blob/v2.5.0/COPYING)
167+
168+
```
169+
Copyright (C) 2017 Alec Thomas
170+
171+
Permission is hereby granted, free of charge, to any person obtaining a copy of
172+
this software and associated documentation files (the "Software"), to deal in
173+
the Software without restriction, including without limitation the rights to
174+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
175+
of the Software, and to permit persons to whom the Software is furnished to do
176+
so, subject to the following conditions:
177+
178+
The above copyright notice and this permission notice shall be included in all
179+
copies or substantial portions of the Software.
180+
181+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
182+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
183+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
184+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
185+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
186+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
187+
SOFTWARE.
188+
160189
```
161190

162191
## github.com/alecthomas/kong
@@ -249,6 +278,69 @@ SOFTWARE.
249278
250279
```
251280

281+
## github.com/cespare/xxhash/v2
282+
283+
* Name: github.com/cespare/xxhash/v2
284+
* Version: v2.1.1
285+
* License: [MIT](https://github.com/cespare/xxhash/blob/v2.1.1/LICENSE.txt)
286+
287+
```
288+
Copyright (c) 2016 Caleb Spare
289+
290+
MIT License
291+
292+
Permission is hereby granted, free of charge, to any person obtaining
293+
a copy of this software and associated documentation files (the
294+
"Software"), to deal in the Software without restriction, including
295+
without limitation the rights to use, copy, modify, merge, publish,
296+
distribute, sublicense, and/or sell copies of the Software, and to
297+
permit persons to whom the Software is furnished to do so, subject to
298+
the following conditions:
299+
300+
The above copyright notice and this permission notice shall be
301+
included in all copies or substantial portions of the Software.
302+
303+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
304+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
305+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
306+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
307+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
308+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
309+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
310+
311+
```
312+
313+
## github.com/dlclark/regexp2
314+
315+
* Name: github.com/dlclark/regexp2
316+
* Version: v1.4.0
317+
* License: [MIT](https://github.com/dlclark/regexp2/blob/v1.4.0/LICENSE)
318+
319+
```
320+
The MIT License (MIT)
321+
322+
Copyright (c) Doug Clark
323+
324+
Permission is hereby granted, free of charge, to any person obtaining a copy
325+
of this software and associated documentation files (the "Software"), to deal
326+
in the Software without restriction, including without limitation the rights
327+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
328+
copies of the Software, and to permit persons to whom the Software is
329+
furnished to do so, subject to the following conditions:
330+
331+
The above copyright notice and this permission notice shall be included in all
332+
copies or substantial portions of the Software.
333+
334+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
335+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
336+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
337+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
338+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
339+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
340+
SOFTWARE.
341+
342+
```
343+
252344
## github.com/docker/distribution
253345

254346
* Name: github.com/docker/distribution
@@ -1612,9 +1704,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16121704
16131705
```
16141706

1615-
## github.com/golang/protobuf/proto
1707+
## github.com/golang/protobuf
16161708

1617-
* Name: github.com/golang/protobuf/proto
1709+
* Name: github.com/golang/protobuf
16181710
* Version: v1.5.2
16191711
* License: [BSD-3-Clause](https://github.com/golang/protobuf/blob/v1.5.2/LICENSE)
16201712

@@ -3352,8 +3444,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33523444
## github.com/prometheus/client_golang/prometheus
33533445

33543446
* Name: github.com/prometheus/client_golang/prometheus
3355-
* Version: v1.1.0
3356-
* License: [Apache-2.0](https://github.com/prometheus/client_golang/blob/v1.1.0/LICENSE)
3447+
* Version: v1.11.1
3448+
* License: [Apache-2.0](https://github.com/prometheus/client_golang/blob/v1.11.1/LICENSE)
33573449

33583450
```
33593451
Apache License
@@ -3563,8 +3655,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35633655
## github.com/prometheus/client_model/go
35643656

35653657
* Name: github.com/prometheus/client_model/go
3566-
* Version: v0.0.0-20190812154241-14fe0d1b01d4
3567-
* License: [Apache-2.0](https://github.com/prometheus/client_model/blob/14fe0d1b01d4/LICENSE)
3658+
* Version: v0.2.0
3659+
* License: [Apache-2.0](https://github.com/prometheus/client_model/blob/v0.2.0/LICENSE)
35683660

35693661
```
35703662
Apache License
@@ -3774,8 +3866,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37743866
## github.com/prometheus/common
37753867

37763868
* Name: github.com/prometheus/common
3777-
* Version: v0.6.0
3778-
* License: [Apache-2.0](https://github.com/prometheus/common/blob/v0.6.0/LICENSE)
3869+
* Version: v0.26.0
3870+
* License: [Apache-2.0](https://github.com/prometheus/common/blob/v0.26.0/LICENSE)
37793871

37803872
```
37813873
Apache License
@@ -3985,8 +4077,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39854077
## github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg
39864078

39874079
* Name: github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg
3988-
* Version: v0.6.0
3989-
* License: [BSD-3-Clause](https://github.com/prometheus/common/blob/v0.6.0/internal\bitbucket.org\ww\goautoneg\README.txt)
4080+
* Version: v0.26.0
4081+
* License: [BSD-3-Clause](https://github.com/prometheus/common/blob/v0.26.0/internal\bitbucket.org\ww\goautoneg\README.txt)
39904082

39914083
```
39924084
PACKAGE
@@ -4657,8 +4749,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46574749
## golang.org/x/net
46584750

46594751
* Name: golang.org/x/net
4660-
* Version: v0.0.0-20221014081412-f15817d10f9b
4661-
* License: [BSD-3-Clause](https://cs.opensource.google/go/x/net/+/f15817d1:LICENSE)
4752+
* Version: v0.7.0
4753+
* License: [BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.7.0:LICENSE)
46624754

46634755
```
46644756
Copyright (c) 2009 The Go Authors. All rights reserved.
@@ -4694,8 +4786,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46944786
## golang.org/x/sys
46954787

46964788
* Name: golang.org/x/sys
4697-
* Version: v0.0.0-20220908164124-27713097b956
4698-
* License: [BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/27713097:LICENSE)
4789+
* Version: v0.5.0
4790+
* License: [BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.5.0:LICENSE)
46994791

47004792
```
47014793
Copyright (c) 2009 The Go Authors. All rights reserved.
@@ -4731,8 +4823,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47314823
## golang.org/x/text
47324824

47334825
* Name: golang.org/x/text
4734-
* Version: v0.4.0
4735-
* License: [BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.4.0:LICENSE)
4826+
* Version: v0.7.0
4827+
* License: [BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.7.0:LICENSE)
47364828

47374829
```
47384830
Copyright (c) 2009 The Go Authors. All rights reserved.

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ We will be implementing command line switches and behaviors over time. Several s
3030

3131
### Miscellaneous enhancements
3232

33+
- Console output coloring (see below)
3334
- `:Connect` now has an optional `-G` parameter to select one of the authentication methods for Azure SQL Database - `SqlAuthentication`, `ActiveDirectoryDefault`, `ActiveDirectoryIntegrated`, `ActiveDirectoryServicePrincipal`, `ActiveDirectoryManagedIdentity`, `ActiveDirectoryPassword`. If `-G` is not provided, either Integrated security or SQL Authentication will be used, dependent on the presence of a `-U` user name parameter.
3435
- The new `--driver-logging-level` command line parameter allows you to see traces from the `go-mssqldb` client driver. Use `64` to see all traces.
3536
- Sqlcmd can now print results using a vertical format. Use the new `-F vertical` command line option to set it. It's also controlled by the `SQLCMDFORMAT` scripting variable.
@@ -107,6 +108,17 @@ These environment variables can be set to configure some aspects of AAD auth and
107108

108109
`SQLCMDCLIENTID` - set this to the identifier of an application registered in your AAD which is authorized to authenticate to Azure SQL Database. Applies to `ActiveDirectoryInteractive` and `ActiveDirectoryPassword` methods.
109110

111+
## Console colors
112+
113+
Sqlcmd now supports syntax coloring the output of `:list` and the results of TSQL queries when output to the terminal.
114+
To enable coloring use the `SQLCMDCOLORSCHEME` variable, which can be set as an environment variable or by using `:setvar`. The valid values are the names of styles supported by the [chroma styles](https://github.com/alecthomas/chroma/tree/master/styles) project.
115+
116+
To see a list of available styles along with colored syntax samples, use this command in interactive mode:
117+
118+
```
119+
:list color
120+
```
121+
110122
### Packages
111123

112124
#### sqlcmd executable

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/microsoft/go-sqlcmd
33
go 1.18
44

55
require (
6+
github.com/alecthomas/chroma/v2 v2.5.0
67
github.com/alecthomas/kong v0.6.2-0.20220922001058-c62bf25854a0
78
github.com/billgraziano/dpapi v0.4.0
89
github.com/docker/distribution v2.8.1+incompatible
@@ -30,6 +31,7 @@ require (
3031
github.com/beorn7/perks v1.0.1 // indirect
3132
github.com/cespare/xxhash/v2 v2.1.1 // indirect
3233
github.com/davecgh/go-spew v1.1.1 // indirect
34+
github.com/dlclark/regexp2 v1.4.0 // indirect
3335
github.com/docker/go-metrics v0.0.1 // indirect
3436
github.com/docker/go-units v0.5.0 // indirect
3537
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect

go.sum

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,14 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
5050
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
5151
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
5252
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
53-
github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0=
53+
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
54+
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
55+
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
56+
github.com/alecthomas/chroma/v2 v2.5.0 h1:CQCdj1BiBV17sD4Bd32b/Bzuiq/EqoNTrnIhyQAZ+Rk=
57+
github.com/alecthomas/chroma/v2 v2.5.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw=
5458
github.com/alecthomas/kong v0.6.2-0.20220922001058-c62bf25854a0 h1:HQ3WlFsqBcr4qsiHtfA7UdFSrChglOcQa8q/tbXJFBI=
5559
github.com/alecthomas/kong v0.6.2-0.20220922001058-c62bf25854a0/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
56-
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
60+
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
5761
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
5862
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
5963
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -79,6 +83,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
7983
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8084
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
8185
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
86+
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
87+
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
8288
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
8389
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
8490
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=

internal/color/color.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package color
2+
3+
import (
4+
"io"
5+
"os"
6+
"sort"
7+
8+
"github.com/alecthomas/chroma/v2"
9+
"github.com/alecthomas/chroma/v2/formatters"
10+
"github.com/alecthomas/chroma/v2/lexers"
11+
"github.com/alecthomas/chroma/v2/quick"
12+
"github.com/alecthomas/chroma/v2/styles"
13+
)
14+
15+
// TextType defines a category of text that can be colorized
16+
type TextType int
17+
18+
const (
19+
// TextTypeNormal is non-categorized text that prints with the default color
20+
TextTypeNormal TextType = iota
21+
// TextTypeTSql is for transact-sql syntax
22+
TextTypeTSql
23+
// TextTypeHeader is for a table column header or cell label
24+
TextTypeHeader
25+
// TextTypeCell is for a cell value
26+
TextTypeCell
27+
// TextTypeSeparator is for characters that delimit columns and rows
28+
TextTypeSeparator
29+
// TextTypeError is for error messages
30+
TextTypeError
31+
// TextTypeWarning is for warning messages
32+
TextTypeWarning
33+
)
34+
35+
var typeMap map[TextType]chroma.TokenType = map[TextType]chroma.TokenType{
36+
TextTypeCell: chroma.StringOther,
37+
TextTypeHeader: chroma.GenericHeading,
38+
TextTypeSeparator: chroma.StringDelimiter,
39+
TextTypeError: chroma.GenericError,
40+
TextTypeWarning: chroma.GenericEmph,
41+
}
42+
43+
// Colorizer has methods to write colorized text to a stream
44+
type Colorizer interface {
45+
// Write prints s to w using the current color scheme. If w is not a terminal or if it is redirected, no color codes are printed
46+
Write(w io.Writer, s string, scheme string, t TextType) error
47+
// Styles returns the array of available style names
48+
Styles() []string
49+
}
50+
51+
type chromaColorizer struct {
52+
forceColor bool
53+
}
54+
55+
func New(forceColor bool) Colorizer {
56+
return &chromaColorizer{forceColor: forceColor}
57+
}
58+
59+
func (c *chromaColorizer) Write(w io.Writer, s string, scheme string, t TextType) (err error) {
60+
style := styles.Get(scheme)
61+
colorize := scheme != "" && style != nil
62+
// only colorize if w is a terminal and it's not redirected, or if forceColor is set
63+
if colorize && !c.forceColor {
64+
if f, ok := w.(*os.File); ok {
65+
if f == os.Stdout || f == os.Stderr {
66+
i, _ := f.Stat()
67+
colorize = (i.Mode() & os.ModeCharDevice) == os.ModeCharDevice
68+
} else {
69+
colorize = false
70+
}
71+
} else {
72+
colorize = false
73+
}
74+
}
75+
// We use this lexer simply for token iteration
76+
lexer := lexers.Get("plaintext")
77+
formatter := formatters.Get("terminal16m")
78+
if !colorize || lexer == nil || formatter == nil {
79+
t = TextTypeNormal
80+
}
81+
switch t {
82+
case TextTypeNormal:
83+
_, err = w.Write([]byte(s))
84+
case TextTypeTSql:
85+
if err = quick.Highlight(w, s, "transact-sql", "terminal16m", scheme); err != nil {
86+
_, err = w.Write([]byte(s))
87+
}
88+
default:
89+
tokens := chroma.Literator(chroma.Token{
90+
Type: typeMap[t], Value: s})
91+
if err = formatter.Format(w, style, tokens); err != nil {
92+
_, err = w.Write([]byte(s))
93+
}
94+
}
95+
return
96+
}
97+
98+
func (c *chromaColorizer) Styles() []string {
99+
s := make([]string, len(styles.Registry))
100+
i := 0
101+
for key := range styles.Registry {
102+
s[i] = key
103+
i++
104+
}
105+
sort.Strings(s)
106+
return s
107+
}

0 commit comments

Comments
 (0)