Skip to content

Commit

Permalink
Initial support for colored text
Browse files Browse the repository at this point in the history
- View contents are stored as cells (rune + colors) instead of runes.
- Uses the escape interpreter coded by @deweerdt in #39.
  • Loading branch information
jroimartin committed Apr 27, 2016
1 parent 4901994 commit b342d3a
Show file tree
Hide file tree
Showing 4 changed files with 323 additions and 36 deletions.
53 changes: 53 additions & 0 deletions _examples/colors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2014 The gocui Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
"fmt"
"log"

"github.com/jroimartin/gocui"
)

func main() {
g := gocui.NewGui()
if err := g.Init(); err != nil {
log.Panicln(err)
}
defer g.Close()

g.SetLayout(layout)

if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
log.Panicln(err)
}

if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
log.Panicln(err)
}
}

func layout(g *gocui.Gui) error {
maxX, maxY := g.Size()
if v, err := g.SetView("hello", maxX/2-6, maxY/2-5, maxX/2+6, maxY/2+5); err != nil {
if err != gocui.ErrUnknownView {
return err
}
fmt.Fprintln(v, "\x1b[0;30;47mHello world")
fmt.Fprintln(v, "\x1b[0;31mHello world")
fmt.Fprintln(v, "\x1b[0;32mHello world")
fmt.Fprintln(v, "\x1b[0;33mHello world")
fmt.Fprintln(v, "\x1b[0;34mHello world")
fmt.Fprintln(v, "\x1b[0;35mHello world")
fmt.Fprintln(v, "\x1b[0;36mHello world")
fmt.Fprintln(v, "\x1b[0;37mHello world")
fmt.Fprintln(v, "\x1b[0;30;41mHello world")
}
return nil
}

func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
174 changes: 174 additions & 0 deletions escape.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// Copyright 2014 The gocui Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gocui

import (
"errors"
"strconv"
)

type (
escapeState int
interpreterReturn int
)

const (
stateNone escapeState = iota
stateEscape
stateCSI
stateParams
)

type escapeInterpreter struct {
state escapeState
curch rune
csiParam []string
curFgColor, curBgColor Attribute
defaultFgColor, defaultBgColor Attribute
}

var (
errNotCSI = errors.New("Not a CSI escape sequence")
errCSINotANumber = errors.New("CSI escape sequence was expecting a number or a ;")
errCSIParseError = errors.New("CSI escape sequence parsing error")
errCSITooLong = errors.New("CSI escape sequence is too long")
)

// runes in case of error will output the non-parsed runes as a string.
func (ei *escapeInterpreter) runes() []rune {
switch ei.state {
case stateNone:
return []rune{0x1b}
case stateEscape:
return []rune{0x1b, ei.curch}
case stateCSI:
return []rune{0x1b, '[', ei.curch}
case stateParams:
ret := []rune{0x1b, '['}
for _, s := range ei.csiParam {
ret = append(ret, []rune(s)...)
ret = append(ret, ';')
}
return append(ret, ei.curch)
}
return nil
}

// newEscapeInterpreter returns an escapeInterpreter that will be able to parse
// terminal escape sequences.
func newEscapeInterpreter() *escapeInterpreter {
ei := &escapeInterpreter{
defaultFgColor: ColorWhite,
defaultBgColor: ColorBlack,
state: stateNone,
curFgColor: ColorWhite,
curBgColor: ColorBlack,
}
return ei
}

// reset sets the escapeInterpreter in inital state.
func (ei *escapeInterpreter) reset() {
ei.state = stateNone
ei.curFgColor = ei.defaultFgColor
ei.curBgColor = ei.defaultBgColor
ei.csiParam = nil
}

// paramToColor returns an attribute given a terminfo coloring.
func paramToColor(p int) Attribute {
switch p {
case 0:
return ColorBlack
case 1:
return ColorRed
case 2:
return ColorGreen
case 3:
return ColorYellow
case 4:
return ColorBlue
case 5:
return ColorMagenta
case 6:
return ColorCyan
case 7:
return ColorWhite
}
return ColorDefault
}

// parseOne parses a rune. If isEscape is true, it means that the rune is part
// of an scape sequence, and as such should not be printed verbatim. Otherwise,

This comment has been minimized.

Copy link
@deweerdt

deweerdt May 21, 2016

A minor typo coming from the original commit: s/space/escape/

This comment has been minimized.

Copy link
@jroimartin

jroimartin May 22, 2016

Author Owner

Fixed!

// it's not an escape sequence.
func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) {
// Sanity checks to make sure we're not parsing something totally bogus.
if len(ei.csiParam) > 20 {
return false, errCSITooLong
}
if len(ei.csiParam) > 0 && len(ei.csiParam[len(ei.csiParam)-1]) > 255 {
return false, errCSITooLong
}
ei.curch = ch
switch ei.state {
case stateNone:
if ch == 0x1b {
ei.state = stateEscape
return true, nil
}
return false, nil
case stateEscape:
if ch == '[' {
ei.state = stateCSI
return true, nil
}
return false, errNotCSI
case stateCSI:
if ch >= '0' && ch <= '9' {
ei.state = stateParams
ei.csiParam = append(ei.csiParam, string(ch))
return true, nil
}
return false, errCSINotANumber
case stateParams:
switch {
case ch >= '0' && ch <= '9':
ei.csiParam[len(ei.csiParam)-1] += string(ch)
return true, nil
case ch == ';':
ei.csiParam = append(ei.csiParam, "")
return true, nil
case ch == 'm':
if len(ei.csiParam) < 1 {
return false, errCSIParseError
}
for _, param := range ei.csiParam {
p, err := strconv.Atoi(param)
if err != nil {
return false, errCSIParseError
}
switch {
case p >= 30 && p <= 37:
ei.curFgColor = paramToColor(p - 30)
case p >= 40 && p <= 47:
ei.curBgColor = paramToColor(p - 40)
case p == 1:
ei.curFgColor |= AttrBold
case p == 4:
ei.curFgColor |= AttrUnderline
case p == 7:
ei.curFgColor |= AttrReverse
case p == 0 || p == 39:
ei.curFgColor = ei.defaultFgColor
ei.curBgColor = ei.defaultBgColor
}
}
ei.state = stateNone
ei.csiParam = nil
return true, nil
}
}
return false, nil
}
1 change: 0 additions & 1 deletion gui.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,6 @@ func (g *Gui) MainLoop() error {
return err
}
}
return nil
}

// consumeevents handles the remaining events in the events pool.
Expand Down
Loading

0 comments on commit b342d3a

Please sign in to comment.