terminal 0.2 → 0.3.beta1

Sign up to get free protection for your applications and to get access to all the features.
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