-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathansiescape.html
162 lines (148 loc) · 8.38 KB
/
ansiescape.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<!DOCTYPE html>
<html>
<head>
<link rel="canonical" href="https://hardmath123.github.io/ansiescape.html"/>
<link rel="stylesheet" type="text/css" href="/static/base.css"/>
<title>ANSIble - Comfortably Numbered</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<link rel="alternate" type="application/rss+xml" title="Comfortably Numbered" href="/feed.xml" />
<!--
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
MathJax.Hub.Config({
tex2jax: {inlineMath: [['$','$']]}
});
</script>
-->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" integrity="sha384-Um5gpz1odJg5Z4HAmzPtgZKdTBHZdw8S29IecapCSB31ligYPhHQZMIlWLYQGVoc" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js" integrity="sha384-YNHdsYkH6gMx9y3mRkmcJ2mFUjTd0qNQQvY9VYZgQd7DcN7env35GzlmFaZ23JGp" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js" integrity="sha384-vZTG03m+2yp6N6BNi5iM4rW4oIwk5DfcNdFfxkk9ZWpDriOkXX8voJBFrAO7MpVl" crossorigin="anonymous"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.body, {
// customised options
// • auto-render specific keys, e.g.:
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '$', right: '$', display: false},
{left: '\\begin{align}', right: '\\end{align}', display: true},
{left: '\\(', right: '\\)', display: false},
{left: '\\[', right: '\\]', display: true}
],
// • rendering keys, e.g.:
throwOnError : false
});
});
</script>
</head>
<body>
<header id="header">
<script src="static/main.js"></script>
<div>
<a href="/"><span class="left-word">Comfortably</span> <span class="right-word">Numbered</span></a>
</div>
</header>
<article id="postcontent" class="centered">
<section>
<h1>ANSIble</h1>
<center><em><p>Using ANSI escape codes for more interactive CLI interfaces</p>
</em></center>
<h4>Wednesday, August 6, 2014 · 3 min read</h4>
<p>Have you ever wondered how to print color to terminals? Or why on some CLI
interfaces, the arrow keys print out mysterious sequences like <code>^[[A</code>? Or why
sometimes the keyboard shortcut control-C becomes <code>^C</code>? Well, the answers to
these are all based on ANSI escape codes.</p>
<p>In UNIX programs, there’s the concept of ‘raw’ and ‘canonical’ mode for input.
‘Raw’ mode takes input character-by-character, while ‘canonical’ mode lets the
user write a line and hit enter to send the program the written line. Canonical
mode is generally more useful: it lets you go back and delete something if you
make a mistake. But applications that work on a per-keypress basis need to
bypass this. In node, you can enter raw mode with
<code>process.stdin.setRawMode(true);</code>.</p>
<p>CLI interactions also need the concept of control characters. When you type
control-C, you’re sending the program the byte <code>0x3</code>, which is… 3. But that’s
the ASCII <strong>control character</strong> which means ‘end of text’. The program takes
this, by convention, as a signal to stop executing (<code>KeyboardInterrupt</code> in
Python, for example). We print control characters with a caret (<code>^</code>), followed
by the letter we type on the keyboard. There are 32 of them, which <a href="http://en.wikipedia.org/wiki/ASCII#ASCII_control_code_chart">Wikipedia
lists</a>. You might
be familiar with using <code>^D</code> (‘end of transmission’) to quickly exit Python or
nodejs.</p>
<p>ANSI escape codes are a way to output to a terminal with more capability than
just raw text (there <em>was</em>, for comparison, a time when computer output was
printed, physically on paper, line by line). You can move the cursor back and
overwrite or clear text. You can also color text or make it blink obnoxiously.</p>
<p>ANSI escape codes start with the CSI: the <strong>Control Sequence Introducer</strong>. The
most common one is <code>\e[</code>. <code>\e</code> is the ASCII escape character <code>0x1b</code>. You can
type it with the control character <code>^[</code> (that is, <code>control-[</code>).</p>
<p>Next, they have a sequence of numerical arguments, separated by semicolons, and
finally, they have a letter which indicates the command. Once more, <a href="http://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes">Wikipedia
lists</a> these. As an
example, we can move the cursor to the top-left corner with <code>\e[1;1H</code> (H is the
command to move, and the arguments are 1 and 1).</p>
<p>Colors are just as easy. We use the <code>m</code> command, with an SGR (‘Set Graphics
Rendition’) parameter. 35 is the SGR parameter to set the text color to
magenta, while 42 makes the background green. So <code>\e[35;42m</code> would give us a
horrible magenta-on-green color scheme. (<code>\e[m</code> (no arguments) restores
everything).</p>
<p>This, by the way, explains the <code>^[[A</code> curiosity. When you press up-arrow, the
terminal sends the application the ANSI escape code to move the cursor up—the
command for this is <code>A</code>. So we get <code>\e[A</code>, and <code>\e</code> gets rendered as its
control code equivalent of <code>^[</code>. (You can, in fact, manually enter
control+[-[-A in Bash, and get the standard up-arrow behavior of pulling up the
last entered command.)</p>
<p>Some nodejs code to get you started—it’s a utility to interactively display the
bytes sent from a terminal when you press a key(combination).</p>
<pre><code class="lang-javascript">process.stdin.resume();
process.stdin.setRawMode(true);
process.stdin.on("data", function(buffer) {
if (buffer.length === 1 && buffer[0] === 3) {// detect ^C
process.stdout.write("\n"); // A trailing \n prevents
// the shell prompt from
// messing up.
process.exit(0); // die
} else {
process.stdout.write("\x1b[1J\x1b[1;1H");
// clear line and go to top
process.stdout.write(
require('util').inspect(buffer)
// Nice output format
);
}
});
</code></pre>
<p>This should give you the tools to write shinier, interactive utilities. But
keep in mind the UNIX philosophy—keep them simple, and make sure they cooperate
as filters (you should be able to pipe stuff in and out of your utility).</p>
<p>P.S. I wrote this post—including the code sample—in vim running in tmux. Please
pardon typos.</p>
</section>
<div id="comment-breaker">◊ ◊ ◊</div>
</article>
<footer id="footer">
<div>
<ul>
<li><a href="https://github.com/kach">
Github</a></li>
<li><a href="feed.xml">
Subscribe (RSS feed)</a></li>
<li><a href="https://twitter.com/hardmath123">
Twitter</a></li>
<li><a href="https://creativecommons.org/licenses/by-nc/3.0/deed.en_US">
CC BY-NC 3.0</a></li>
</ul>
</div>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-46120535-1', 'hardmath123.github.io');
ga('require', 'displayfeatures');
ga('send', 'pageview');
</script>
</footer>
</body>
</html>