Class: Opal::REPL

Inherits:
Object
  • Object
show all
Defined in:
lib/opal/repl.rb

Defined Under Namespace

Classes: Silencer

Constant Summary collapse

HISTORY_PATH =
File.expand_path('~/.opal-repl-history')

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeREPL

Returns a new instance of REPL.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/opal/repl.rb', line 15

def initialize
  @argv = []
  @colorize = true

  begin
    require 'readline'
  rescue LoadError
    abort 'opal-repl depends on readline, which is not currently available'
  end

  begin
    FileUtils.touch(HISTORY_PATH)
  rescue
    nil
  end
  @history = File.exist?(HISTORY_PATH)
end

Instance Attribute Details

#colorizeObject

Returns the value of attribute colorize.



13
14
15
# File 'lib/opal/repl.rb', line 13

def colorize
  @colorize
end

Instance Method Details

#eval_ruby(code) ⇒ Object



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
# File 'lib/opal/repl.rb', line 75

def eval_ruby(code)
  builder = Opal::Builder.new
  silencer = Silencer.new

  code = "#{@incomplete}#{code}"
  if code.start_with? 'ls '
    eval_code = code[3..-1]
    mode = :ls
  elsif code == 'ls'
    eval_code = 'self'
    mode = :ls
  elsif code.start_with? 'show '
    eval_code = code[5..-1]
    mode = :show
  else
    eval_code = code
    mode = :inspect
  end

  begin
    silencer.silence do
      builder.build_str(eval_code, '(irb)', irb: true, const_missing: true)
    end
    @incomplete = nil
  rescue Opal::SyntaxError => e
    if LINEBREAKS.include?(e.message)
      @incomplete = "#{code}\n"
    else
      @incomplete = nil
      if silencer.warnings.empty?
        warn e.full_message
      else
        # Most likely a parser error
        warn silencer.warnings
      end
    end
    return
  end
  builder.processed[0...-1].each { |js_code| eval_js(:silent, js_code.to_s) }
  last_processed_file = builder.processed.last.to_s

  if mode == :show
    puts last_processed_file
    return
  end

  eval_js(mode, last_processed_file)
rescue Interrupt, SystemExit => e
  raise e
rescue Exception => e # rubocop:disable Lint/RescueException
  puts e.full_message(highlight: true)
end

#finishObject



69
70
71
72
73
# File 'lib/opal/repl.rb', line 69

def finish
  @pipe.close
rescue
  nil
end

#load_opalObject



45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/opal/repl.rb', line 45

def load_opal
  runner = @argv.reject { |i| i == '--repl' }
  runner += ['-e', 'require "opal/repl_js"']
  runner = [RbConfig.ruby, "#{__dir__}/../../exe/opal"] + runner

  @pipe = IO.popen(runner, 'r+',
    # What I try to achieve here: let the runner ignore
    # interrupts. Those should be handled by a supervisor.
    pgroup: true,
    new_pgroup: true,
  )
end

#run(argv = []) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
# File 'lib/opal/repl.rb', line 33

def run(argv = [])
  @argv = argv

  savepoint = save_tty
  load_opal
  load_history
  run_input_loop
ensure
  dump_history
  restore_tty(savepoint)
end

#run_input_loopObject



58
59
60
61
62
63
64
65
66
67
# File 'lib/opal/repl.rb', line 58

def run_input_loop
  while (line = readline)
    eval_ruby(line)
  end
rescue Interrupt
  @incomplete = nil
  retry
ensure
  finish
end