terminal 0.2 → 0.3.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4f11cc567d402255f58454144255b1f72881b70e
4
- data.tar.gz: df412b25640995f4ee16c04e01bf0de484771f25
3
+ metadata.gz: a4b4dd150fe6a6cfcad302893bdd602885b6dffd
4
+ data.tar.gz: f2caa188722eca09b14e8f404ff96b837b6b9652
5
5
  SHA512:
6
- metadata.gz: 81ea8c713c6b2d49f2bc8b6b0c7c1f82298cb435b25f58a166eedb5ef33a74d9b0171f146b168a0cc2778fb2945df88a0133485b97402f582e8964b998349919
7
- data.tar.gz: 2f68cdd314e1503a8227d986497fbf047fe437956dc9a799f1f2b6773d31b22feade0c8c5c06d68f5a8072a385953dc0c4d073465680fcf547d996baddb08a09
6
+ metadata.gz: c658ae729a6ef14725497173d11eb8169aaaa0f04387e8a84a32c0461447a805b6d08cbb5b71a1f6fd78292d841ee4b30f0f54f853fbd36aa83f6d4fa8929634
7
+ data.tar.gz: 35e07c07b55db481be848513ee9838d529e8dfc12e7b5b22d44da79078994284e9f039c4704e788dc965bd0a47e5de5ba012192a9c2298506f0dc7934b562401
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ -Ilib
@@ -1,33 +1,39 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- terminal (0.1.1)
4
+ terminal (0.2)
5
5
  escape_utils (~> 1.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
+ benchmark-ips (2.0.0)
10
11
  diff-lcs (1.2.5)
11
12
  escape_utils (1.0.1)
13
+ hirb (0.7.2)
14
+ method_profiler (2.0.1)
15
+ hirb (>= 0.6.0)
12
16
  rake (10.3.2)
13
17
  rspec (3.0.0)
14
18
  rspec-core (~> 3.0.0)
15
19
  rspec-expectations (~> 3.0.0)
16
20
  rspec-mocks (~> 3.0.0)
17
- rspec-core (3.0.2)
21
+ rspec-core (3.0.3)
18
22
  rspec-support (~> 3.0.0)
19
- rspec-expectations (3.0.2)
23
+ rspec-expectations (3.0.3)
20
24
  diff-lcs (>= 1.2.0, < 2.0)
21
25
  rspec-support (~> 3.0.0)
22
- rspec-mocks (3.0.2)
26
+ rspec-mocks (3.0.3)
23
27
  rspec-support (~> 3.0.0)
24
- rspec-support (3.0.2)
28
+ rspec-support (3.0.3)
25
29
 
26
30
  PLATFORMS
27
31
  ruby
28
32
 
29
33
  DEPENDENCIES
34
+ benchmark-ips
30
35
  bundler (~> 1.6)
36
+ method_profiler
31
37
  rake
32
38
  rspec
33
39
  terminal!
@@ -0,0 +1,256 @@
1
+ .term-fg30 { color: #666; } /* black (but we can't use black, so a diff color) */
2
+ .term-fg31 { color: #e10c02; } /* red */
3
+ .term-fg32 { color: #00bd02; } /* green */
4
+ .term-fg33 { color: #c6c502; } /* yellow */
5
+ .term-fg34 { color: #8db7e0; } /* blue */
6
+ .term-fg35 { color: #ceacde; } /* magenta */
7
+ .term-fg36 { color: #00cdd9; } /* cyan */
8
+ .term-fg90 { color: #838887; } /* grey */
9
+
10
+ /* high intense colors */
11
+ .term-fgi1 { color: #5ef765; }
12
+
13
+ /* background colors */
14
+ .term-bg42 { background: #99ff5f; }
15
+
16
+ /* xterm colors */
17
+ .term-fgx16 { color: #000000; }
18
+ .term-fgx17 { color: #00005f; }
19
+ .term-fgx18 { color: #000087; }
20
+ .term-fgx19 { color: #0000af; }
21
+ .term-fgx20 { color: #0000d7; }
22
+ .term-fgx21 { color: #0000ff; }
23
+ .term-fgx22 { color: #005f00; }
24
+ .term-fgx23 { color: #005f5f; }
25
+ .term-fgx24 { color: #005f87; }
26
+ .term-fgx25 { color: #005faf; }
27
+ .term-fgx26 { color: #005fd7; }
28
+ .term-fgx27 { color: #005fff; }
29
+ .term-fgx28 { color: #008700; }
30
+ .term-fgx29 { color: #00875f; }
31
+ .term-fgx30 { color: #008787; }
32
+ .term-fgx31 { color: #0087af; }
33
+ .term-fgx32 { color: #0087d7; }
34
+ .term-fgx33 { color: #0087ff; }
35
+ .term-fgx34 { color: #00af00; }
36
+ .term-fgx35 { color: #00af5f; }
37
+ .term-fgx36 { color: #00af87; }
38
+ .term-fgx37 { color: #00afaf; }
39
+ .term-fgx38 { color: #00afd7; }
40
+ .term-fgx39 { color: #00afff; }
41
+ .term-fgx40 { color: #00d700; }
42
+ .term-fgx41 { color: #00d75f; }
43
+ .term-fgx42 { color: #00d787; }
44
+ .term-fgx43 { color: #00d7af; }
45
+ .term-fgx44 { color: #00d7d7; }
46
+ .term-fgx45 { color: #00d7ff; }
47
+ .term-fgx46 { color: #00ff00; }
48
+ .term-fgx47 { color: #00ff5f; }
49
+ .term-fgx48 { color: #00ff87; }
50
+ .term-fgx49 { color: #00ffaf; }
51
+ .term-fgx50 { color: #00ffd7; }
52
+ .term-fgx51 { color: #00ffff; }
53
+ .term-fgx52 { color: #5f0000; }
54
+ .term-fgx53 { color: #5f005f; }
55
+ .term-fgx54 { color: #5f0087; }
56
+ .term-fgx55 { color: #5f00af; }
57
+ .term-fgx56 { color: #5f00d7; }
58
+ .term-fgx57 { color: #5f00ff; }
59
+ .term-fgx58 { color: #5f5f00; }
60
+ .term-fgx59 { color: #5f5f5f; }
61
+ .term-fgx60 { color: #5f5f87; }
62
+ .term-fgx61 { color: #5f5faf; }
63
+ .term-fgx62 { color: #5f5fd7; }
64
+ .term-fgx63 { color: #5f5fff; }
65
+ .term-fgx64 { color: #5f8700; }
66
+ .term-fgx65 { color: #5f875f; }
67
+ .term-fgx66 { color: #5f8787; }
68
+ .term-fgx67 { color: #5f87af; }
69
+ .term-fgx68 { color: #5f87d7; }
70
+ .term-fgx69 { color: #5f87ff; }
71
+ .term-fgx70 { color: #5faf00; }
72
+ .term-fgx71 { color: #5faf5f; }
73
+ .term-fgx72 { color: #5faf87; }
74
+ .term-fgx73 { color: #5fafaf; }
75
+ .term-fgx74 { color: #5fafd7; }
76
+ .term-fgx75 { color: #5fafff; }
77
+ .term-fgx76 { color: #5fd700; }
78
+ .term-fgx77 { color: #5fd75f; }
79
+ .term-fgx78 { color: #5fd787; }
80
+ .term-fgx79 { color: #5fd7af; }
81
+ .term-fgx80 { color: #5fd7d7; }
82
+ .term-fgx81 { color: #5fd7ff; }
83
+ .term-fgx82 { color: #5fff00; }
84
+ .term-fgx83 { color: #5fff5f; }
85
+ .term-fgx84 { color: #5fff87; }
86
+ .term-fgx85 { color: #5fffaf; }
87
+ .term-fgx86 { color: #5fffd7; }
88
+ .term-fgx87 { color: #5fffff; }
89
+ .term-fgx88 { color: #870000; }
90
+ .term-fgx89 { color: #87005f; }
91
+ .term-fgx90 { color: #870087; }
92
+ .term-fgx91 { color: #8700af; }
93
+ .term-fgx92 { color: #8700d7; }
94
+ .term-fgx93 { color: #8700ff; }
95
+ .term-fgx94 { color: #875f00; }
96
+ .term-fgx95 { color: #875f5f; }
97
+ .term-fgx96 { color: #875f87; }
98
+ .term-fgx97 { color: #875faf; }
99
+ .term-fgx98 { color: #875fd7; }
100
+ .term-fgx99 { color: #875fff; }
101
+ .term-fgx100 { color: #878700; }
102
+ .term-fgx101 { color: #87875f; }
103
+ .term-fgx102 { color: #878787; }
104
+ .term-fgx103 { color: #8787af; }
105
+ .term-fgx104 { color: #8787d7; }
106
+ .term-fgx105 { color: #8787ff; }
107
+ .term-fgx106 { color: #87af00; }
108
+ .term-fgx107 { color: #87af5f; }
109
+ .term-fgx108 { color: #87af87; }
110
+ .term-fgx109 { color: #87afaf; }
111
+ .term-fgx110 { color: #87afd7; }
112
+ .term-fgx111 { color: #87afff; }
113
+ .term-fgx112 { color: #87d700; }
114
+ .term-fgx113 { color: #87d75f; }
115
+ .term-fgx114 { color: #87d787; }
116
+ .term-fgx115 { color: #87d7af; }
117
+ .term-fgx116 { color: #87d7d7; }
118
+ .term-fgx117 { color: #87d7ff; }
119
+ .term-fgx118 { color: #87ff00; }
120
+ .term-fgx119 { color: #87ff5f; }
121
+ .term-fgx120 { color: #87ff87; }
122
+ .term-fgx121 { color: #87ffaf; }
123
+ .term-fgx122 { color: #87ffd7; }
124
+ .term-fgx123 { color: #87ffff; }
125
+ .term-fgx124 { color: #af0000; }
126
+ .term-fgx125 { color: #af005f; }
127
+ .term-fgx126 { color: #af0087; }
128
+ .term-fgx127 { color: #af00af; }
129
+ .term-fgx128 { color: #af00d7; }
130
+ .term-fgx129 { color: #af00ff; }
131
+ .term-fgx130 { color: #af5f00; }
132
+ .term-fgx131 { color: #af5f5f; }
133
+ .term-fgx132 { color: #af5f87; }
134
+ .term-fgx133 { color: #af5faf; }
135
+ .term-fgx134 { color: #af5fd7; }
136
+ .term-fgx135 { color: #af5fff; }
137
+ .term-fgx136 { color: #af8700; }
138
+ .term-fgx137 { color: #af875f; }
139
+ .term-fgx138 { color: #af8787; }
140
+ .term-fgx139 { color: #af87af; }
141
+ .term-fgx140 { color: #af87d7; }
142
+ .term-fgx141 { color: #af87ff; }
143
+ .term-fgx142 { color: #afaf00; }
144
+ .term-fgx143 { color: #afaf5f; }
145
+ .term-fgx144 { color: #afaf87; }
146
+ .term-fgx145 { color: #afafaf; }
147
+ .term-fgx146 { color: #afafd7; }
148
+ .term-fgx147 { color: #afafff; }
149
+ .term-fgx148 { color: #afd700; }
150
+ .term-fgx149 { color: #afd75f; }
151
+ .term-fgx150 { color: #afd787; }
152
+ .term-fgx151 { color: #afd7af; }
153
+ .term-fgx152 { color: #afd7d7; }
154
+ .term-fgx153 { color: #afd7ff; }
155
+ .term-fgx154 { color: #afff00; }
156
+ .term-fgx155 { color: #afff5f; }
157
+ .term-fgx156 { color: #afff87; }
158
+ .term-fgx157 { color: #afffaf; }
159
+ .term-fgx158 { color: #afffd7; }
160
+ .term-fgx159 { color: #afffff; }
161
+ .term-fgx160 { color: #d70000; }
162
+ .term-fgx161 { color: #d7005f; }
163
+ .term-fgx162 { color: #d70087; }
164
+ .term-fgx163 { color: #d700af; }
165
+ .term-fgx164 { color: #d700d7; }
166
+ .term-fgx165 { color: #d700ff; }
167
+ .term-fgx166 { color: #d75f00; }
168
+ .term-fgx167 { color: #d75f5f; }
169
+ .term-fgx168 { color: #d75f87; }
170
+ .term-fgx169 { color: #d75faf; }
171
+ .term-fgx170 { color: #d75fd7; }
172
+ .term-fgx171 { color: #d75fff; }
173
+ .term-fgx172 { color: #d78700; }
174
+ .term-fgx173 { color: #d7875f; }
175
+ .term-fgx174 { color: #d78787; }
176
+ .term-fgx175 { color: #d787af; }
177
+ .term-fgx176 { color: #d787d7; }
178
+ .term-fgx177 { color: #d787ff; }
179
+ .term-fgx178 { color: #d7af00; }
180
+ .term-fgx179 { color: #d7af5f; }
181
+ .term-fgx180 { color: #d7af87; }
182
+ .term-fgx181 { color: #d7afaf; }
183
+ .term-fgx182 { color: #d7afd7; }
184
+ .term-fgx183 { color: #d7afff; }
185
+ .term-fgx184 { color: #d7d700; }
186
+ .term-fgx185 { color: #d7d75f; }
187
+ .term-fgx186 { color: #d7d787; }
188
+ .term-fgx187 { color: #d7d7af; }
189
+ .term-fgx188 { color: #d7d7d7; }
190
+ .term-fgx189 { color: #d7d7ff; }
191
+ .term-fgx190 { color: #d7ff00; }
192
+ .term-fgx191 { color: #d7ff5f; }
193
+ .term-fgx192 { color: #d7ff87; }
194
+ .term-fgx193 { color: #d7ffaf; }
195
+ .term-fgx194 { color: #d7ffd7; }
196
+ .term-fgx195 { color: #d7ffff; }
197
+ .term-fgx196 { color: #ff0000; }
198
+ .term-fgx197 { color: #ff005f; }
199
+ .term-fgx198 { color: #ff0087; }
200
+ .term-fgx199 { color: #ff00af; }
201
+ .term-fgx200 { color: #ff00d7; }
202
+ .term-fgx201 { color: #ff00ff; }
203
+ .term-fgx202 { color: #ff5f00; }
204
+ .term-fgx203 { color: #ff5f5f; }
205
+ .term-fgx204 { color: #ff5f87; }
206
+ .term-fgx205 { color: #ff5faf; }
207
+ .term-fgx206 { color: #ff5fd7; }
208
+ .term-fgx207 { color: #ff5fff; }
209
+ .term-fgx208 { color: #ff8700; }
210
+ .term-fgx209 { color: #ff875f; }
211
+ .term-fgx210 { color: #ff8787; }
212
+ .term-fgx211 { color: #ff87af; }
213
+ .term-fgx212 { color: #ff87d7; }
214
+ .term-fgx213 { color: #ff87ff; }
215
+ .term-fgx214 { color: #ffaf00; }
216
+ .term-fgx215 { color: #ffaf5f; }
217
+ .term-fgx216 { color: #ffaf87; }
218
+ .term-fgx217 { color: #ffafaf; }
219
+ .term-fgx218 { color: #ffafd7; }
220
+ .term-fgx219 { color: #ffafff; }
221
+ .term-fgx220 { color: #ffd700; }
222
+ .term-fgx221 { color: #ffd75f; }
223
+ .term-fgx222 { color: #ffd787; }
224
+ .term-fgx223 { color: #ffd7af; }
225
+ .term-fgx224 { color: #ffd7d7; }
226
+ .term-fgx225 { color: #ffd7ff; }
227
+ .term-fgx226 { color: #ffff00; }
228
+ .term-fgx227 { color: #ffff5f; }
229
+ .term-fgx228 { color: #ffff87; }
230
+ .term-fgx229 { color: #ffffaf; }
231
+ .term-fgx230 { color: #ffffd7; }
232
+ .term-fgx231 { color: #ffffff; }
233
+ .term-fgx232 { color: #080808; }
234
+ .term-fgx233 { color: #121212; }
235
+ .term-fgx234 { color: #1c1c1c; }
236
+ .term-fgx235 { color: #262626; }
237
+ .term-fgx236 { color: #303030; }
238
+ .term-fgx237 { color: #3a3a3a; }
239
+ .term-fgx238 { color: #444444; }
240
+ .term-fgx239 { color: #4e4e4e; }
241
+ .term-fgx240 { color: #585858; }
242
+ .term-fgx241 { color: #626262; }
243
+ .term-fgx242 { color: #6c6c6c; }
244
+ .term-fgx243 { color: #767676; }
245
+ .term-fgx244 { color: #808080; }
246
+ .term-fgx245 { color: #8a8a8a; }
247
+ .term-fgx246 { color: #949494; }
248
+ .term-fgx247 { color: #9e9e9e; }
249
+ .term-fgx248 { color: #a8a8a8; }
250
+ .term-fgx249 { color: #b2b2b2; }
251
+ .term-fgx250 { color: #bcbcbc; }
252
+ .term-fgx251 { color: #c6c6c6; }
253
+ .term-fgx252 { color: #d0d0d0; }
254
+ .term-fgx253 { color: #dadada; }
255
+ .term-fgx254 { color: #e4e4e4; }
256
+ .term-fgx255 { color: #eeeeee; }
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # encoding: UTF-8
2
3
 
3
4
  $:.unshift(File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib'))
4
5
 
@@ -1,13 +1,12 @@
1
1
  require "terminal/version"
2
- require "terminal/node"
3
- require "terminal/color"
4
- require "terminal/reset"
2
+ require "terminal/screen"
5
3
  require "terminal/renderer"
6
4
  require "terminal/cli"
7
5
  require "terminal/preview"
6
+ require "terminal/engine" if defined?(Rails)
8
7
 
9
8
  module Terminal
10
9
  def self.render(output)
11
- Terminal::Renderer.new.render(output)
10
+ Terminal::Renderer.new(output).render
12
11
  end
13
12
  end
@@ -0,0 +1,4 @@
1
+ module Terminal
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -2,7 +2,30 @@ require 'erb'
2
2
 
3
3
  module Terminal
4
4
  class Preview
5
- class Binding < Struct.new(:raw, :rendered)
5
+ class Binding
6
+ def initialize(asset_path, raw, rendered)
7
+ @asset_path = asset_path
8
+ @raw = raw
9
+ @rendered = rendered
10
+ end
11
+
12
+ def asset_path(path)
13
+ File.join(@asset_path, path)
14
+ end
15
+
16
+ def raw
17
+ # Call out special escape characters to make debugging easier
18
+ @raw.
19
+ gsub("\n", "\\n\n").
20
+ gsub("\r", "\\r").
21
+ gsub("\b", "\\b").
22
+ gsub(/\e/, "\\\\e")
23
+ end
24
+
25
+ def rendered
26
+ @rendered
27
+ end
28
+
6
29
  def get_binding
7
30
  binding
8
31
  end
@@ -16,15 +39,23 @@ module Terminal
16
39
  def render
17
40
  template = File.read(template_path)
18
41
  renderer = ERB.new(template)
19
- binding = Binding.new(@raw, @rendered)
42
+ binding = Binding.new(assets_path, @raw, @rendered)
20
43
 
21
44
  renderer.result(binding.get_binding)
22
45
  end
23
46
 
24
47
  private
25
48
 
49
+ def root_path
50
+ File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
51
+ end
52
+
53
+ def assets_path
54
+ File.join(root_path, 'app/assets')
55
+ end
56
+
26
57
  def template_path
27
- File.join(File.expand_path(File.dirname(__FILE__)), 'templates/preview.html.erb')
58
+ File.join(root_path, 'lib/terminal/templates/preview.html.erb')
28
59
  end
29
60
  end
30
61
  end
@@ -2,52 +2,30 @@ require 'escape_utils'
2
2
 
3
3
  module Terminal
4
4
  class Renderer
5
- # If the string (which is a regex) matches, it'll split on that space, so we
6
- # end up with an array of special characters and normal characters, i.e:
7
- # [ '\n', '\r', 'a', 'b', '\e123m' ]
8
- SPLIT_BY_CHARACTERS = [
9
- # \n moves the cursor to a new line
10
- # \r moves the cursor to the begining of the line
11
- # \b moves the cursor back one
12
- '[\n\r\b]',
13
-
14
- # [K Erases from the current cursor position to the end of the current line.
15
- '\e\[0?K',
16
-
17
- # Clears tab at the current position
18
- '\e\[0?[Gg]',
19
-
20
- # [1K Erases from the current cursor position to the start of the current line.
21
- # [2K Erases the entire current line.
22
- '\e\[[1-2]K',
23
-
24
- # \e[?D move the cursor back ? many characters
25
- '\e\[[\d;]+D',
26
-
27
- # \e[0m reset color information
28
- # \e[?m use the ? color going forward
29
- '\e\[[\d;]+m',
5
+ ESCAPE_CONTROL_CHARACTERS = "qQmKGgKAaBbCcDd"
6
+ MEGABYTES = 1024 * 1024
30
7
 
31
- # Random escpae sequences
32
- '\e',
33
-
34
- # Every other character
35
- '.'
36
- ]
8
+ def initialize(output)
9
+ @output = output
10
+ @screen = Screen.new
11
+ end
37
12
 
38
- SPLIT_BY_CHARACTERS_REGEX = Regexp.new(SPLIT_BY_CHARACTERS.join("|"))
13
+ def render
14
+ return "" if @output.nil? || @output.strip.length == 0
39
15
 
40
- def render(output)
41
- return "" if output.nil? || output.strip.length == 0
16
+ # First duplicate the string, because we're going to be editing and chopping it
17
+ # up directly.
18
+ output = @output.dup
42
19
 
43
- # Limit the entire size of the output to 4 meg (4 * megabyte * kilabyte)
44
- max_total_size = 4 * 1024 * 1024
20
+ # Limit the entire size of the output to 4 meg
21
+ max_total_size = 4 * MEGABYTES
45
22
  if output.bytesize > max_total_size
46
23
  output = output.byteslice(0, max_total_size)
47
24
  output << "\n\nWarning: Terminal has chopped off the rest of the build as it's over the allowed 4 megabyte limit for logs."
48
25
  end
49
26
 
50
27
  # Limit each line to (x) chars
28
+ # TODO: Move this to the screen
51
29
  max_line_length = 50_000
52
30
  output = output.split("\n").map do |line|
53
31
  if line.length > max_line_length
@@ -58,154 +36,125 @@ module Terminal
58
36
  end
59
37
  end.join("\n")
60
38
 
61
- # Now do the terminal rendering
62
- output = emulate_terminal_rendering(sanitize(output))
39
+ # Force encoding on the output first
40
+ force_encoding!(output)
63
41
 
64
- # Replace empty lines with a non breaking space.
65
- output.gsub(/$^/, "&nbsp;")
66
- end
42
+ # Now do the render the output to the screen
43
+ render_to_screen(output)
67
44
 
68
- private
45
+ # Convert the screen to a string
46
+ output = convert_screen_to_string
69
47
 
70
- def emulate_terminal_rendering(string)
71
- # Splits the output into intersting parts.
72
- parts = string.scan(SPLIT_BY_CHARACTERS_REGEX)
73
-
74
- lines = []
75
-
76
- index = 0
77
- length = string.length
78
-
79
- line = []
80
- cursor = 0
81
-
82
- # Every time a color is found, we increment this
83
- # counter, every time it resets, we decrement.
84
- # We do this so we close all the open spans at the
85
- # end of the output.
86
- colors_opened = 0
87
-
88
- parts.each do |char|
89
- case char
90
- when "\n"
91
- # Starts writing from a new line
92
- lines << line
93
- line = []
94
- cursor = 0
95
- when "\r"
96
- # Returns the writing cursor back to the begining of the line
97
- cursor = 0
98
- when "\e[G", "\e[0G", "\e[g"
99
- # TODO: I have no idea how these characters are supposed to work,
100
- # but this seems to be produce nicer results that what currently
101
- # gets rendered.
102
- cursor = 0
103
- when "\e[K", "\e[0K"
104
- # erases everything after the cursor
105
- line = line.fill(" ", cursor..line.length)
106
- when "\e[1K"
107
- # erases everything before the cursor
108
- line = line.fill(" ", 0..cursor)
109
- when "\e[2K"
110
- # erase entire line
111
- line = Array.new(line.length, " ")
112
- when "\b"
113
- pointer = cursor-1
114
-
115
- # Seek backwards until something that isn't a color is reached. When we
116
- # reach it (probably a string) remove the last character of it.
117
- # Colors aren't affected by \b
118
- while pointer >= 0
119
- char_at_pointer = line[pointer]
120
-
121
- unless char_at_pointer.kind_of?(Terminal::Node)
122
- line[pointer] = char_at_pointer[0..-2]
123
- break
124
- end
125
-
126
- pointer -= 1
127
- end
128
- when /\e\[(\d+)D/
129
- backwards = $1.to_i
48
+ # Escape any HTML
49
+ output = escape_html(output)
130
50
 
131
- new_position = cursor - backwards
132
- new_position = 0 if new_position < 0
51
+ # Now convert the colors to HTML
52
+ convert_to_html(output)
53
+ end
133
54
 
134
- cursor = new_position
135
- when /\A\e\[(.*)m\z/
136
- color_code = $1.to_s
55
+ private
137
56
 
138
- # Determine what sort of color code it is.
139
- if color_code == "0"
140
- line[cursor] = Terminal::Reset.new(colors_opened)
57
+ def force_encoding!(string)
58
+ string.force_encoding('UTF-8')
141
59
 
142
- colors_opened = 0
143
- else
144
- line[cursor] = Terminal::Color.new(color_code)
60
+ if string.valid_encoding?
61
+ string
62
+ else
63
+ string.force_encoding('ASCII-8BIT').encode!('UTF-8', invalid: :replace, undef: :replace)
64
+ end
65
+ end
145
66
 
146
- colors_opened += 1
67
+ def render_to_screen(string)
68
+ # The when cases are ordered by most likely, the lest checks it has to go
69
+ # through before matching, the faster the render will be. Colors are
70
+ # usually most likey, so that's first.
71
+ split_by_escape_character(string).each do |char|
72
+ # Hackers way of not having to run a regex over every
73
+ # character.
74
+ if char.length == 1
75
+ case char
76
+ when "\n"
77
+ @screen.x = 0
78
+ @screen.y += 1
79
+ when "\r"
80
+ @screen.x = 0
81
+ when "\r"
82
+ @screen.x = 0
83
+ when "\b"
84
+ @screen.x -= 1
85
+ else
86
+ @screen << char
147
87
  end
148
-
149
- index += char.length
150
- cursor += 1
151
88
  else
152
- line[cursor] = char
153
- cursor += 1
89
+ handle_escape_code(char)
154
90
  end
155
-
156
- # The cursor can't go back furthur than 0, so if you \b at the begining
157
- # of a line, nothing happens.
158
- cursor = 0 if cursor < 0
159
-
160
- index += 1
161
- end
162
-
163
- # Be sure to reset any unclosed colors on the last line
164
- if colors_opened > 0
165
- line << Terminal::Reset.new(colors_opened)
166
91
  end
92
+ end
167
93
 
168
- # Add back in the last line if the end of the output
169
- # didn't end with a \n
170
- lines << line if line.any?
171
-
172
- # Join all the strings back together again
173
- lines = lines.map do |parts|
174
- completed_line = parts.map(&:to_s).join("")
175
- end.join("\n")
176
-
177
- # Now escape all the things
178
- lines = EscapeUtils.escape_html(lines)
94
+ def escape_html(string)
95
+ EscapeUtils.escape_html(string)
96
+ end
179
97
 
180
- matches = []
98
+ def convert_screen_to_string
99
+ @screen.to_s
100
+ end
181
101
 
182
- # Now we can easily gsub colors like a baws
183
- lines.gsub!(/\e\[([0-9;]+)m/) do |match|
184
- color_codes = $1.split(';')
102
+ def handle_escape_code(sequence)
103
+ # Escapes have the following: \e [ (instruction) (code)
104
+ parts = sequence.match(/\e\[(.*)([#{ESCAPE_CONTROL_CHARACTERS}])/)
105
+
106
+ instruction = parts[1].to_s
107
+ code = parts[2].to_s
108
+
109
+ case code
110
+ when ""
111
+ # no-op - an empty \e
112
+ when "m"
113
+ @screen.color(instruction)
114
+ when "G", "g"
115
+ @screen.x = 0
116
+ when "K", "k"
117
+ case instruction
118
+ when nil, "0"
119
+ # clear everything after the current x co-ordinate
120
+ @screen.clear(@screen.y, @screen.x, Screen::END_OF_LINE)
121
+ when "1"
122
+ # clear everything before the current x co-ordinate
123
+ @screen.clear(@screen.y, Screen::START_OF_LINE, @screen.x)
124
+ when "2"
125
+ @screen.clear(@screen.y, Screen::START_OF_LINE, Screen::END_OF_LINE)
126
+ end
127
+ when "A"
128
+ @screen.up(instruction)
129
+ when "B"
130
+ @screen.down(instruction)
131
+ when "C"
132
+ @screen.foward(instruction)
133
+ when "D"
134
+ @screen.backward(instruction)
135
+ end
136
+ end
185
137
 
186
- if color_codes == [ "0" ]
187
- "</span>"
138
+ def convert_to_html(string)
139
+ string = string.gsub(/\e\[([^;m]+);([^;m]+)?;m/) do |match|
140
+ if $2
141
+ %{<span class='term-#{$1} term-#{$2}'>}
188
142
  else
189
- classes = color_codes.map { |code| "c#{code}" }
190
-
191
- "<span class='#{classes.join(" ")}'>"
143
+ %{<span class='term-#{$1}'>}
192
144
  end
193
145
  end
194
146
 
195
- lines
147
+ string = string.gsub("\e[0m", "</span>")
148
+
149
+ # Replace empty lines with a non breaking space.
150
+ string.gsub(/$^/, "&nbsp;")
196
151
  end
197
152
 
198
- def sanitize(string)
199
- string = string.dup.force_encoding('UTF-8')
200
- if string.valid_encoding?
201
- string
202
- else
203
- string.
204
- force_encoding('ASCII-8BIT').
205
- encode!('UTF-8',
206
- invalid: :replace,
207
- undef: :replace)
208
- end
153
+ # Scan the string to create an array of interesting things, for example
154
+ # it would look like this:
155
+ # [ '\n', '\r', 'a', 'b', '\e123m' ]
156
+ def split_by_escape_character(string)
157
+ string.scan(/[\n\r\b]|\e\[[\d;]*[#{ESCAPE_CONTROL_CHARACTERS}]|./)
209
158
  end
210
159
  end
211
160
  end