© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2023
N. TolaramSoftware Development with Gohttps://doi.org/10.1007/978-1-4842-8731-6_15

15. ANSI and UI

Nanik Tolaram1  
(1)
Sydney, NSW, Australia
 

In this chapter, you will learn about writing command-line applications that have a user interface (UI). You will look at adding text styling, such as italic or bold text, text of a different color, a UI that uses a spinner, and more. This kind of user interface is possible by using ANSI escape codes, which contain code to do certain things in the terminal.

You will also look at an open source library that makes it easy to write a user interface that takes care of all the heavy lifting of writing the different ANSI escape codes to do fancy UI tricks. In this chapter, you will learn about the following:
  • ANSI escape codes for UI

  • An open source library to write different kinds of UIs

  • Styling text, such as italic and bold

Source Code

The source code for this chapter is available from the https://github.com/Apress/Software-Development-Go repository.

ANSI Escape Code

Terminal-based applications that provide a user interface are normally built using ANSI escape codes. The Wikipedia page at https://en.wikipedia.org/wiki/ANSI_escape_code provides a comprehensive explanation of the ANSI code:

ANSI escape sequences are a standard for in-band signaling to control cursor location, color, font styling, and other options on video text terminals and terminal emulators.

With the help of ANSI code, there are a proliferation of terminal-based applications that provide a rich terminal-based user interface.

Let’s experiment with a simple Bash script to print different background and foreground colors with text using ANSI code, as shown in the following example script:
for x in {0..8}; do for i in {30..37}; do
     for a in {40..47}; do echo -ne "e[$i;$a""m\e[$i;$a""me[37;40m "; done
     echo
done; done
echo ""
Figure 15-1 shows the output that you will see on your screen.

A screenshot of bash output.

Figure 15-1

Bash output

The following script prints out numbers in 256 different foreground colors using ANSI code. Figure 15-2 shows the output.
for i in {0..255}; do printf 'e[38;5;%dm%3d ' $i $i; (((i+3) % 18)) || printf 'e[0m '; done

A screenshot of the 256-color output. The numbers are arranged from 1 to 249.

Figure 15-2

256 color output

Both Bash scripts use ANSI code to select color. For Figure 15-2, the ANSI code is the following:
e[38;5;228m
Table 15-1 explains what the code means.
Table 15-1

Code Description

Code

Description

e

Escape character

38;5

ANSI code specifying foreground color

228

Color code for bright yellow

In this section, you learned about ANSI codes and how to use them to print text with different colors by writing Bash scripts. This lays the foundation for the next section where you are going to use ANSI code to write different kinds of terminal-based user interfaces inside Go.

ANSI-Based UI

In the previous section, you looked at ANSI codes and how to use them in Bash. In this section, you are going to use the ANSI codes inside a Go application. You will use ANSI code to set text color, style the text such as italic, and more.

Color Table

Open your terminal and run the code inside the chapter15/ansi folder.
go run main.go
Figure 15-3 shows the output.

A screenshot of the different color text output. The text Aa is the combination of foreground and background. The foreground numbers are from 30 to 97, while the background numbers are from 40 to 107.

Figure 15-3

Different color text output

The code prints the text Aa combined with the foreground and background color. The color values are set using escape code, which is obtained from the fg and bg variables, as shown in the following snippet:
  ...
  for _, fg := range fgColors {
     fmt.Printf("%2s ", fg)
  ...
        if len(fg) > 0 {
  ...
           fmt.Printf("x1b[%sm Aa x1b[0m", bg)
        }
     }
  }
Then different foreground and background numbers are specified in the fgColors and bgColors arrays, as follows:
var fgColors = []string{
  "", "30", "31", "32", "33", "34", "35", "36", "37",
  "90", "91", "92", "93", "94", "95", "96", "97",
}
var bgColors = []string{
  "", "40", "41", "42", "43", "44", "45", "46", "47",
  "100", "101", "102", "103", "104", "105", "106", "107",
}
The string printed to the screen looks like the following:
[31;40m Aa [0m
Here is the breakdown of what the code means:
  • [31;40m: ANSI escape code for black background with red color text

  • Aa: The Aa text

  • [0m: Reset

Figure 15-4 shows a table extracted from https://en.wikipedia.org/wiki/ANSI_escape_code showing the different foreground and background value combinations and the color each represents.

A screenshot with columns as FG, BG, Name, VGA, Windows XP Console, Windows power, visual studio code, windows 10, and terminal explains the foreground and background mapping.

Figure 15-4

Foreground and background mapping

In the next section, you will look at examples of how to use ANSI code to format text on a screen.

Styling Text

ANSI code is also available to style text such as italic, superscript, and more. Let’s take a look at the sample code inside the chapter15/textstyle folder, which will print output like Figure 15-5.

The text reads Italics text at the top and Underline text at the bottom.

Figure 15-5

Text styling using ANSI

The following code declares different constants containing ANSI code to format text with different styles:
package main
import "fmt"
const (
  Underline    = "x1b[4m"
  UnderlineOff = "x1b[24m"
  Italics      = "x1b[3m"
  ItalicsOff   = "x1b[23m"
)
...

In this section, you used different ANSI codes to format text in the console with different colors and formats. Going through the sample code, it is obvious that writing a command-line application that uses ANSI codes is quite laborious because you need to specify the different ANSI codes that are required in the application.

In the next section, you will look at some open source projects that take care of the different aspects of command-line user interface development to make writing code easier.

Open Source Library

In this section, you will look at two different open source libraries that are useful when writing command-line user interfaces. You will look at examples of how to use the libraries and look at how the internals of the libraries work.

Gookit

This library provides a simple API for applications to print text in different foreground and background colors. It also provides text styling such as italics, superscript, etc. The following is the link to the library project: https://github.com/gookit/color.

Run the sample code inside the chapter15/gookit folder as shown:
go run main.go
Figure 15-6 shows the output.

A screenshot of Gookit sample output. The text reads, Italing style, strikethrough style, Bold style. Warning colon warning message and Error colon error message.

Figure 15-6

Gookit sample output

The following code snippet shows the simple API call:
...
func main() {
  color.Warn = &color.Theme{"warning", color.Style{color.BgDefault, color.FgWhite}}
  ...
  color.Style{color.FgDefault, color.BgDefault, color.OpStrikethrough}.Println("Strikethrough style")
  color.Style{color.FgDefault, color.BgDefault, color.OpBold}.Println("Bold style")
  ...
}
Calling color.Style.Println prints the text that you want using the foreground and background colors specified. For example,
color.Style{color.FgDefault, color.BgDefault, color.OpStrikethrough}.Println("Strikethrough style")

prints the words Strikethrough style in the default foreground and background colors with the strikethrough text format.

The library uses a constant value to define the different colors it provides, as shown in the following code snippets, which can be found in the library under the file color_16.go:
const (
  FgBlack Color = iota + 30
  FgRed
  FgGreen
  FgYellow
  ...
)
const (
  FgDarkGray Color = iota + 90
  FgLightRed
  FgLightGreen
  ...
)
const (
  BgBlack Color = iota + 40
  BgRed
  ...
)
const (
  BgDarkGray Color = iota + 100
  BgLightRed
  ...
)
const (
  OpReset         Color = iota
  OpBold
  OpFuzzy
  OpItalic
  ...
)
The library uses the same ANSI codes to format the color and text styling as you saw in the previous section. The following code snippet is from the file color.go:
const (
  SettingTpl   = "x1b[%sm"
  FullColorTpl = "x1b[%sm%sx1b[0m"
)

Spinner

This library provides progress indicators for command-line applications. Progress indicators are mostly found in mobile applications or in graphical user interfaces like browsers. Progress indicators are used to indicate to the user that the application is processing the user's request. The library project’s home is https://github.com/briandowns/spinner. Open your terminal and run the code inside the chapter15/spinner folder as follows:
go run main.go
Figure 15-7 shows the output you will see when running the sample code. It prints the words Processing request with a red bar moving back and forth as the spinner.

The text on the left reads processing request. On the right are the dark-shaded blocks.

Figure 15-7

Spinner sample output

The library is straightforward to use, as shown in the following code snippet:
func main() {
  s := spinner.New(spinner.CharSets[35], 100*time.Millisecond)
  s.Color("red")
  s.Prefix = "Processing request : "
  s.Start()
  ...
  s.Stop()
}

Calling spinner.New initializes a new spinner with the type specified, in this case spinner.CharSets[35], and the time delay rendering the spinner, which is 100 milliseconds.

You can specify the different spinners, which can be found inside the library in the file character_sets.go.

A screenshot with a text reads character sets = map left bracket int right bracket left bracket right bracket string. Symbols are listed from rows 0 to 90.

The library renders the spinner to the screen by printing through each character byte in the array specified after a certain amount of delay. By doing this, it gives the illusion of animation when seeing it printed on the screen.

In Figure 15-8, you can see in the debugging window how the different characters that will form the spinner are stored inside the Spinner struct, allowing the library to render them individually. This way, when the library renders the different characters, it looks like an animation.

A screenshot of the spinner structure has different characters such as mu, delay, chars with 5 strings, and prefixes as processing requests.

Figure 15-8

Spinner struct containing spinner characters

The spinner.Start() function is the central piece of the logic inside the library that renders the spinner’s animation.
func (s *Spinner) Start() {
  ...
  go func() {
     for {
        for i := 0; i < len(s.chars); i++ {
           select {
           ...
           default:
              ...
              if runtime.GOOS == "windows" {
                 ...
              } else {
                 outColor = fmt.Sprintf(" %s%s%s", s.Prefix, s.color(s.chars[i]), s.Suffix)
              }
              ...
              fmt.Fprint(s.Writer, outColor)
              ...
              time.Sleep(delay)
           }
        }
     }
  }()
}

The function fires off a goroutine and endlessly loops the animation on the screen until the stop() function is called by the application.

The outColor variable contains the text to be printed. In this example, it’s Processing request, along with the ANSI code of the color specified in the sample code, so the content of the variable looks like Figure 15-9.

A screenshot of the output. S = left bracket star spinner dot spinner 0 x c 0 0 0 0 d 0 0 b 0 I = left bracket interest right bracket 0. outColor = left bracket string right bracket slash r.

Figure 15-9

outColor final output

Summary

In this chapter, you learn about ANSI codes and how they are useful for creating user interfaces in terminals. The available ANSI codes allow you to write text in color and apply different formatting to the text printed on the screen. You learned that the ANSI codes can be used inside a Bash script and inside Go code.

You explored deeper into the usage of ANSI codes by looking at different open source libraries that provide richer user interface functionality for terminal-based applications. The libraries you looked at provide text-based formatting such as color and styles and progress indicators.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.145.217