opal-irb 0.7.0

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.
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