opal-irb 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +3 -0
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +14 -0
  5. data/Gemfile.lock +113 -0
  6. data/Guardfile +5 -0
  7. data/LICENSE +21 -0
  8. data/README.md +175 -0
  9. data/Rakefile +65 -0
  10. data/Roadmap.org +17 -0
  11. data/app/assets/stylesheets/opal-irb/jqconsole.css +263 -0
  12. data/compiled/app-embeddable.js +39765 -0
  13. data/compiled/app-jqconsole.js +39767 -0
  14. data/compiled/application.js +27399 -0
  15. data/css/ansi.css +172 -0
  16. data/css/opal_irb_jqconsole.css +79 -0
  17. data/css/show-hint.css +38 -0
  18. data/doc/presentations/opal_irb_overview.html +678 -0
  19. data/doc/presentations/opal_irb_overview.org +448 -0
  20. data/examples/app-embeddable.rb +8 -0
  21. data/examples/app-jqconsole.rb +10 -0
  22. data/examples/application.rb +8 -0
  23. data/index-embeddable.html +29 -0
  24. data/index-homebrew.html +115 -0
  25. data/index-jq.html +80 -0
  26. data/js/anyword-hint.js +44 -0
  27. data/js/jqconsole.js +1583 -0
  28. data/js/nodeutil.js +546 -0
  29. data/js/ruby.js +285 -0
  30. data/js/show-hint.js +383 -0
  31. data/lib/opal-irb/rails_engine.rb +3 -0
  32. data/lib/opal-irb/version.rb +3 -0
  33. data/lib/opal-irb-rails.rb +2 -0
  34. data/lib/opal-irb.rb +44 -0
  35. data/opal/object_extensions.rb +20 -0
  36. data/opal/opal_irb/completion_engine.rb +202 -0
  37. data/opal/opal_irb/completion_formatter.rb +49 -0
  38. data/opal/opal_irb/completion_results.rb +88 -0
  39. data/opal/opal_irb.rb +88 -0
  40. data/opal/opal_irb_homebrew_console.rb +398 -0
  41. data/opal/opal_irb_jqconsole.rb +517 -0
  42. data/opal/opal_irb_jqconsole_css.rb +259 -0
  43. data/opal/opal_irb_log_redirector.rb +32 -0
  44. data/opal/opal_phantomjs.rb +49 -0
  45. data/opal-irb.gemspec +20 -0
  46. data/spec/code_link_handler_spec.rb +30 -0
  47. data/spec/jquery.js +5 -0
  48. data/spec/object_extensions_spec.rb +32 -0
  49. data/spec/opal_irb/completion_engine_spec.rb +204 -0
  50. data/spec/opal_irb/completion_results_spec.rb +32 -0
  51. data/spec/opal_irb_log_director_spec.rb +19 -0
  52. data/spec/opal_irb_spec.rb +19 -0
  53. data/spec/spec_helper.rb +1 -0
  54. metadata +151 -0
@@ -0,0 +1,398 @@
1
+ require 'opal'
2
+ require "opal-jquery"
3
+ require "opal_irb"
4
+
5
+ class OpalIRBHomebrewConsole
6
+
7
+ def reset_settings
8
+ `localStorage.clear()`
9
+ end
10
+
11
+ def save_settings
12
+ `localStorage.settings = JSON.stringify( #{@settings.map})`
13
+ end
14
+
15
+ def resize_input(e)
16
+ width = @inputdiv.width() - @inputl.width()
17
+ content = @input.value()
18
+ # content.gsub /\n/, '<br/>'
19
+ @inputcopy.html content
20
+
21
+ @inputcopy.width width
22
+ @input.width width
23
+ @input.height @inputcopy.height() + 2
24
+ end
25
+
26
+ def scroll_to_bottom
27
+ `window.scrollTo( 0, #@prompt[0].offsetTop)`
28
+ end
29
+
30
+ DEFAULT_SETTINGS = {
31
+ # last_variable: '$_',
32
+ max_lines: 500,
33
+ max_depth: 2,
34
+ show_hidden: false,
35
+ colorize: true,
36
+ }
37
+
38
+ def escape_html(s)
39
+ s.gsub(/&/,'&amp;').gsub(/</,'&lt;').gsub(/>/,'&gt;');
40
+ end
41
+
42
+ attr_reader :settings
43
+
44
+ def initialize (output, input, prompt, inputdiv, inputl, inputr, inputcopy, settings={})
45
+ @output, @input, @prompt, @inputdiv, @inputl, @inputr, @inputcopy =
46
+ output, input, prompt, inputdiv, inputl, inputr, inputcopy
47
+ @history = []
48
+ @historyi = -1
49
+ @saved = ''
50
+ @multiline = false
51
+ @settings = DEFAULT_SETTINGS.clone
52
+
53
+ @irb = OpalIrb.new
54
+
55
+ # if localStorage and localStorage.settings
56
+ # for k, v of JSON.parse(localStorage.settings)
57
+ # @settings[k] = v
58
+ # end
59
+ # for k, v of settings
60
+ # @settings[k] = v
61
+ # end
62
+ myself = self
63
+ @input.on :keydown do |evt|
64
+ myself.handle_keypress(evt)
65
+ end
66
+
67
+ initialize_window
68
+ print_header
69
+
70
+ end
71
+
72
+
73
+ def print(args)
74
+ s = args
75
+ o = @output.html + s + "\n"
76
+ # @output[0].innerHTML = o.split("\n")[-@settings.max_lines...].join("\n")
77
+ # @output.html = o.split("\n")[-@settings[:max_lines]].join("\n")
78
+ @output.html = o
79
+
80
+ nil
81
+ end
82
+
83
+ def to_s
84
+ {
85
+ history: @history,
86
+ multiline: @multiline,
87
+ settings: @settings
88
+ }.inspect
89
+ end
90
+
91
+
92
+ def add_to_history(s)
93
+ @history.unshift s
94
+ @historyi = -1
95
+ end
96
+
97
+ def add_to_saved(s)
98
+ @saved += s[0...-1] == '\\' ? s[0...-1] : s
99
+ @saved += "\n"
100
+ add_to_history s
101
+ end
102
+
103
+ def clear
104
+ @output.html = ""
105
+ nil
106
+ end
107
+
108
+ def process_saved
109
+ begin
110
+ compiled = @irb.parse @saved
111
+ # doesn't work w/th opal 0.3.27 compiled = compiled[14..-7] # strip off anonymous function so variables will persist
112
+ # compiled = compiled.split("\n")[2..-2].join("\n")
113
+ # compiled = compiled.gsub("return", "")
114
+ # value = eval.call window, compiled
115
+ log compiled
116
+ value = `eval(compiled)`
117
+ # window[@settings.last_variable] = value
118
+ $_ = value
119
+ output = `nodeutil.inspect( value, #{@settings[:show_hidden]}, #{@settings[:max_depth]}, #{@settings[:colorize]})`
120
+ # output = value
121
+ rescue Exception => e
122
+ if e.backtrace
123
+ output = "FOR:\n#{compiled}\n============\n" + e.backtrace.join("\n")
124
+
125
+ # FF doesn't have Error.toString() as the first line of Error.stack
126
+ # while Chrome does.
127
+ # if output.split("\n")[0] != `e.toString()`
128
+ # output = "#{`e.toString()`}\n#{`e.stack`}"
129
+ # end
130
+ else
131
+ output = `e.toString()`
132
+ end
133
+ end
134
+ @saved = ''
135
+ print output
136
+ end
137
+
138
+ # help
139
+ def help
140
+ text = [
141
+ " ",
142
+ "<strong>Features</strong>",
143
+ "<strong>========</strong>",
144
+ "+ <strong>Esc</strong> enters multiline mode.",
145
+ "+ <strong>Up/Down arrow and ctrl-p/ctrl-n</strong> flips through line history.",
146
+ # "+ <strong>#{@settings[:last_variable]}</strong> stores the last returned value.",
147
+ "+ Access the internals of this console through <strong>$irb</strong>.",
148
+ "+ <strong>clear</strong> clears this console.",
149
+ "+ <strong>history</strong> shows line history.",
150
+ " ",
151
+ "<strong>@Settings</strong>",
152
+ "<strong>========</strong>",
153
+ "You can modify the behavior of this IRB by altering <strong>$irb.@settings</strong>:",
154
+ " ",
155
+ # "+ <strong>last_variable</strong> (#{@settings[:last_variable]}): variable name in which last returned value is stored",
156
+ "+ <strong>max_lines</strong> (#{@settings[:max_lines]}): max line count of this console",
157
+ "+ <strong>max_depth</strong> (#{@settings[:max_depth]}): max_depth in which to inspect outputted object",
158
+ "+ <strong>show_hidden</strong> (#{@settings[:show_hidden]}): flag to output hidden (not enumerable) properties of objects",
159
+ "+ <strong>colorize</strong> (#{@settings[:colorize]}): flag to colorize output (set to false if IRB is slow)",
160
+ " ",
161
+ # "<strong>$irb.save_settings()</strong> will save settings to localStorage.",
162
+ # "<strong>$irb.reset_settings()</strong> will reset settings to default.",
163
+ " "
164
+ ].join("\n")
165
+ print text
166
+ end
167
+
168
+ # only outputs to console log, use for debugging
169
+ def log thing
170
+ `console.orig_log(#{thing})`
171
+ end
172
+
173
+ def history
174
+ @history.reverse.each_with_index {|line, i|
175
+ print "#{i}: #{line}"
176
+ }
177
+ end
178
+
179
+ def handle_keypress(e)
180
+ # log e.which
181
+
182
+ case e.which
183
+ when 13 # return
184
+ e.prevent_default()
185
+ input = @input.value()
186
+ @input.value = ''
187
+
188
+ print @prompt.html + escape_html(input)
189
+
190
+ if input
191
+ add_to_saved input
192
+ if input[0...-1] != '\\' and not @multiline
193
+ process_saved()
194
+ end
195
+ end
196
+ when 27 # escape
197
+ e.prevent_default
198
+ open_multiline_dialog
199
+ when 38 # up arrow
200
+ e.prevent_default
201
+ show_previous_history
202
+ when 40 # down arrow
203
+ e.prevent_default
204
+ show_next_history
205
+ when 80 # p
206
+ if e.ctrl_key
207
+ e.prevent_default
208
+ show_previous_history
209
+ end
210
+ when 78 # n
211
+ if e.ctrl_key
212
+ e.prevent_default
213
+ show_next_history
214
+ end
215
+
216
+ end
217
+
218
+ end
219
+
220
+ def show_previous_history
221
+ if @historyi < @history.length-1
222
+ @historyi += 1
223
+ @input.value = @history[@historyi]
224
+ end
225
+
226
+ end
227
+
228
+ def show_next_history
229
+ if @historyi > 0
230
+ @historyi += -1
231
+ @input.value = @history[@historyi]
232
+ end
233
+ end
234
+
235
+ def initialize_window
236
+ resize_input()
237
+ @input.focus()
238
+ end
239
+
240
+ CMD_LINE_METHOD_DEFINITIONS = [
241
+ 'def help
242
+ $irb.help
243
+ nil
244
+ end',
245
+
246
+ 'def clear
247
+ $irb.clear
248
+ nil
249
+ end',
250
+
251
+ 'def history
252
+ $irb.history
253
+ nil
254
+ end'
255
+ ]
256
+ def setup_cmd_line_methods
257
+ CMD_LINE_METHOD_DEFINITIONS.each {|method_defn|
258
+ compiled = @irb.parse method_defn
259
+ `eval(compiled)`
260
+ }
261
+
262
+
263
+ end
264
+
265
+ def print_header
266
+ print [
267
+ "# Opal v#{Opal::VERSION} IRB", #"# Opal v#{OPAL_VERSION} IRB",
268
+ "# <a href=\"https://github.com/fkchang/opal-irb\" target=\"_blank\">https://github.com/fkchang/opal-irb</a>",
269
+ "# inspired by <a href=\"https://github.com/larryng/coffeescript-repl\" target=\"_blank\">https://github.com/larryng/coffeescript-repl</a>",
270
+ "#",
271
+ "# <strong>help</strong> for features and tips.",
272
+ " "
273
+ ].join("\n")
274
+ end
275
+
276
+ def self.create_html(parent_container_id)
277
+ parent = Element.find(parent_container_id)
278
+ parent.html = ' <div id="outputdiv">
279
+ <pre id="output"></pre>
280
+ </div>
281
+ <div id="inputdiv">
282
+ <div id="inputl">
283
+ <pre id="prompt">opal&gt;&nbsp;</pre>
284
+ </div>
285
+ <div id="inputr">
286
+ <textarea id="input" spellcheck="false"></textarea>
287
+ <div id="inputcopy"></div>
288
+ </div>
289
+ '
290
+ # puts parent.html
291
+
292
+ end
293
+
294
+ def self.create(container_id)
295
+ create_html(container_id)
296
+ output = Element.find('#output')
297
+ input = Element.find('#input')
298
+ prompt = Element.find('#prompt')
299
+ inputdiv = Element.find('#inputdiv')
300
+ inputl = Element.find('#inputl')
301
+ inputr = Element.find('#inputr')
302
+ inputcopy = Element.find('#inputcopy')
303
+
304
+ # instantiate our IRB and expose irb as $irb
305
+ irb = OpalIRBHomebrewConsole.new( output, input, prompt, inputdiv, inputl, inputr,
306
+ inputcopy)
307
+ irb.setup_cmd_line_methods
308
+ # bind other handlers
309
+ input.on :keydown do
310
+ irb.scroll_to_bottom
311
+ end
312
+ Element.find(`window`).on :resize do |e|
313
+ irb.resize_input e
314
+ end
315
+
316
+ input.on :keyup do |e|
317
+ irb.resize_input e
318
+ end
319
+ input.on :change do |e|
320
+ irb.resize_input e
321
+ end
322
+
323
+ Element.find('html').on :click do |e|
324
+ # if e.clientY > input[0].offsetTop
325
+ input.focus()
326
+ # end
327
+ end
328
+
329
+ %x|
330
+ console.orig_log = console.log
331
+ console.log = function() {
332
+ var args;
333
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
334
+ console.orig_log(args);
335
+ Opal.gvars["irb"].$print(args);
336
+ };
337
+ |
338
+ $irb = irb
339
+
340
+ irb.setup_multi_line
341
+
342
+ end
343
+
344
+ def setup_multi_line
345
+ myself = self # hate that I have to do this, didn't have to do it before
346
+ %x|
347
+ $( ".dialog" ).dialog({
348
+ autoOpen: false,
349
+ show: "blind",
350
+ hide: "explode",
351
+ modal: true,
352
+ width: "500px",
353
+ title: "Multi Line Edit",
354
+ buttons: {
355
+ "Run it": function() {
356
+ $( this ).dialog( "close" );
357
+ #{myself}.$process_multiline();
358
+ },
359
+ "Cancel": function() {
360
+ $( this ).dialog( "close" );
361
+ },
362
+ }
363
+ });
364
+ |
365
+
366
+ @open_editor_dialog_function = %x|function() {
367
+ $( ".dialog" ).dialog( "open" );
368
+ setTimeout(function(){editor.refresh();}, 20);
369
+ }
370
+ |
371
+ @editor = %x|
372
+ editor = CodeMirror.fromTextArea(document.getElementById("multi_line_input"),
373
+ {mode: "ruby",
374
+ lineNumbers: true,
375
+ matchBrackets: true,
376
+ keyMap: "emacs",
377
+ theme: "default"
378
+ });
379
+
380
+ |
381
+
382
+ end
383
+
384
+ def open_multiline_dialog
385
+ @editor.setValue(@input.value)
386
+ @open_editor_dialog_function.call
387
+ end
388
+
389
+
390
+ def process_multiline
391
+ multi_line_value = @editor.getValue.sub(/(\n)+$/, "")
392
+ add_to_saved multi_line_value
393
+ print multi_line_value
394
+ process_saved
395
+ @input.value = ""
396
+ end
397
+
398
+ end