Skip to content

Commit

Permalink
Initial work on terminal graphics
Browse files Browse the repository at this point in the history
  • Loading branch information
kenshaw committed Jan 11, 2024
1 parent f26fc8b commit 074448a
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 17 deletions.
92 changes: 86 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<p align="center">
<div align="center">
<img src="https://raw.githubusercontent.com/xo/usql-logo/master/usql.png" height="120">
</p>
</div>

<p align="center">
<div align="center">
<a href="#installing" title="Installing">Installing</a> |
<a href="#building" title="Building">Building</a> |
<a href="#database-support" title="Database Support">Database Support</a> |
<a href="#using" title="Using">Using</a> |
<a href="#features-and-compatibility" title="Features and Compatibility">Features and Compatibility</a> |
<a href="https://github.com/xo/usql/releases" title="Releases">Releases</a> |
<a href="#contributing" title="Contributing">Contributing</a>
</p>
</div>

<br/>

Expand All @@ -23,7 +23,8 @@ via a command-line inspired by PostgreSQL's `psql`. `usql` supports most of the
core `psql` features, such as [variables][variables], [backticks][backticks],
[backslash commands][commands] and has additional features that `psql` does
not, such as [multiple database support][databases], [copying between databases][copying],
[syntax highlighting][highlighting], and [context-based completion][completion].
[syntax highlighting][highlighting], [context-based completion][completion],
and [terminal graphics][termgraphics].

Database administrators and developers that would prefer to work with a tool
like `psql` with non-PostgreSQL databases, will find `usql` intuitive,
Expand Down Expand Up @@ -214,8 +215,9 @@ The following are the [Go SQL drivers][go-sql] that `usql` supports, the
associated database, scheme / build tag, and scheme aliases:

<!-- DRIVER DETAILS START -->

| Database | Scheme / Tag | Scheme Aliases | Driver Package / Notes |
|----------------------|-----------------|-------------------------------------------------|-----------------------------------------------------------------------------|
| -------------------- | --------------- | ----------------------------------------------- | --------------------------------------------------------------------------- |
| PostgreSQL | `postgres` | `pg`, `pgsql`, `postgresql` | [github.com/lib/pq][d-postgres] |
| MySQL | `mysql` | `my`, `maria`, `aurora`, `mariadb`, `percona` | [github.com/go-sql-driver/mysql][d-mysql] |
| Microsoft SQL Server | `sqlserver` | `ms`, `mssql`, `azuresql` | [github.com/microsoft/go-mssqldb][d-sqlserver] |
Expand Down Expand Up @@ -323,6 +325,7 @@ associated database, scheme / build tag, and scheme aliases:
[d-vertica]: https://github.com/vertica/vertica-sql-go
[d-voltdb]: https://github.com/VoltDB/voltdb-client-go
[d-ydb]: https://github.com/ydb-platform/ydb-go-sdk

<!-- DRIVER DETAILS END -->

[f-cgo]: #f-cgo "Requires CGO"
Expand Down Expand Up @@ -1195,6 +1198,72 @@ Connected with driver postgres (PostgreSQL 9.6.9)
pg:booktest@=>
```
#### Terminal Graphics
`usql` supports terminal graphics for [Kitty][kitty-graphics], [iTerm][iterm-graphics],
and [Sixel][sixel-graphics] enabled terminals using the [`github.com/kenshaw/rasterm` package][rasterm].
Terminal graphics are only available when using the interactive shell.
##### Detection and Support
`usql` will attempt to detect when terminal graphics support is available using
the `USQL_TERM_GRAPHICS`, `TERM_GRAPHICS` and other environment variables
unique to various terminals.
When support is available, the logo will be displayed at the start of an
interactive session:
<div style="padding-left: 20px;">
<img src="https://raw.githubusercontent.com/xo/usql-logo/master/usql-interactive.png" height="120">
</div>
##### Charts and Graphs
The [`\chart` meta command][chart-command] can be used to display a chart
directly in the terminal:
<div style="padding-left: 20px;">
<img src="https://raw.githubusercontent.com/xo/usql-logo/master/chart-example.png" height="120">
</div>
See [the section on the `\chart` meta command][chart-command] for details.
##### Enabling/Disabling Terminal Graphics
Terminal graphics can be forced enabled or disabled by setting the
`USQL_TERM_GRAPHICS` or the `TERM_GRAPHICS` environment variable:
```sh
# disable
$ USQL_TERM_GRAPHICS=none usql
# force iterm graphics
$ TERM_GRAPHICS=iterm usql
```
| Variable | Default | Values | Description |
| --------------- | ------- | ------------------------------------- | ------------------------------ |
| `TERM_GRAPHICS` | `` | ``, `kitty`, `iterm`, `sixel`, `none` | enables/disables term graphics |
##### Terminals with Graphics Support
The following terminals have been tested with `usql`:
- [WezTerm][wezterm] is a cross-platform terminal for Windows, macOS, Linux, and
many other platforms that supports [iTerm][iterm-graphics] graphics
- [iTerm2][iterm2] is a macOS terminal that supports [iTerm][iterm-graphics]
graphics
- [kitty][kitty] is a terminal for Linux, macOS, and various BSDs that supports
[Kitty][kitty-graphics] graphics
- [foot][foot] is a Wayland terminal for Linux (and other Wayland hosts) that
supports [Sixel][sixel-graphics] graphics
Additional terminals that support [Sixel][sixel-graphics] graphics are
catalogued on the [Are We Sixel Yet?][arewesixelyet] website.
## Additional Notes
The following are additional notes and miscellania related to `usql`:
Expand Down Expand Up @@ -1267,7 +1336,18 @@ contributing, see CONTRIBUTING.md](CONTRIBUTING.md).
[contributing]: #contributing "Contributing"
[copying]: #copying-between-databases "Copying Between Databases"
[highlighting]: #syntax-highlighting "Syntax Highlighting"
[termgraphics]: #terminal-graphics "Terminal Graphics"
[timefmt]: #time-formatting "Time Formatting"
[usqlpass]: #passwords "Passwords"
[usqlrc]: #runtime-configuration-rc-file "Runtime Configuration File"
[variables]: #variables-and-interpolation "Variable Interpolation"
[kitty-graphics]: https://sw.kovidgoyal.net/kitty/graphics-protocol.html
[iterm-graphics]: https://iterm2.com/documentation-images.html
[sixel-graphics]: https://saitoha.github.io/libsixel/
[rasterm]: https://github.com/kenshaw/rasterm
[wezterm]: https://wezfurlong.org/wezterm/
[iterm2]: https://iterm2.com
[foot]: https://codeberg.org/dnkl/foot
[kitty]: https://sw.kovidgoyal.net/kitty/
[arewesixelyet]: https://www.arewesixelyet.com
[chart-command]: #chart-command "\\chart meta command"
8 changes: 8 additions & 0 deletions env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"strings"
"unicode/utf8"

"github.com/kenshaw/rasterm"
"github.com/xo/dburl/passfile"
"github.com/xo/usql/text"
)
Expand Down Expand Up @@ -322,3 +323,10 @@ func Unquote(u *user.User, exec bool, v Vars) func(string, bool) (bool, string,
return true, res, nil
}
}

// TermGraphics returns the [rasterm.TermType] based on
func TermGraphics() rasterm.TermType {
var typ rasterm.TermType
_ = typ.UnmarshalText([]byte(Get("TERM_GRAPHICS")))
return typ
}
5 changes: 5 additions & 0 deletions env/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ var envVarNames = []varName{
"SYNTAX_HL_OVERRIDE_BG",
"enables overriding the background color of the chroma styles",
},
{
"TERM_GRAPHICS",
`use the specified terminal graphics`,
},
{
"SHELL",
"shell used by the \\! command",
Expand Down Expand Up @@ -263,6 +267,7 @@ func init() {
"SYNTAX_HL_STYLE": "monokai",
"SYNTAX_HL_OVERRIDE_BG": "true",
"SSLMODE": sslmode,
"TERM_GRAPHICS": "none",
}
// determine locale
locale := "en-US"
Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ require (
github.com/jackc/pgx/v5 v5.5.1
github.com/jeandeaual/go-locale v0.0.0-20220711133428-7de61946b173
github.com/jmrobles/h2go v0.5.0
github.com/kenshaw/rasterm v0.1.7
github.com/lib/pq v1.10.9
github.com/marcboeker/go-duckdb v1.5.6
github.com/mattn/go-adodb v0.0.1
Expand Down Expand Up @@ -227,6 +228,7 @@ require (
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mattn/go-sixel v0.0.5 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect
Expand Down Expand Up @@ -261,6 +263,7 @@ require (
github.com/segmentio/asm v1.2.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/soniakeys/quant v1.0.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/stretchr/objx v0.5.1 // indirect
github.com/stretchr/testify v1.8.4 // indirect
Expand Down Expand Up @@ -290,7 +293,7 @@ require (
golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/term v0.15.0 // indirect
golang.org/x/term v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.16.1 // indirect
Expand Down
10 changes: 8 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,8 @@ github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uia
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kenshaw/rasterm v0.1.7 h1:hrbIQGD5lfY6mlgzzmLuASaVn49ze6ZcUXwlLZ1XlZE=
github.com/kenshaw/rasterm v0.1.7/go.mod h1:xJTyDIvvaXfHUegYu2+X0oap7TysUO95VCv2eNykZBs=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
Expand Down Expand Up @@ -867,6 +869,8 @@ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sixel v0.0.5 h1:55w2FR5ncuhKhXrM5ly1eiqMQfZsnAHIpYNGZX03Cv8=
github.com/mattn/go-sixel v0.0.5/go.mod h1:h2Sss+DiUEHy0pUqcIB6PFXo5Cy8sTQEFr3a9/5ZLNw=
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
Expand Down Expand Up @@ -998,6 +1002,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/snowflakedb/gosnowflake v1.7.1 h1:c9JjyjjDlvxex9ud71TwKL+Wu54Vfx+39h4DAwbIdqU=
github.com/snowflakedb/gosnowflake v1.7.1/go.mod h1:JI3eRZL8CpimPek6CJO0aTbDQjDGOt7Rxv9A/ti4f5c=
github.com/soniakeys/quant v1.0.0 h1:N1um9ktjbkZVcywBVAAYpZYSHxEfJGzshHCxx/DaI0Y=
github.com/soniakeys/quant v1.0.0/go.mod h1:HI1k023QuVbD4H8i9YdfZP2munIHU4QpjsImz6Y6zds=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
Expand Down Expand Up @@ -1404,8 +1410,8 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
16 changes: 14 additions & 2 deletions handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"database/sql"
"errors"
"fmt"
"image/png"
"io"
"log"
"net/url"
Expand Down Expand Up @@ -226,8 +227,19 @@ func (h *Handler) Run() error {
stdout, stderr, iactive := h.l.Stdout(), h.l.Stderr(), h.l.Interactive()
// display welcome info
if iactive {
fmt.Fprintln(h.l.Stdout(), text.WelcomeDesc)
fmt.Fprintln(h.l.Stdout())
// graphics logo
if typ := env.TermGraphics(); typ.Available() {
logo, err := png.Decode(bytes.NewReader(text.LogoPng))
if err != nil {
return err
}
if err := typ.Encode(stdout, logo); err != nil {
return err
}
}
// welcome text
fmt.Fprintln(stdout, text.WelcomeDesc)
fmt.Fprintln(stdout)
}
var lastErr error
for {
Expand Down
20 changes: 19 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"os/user"
"strings"

"github.com/mattn/go-isatty"
"github.com/xo/usql/drivers"
"github.com/xo/usql/env"
"github.com/xo/usql/handler"
Expand Down Expand Up @@ -80,6 +81,23 @@ func run(args *Args, u *user.User) error {
if err != nil {
return err
}
// determine if interactive
interactive := isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stdin.Fd())
cygwin := isatty.IsCygwinTerminal(os.Stdout.Fd()) && isatty.IsCygwinTerminal(os.Stdin.Fd())
forceNonInteractive := len(args.CommandOrFiles) != 0
// enable term graphics
if !forceNonInteractive && interactive && !cygwin {
// NOTE: this is done here and not in the env.init() package, because
// NOTE: we need to determine if it is interactive first, otherwise it
// NOTE: could mess up the non-interactive output with control characters
var typ string
if s, _ := env.Getenv(text.CommandUpper()+"_TERM_GRAPHICS", "TERM_GRAPHICS"); s != "" {
typ = s
}
if err := env.Set("TERM_GRAPHICS", typ); err != nil {
return err
}
}
// handle variables
for _, v := range args.Variables {
if i := strings.Index(v, "="); i != -1 {
Expand Down Expand Up @@ -108,7 +126,7 @@ func run(args *Args, u *user.User) error {
}
}
// create input/output
l, err := rline.New(len(args.CommandOrFiles) != 0, args.Out, env.HistoryFile(u))
l, err := rline.New(interactive, cygwin, forceNonInteractive, args.Out, env.HistoryFile(u))
if err != nil {
return err
}
Expand Down
6 changes: 1 addition & 5 deletions rline/rline.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"os"

"github.com/gohxs/readline"
isatty "github.com/mattn/go-isatty"
)

var (
Expand Down Expand Up @@ -130,10 +129,7 @@ func (l *Rline) SetOutput(f func(string) string) {
}

// New creates a new readline input/output handler.
func New(forceNonInteractive bool, out, histfile string) (IO, error) {
// determine if interactive
interactive := isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stdin.Fd())
cygwin := isatty.IsCygwinTerminal(os.Stdout.Fd()) && isatty.IsCygwinTerminal(os.Stdin.Fd())
func New(interactive, cygwin, forceNonInteractive bool, out, histfile string) (IO, error) {
var closers []func() error
// configure stdin
var stdin io.ReadCloser
Expand Down
Binary file added text/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions text/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package text

import (
_ "embed"
"regexp"
"strings"
)
Expand Down Expand Up @@ -118,6 +119,11 @@ var CommandUpper = func() string {
return strings.ToUpper(Command())
}

// LogoPng is the embedded logo.
//
//go:embed logo.png
var LogoPng []byte

// UsageTemplate returns the usage template.
var UsageTemplate = func() string {
n := CommandLower()
Expand Down

0 comments on commit 074448a

Please sign in to comment.