summaryrefslogtreecommitdiff
path: root/lib/irb/workspace.rb
blob: 9ce84b60fa53d0815a80ab5ef783a7c036342395 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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
127
128
129
130
131
132
133
134
135
# frozen_string_literal: false
#
#   irb/workspace-binding.rb -
#   	$Release Version: 0.9.6$
#   	$Revision$
#   	by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
# --
#
#
#
module IRB # :nodoc:
  class WorkSpace
    # Creates a new workspace.
    #
    # set self to main if specified, otherwise
    # inherit main from TOPLEVEL_BINDING.
    def initialize(*main)
      if main[0].kind_of?(Binding)
        @binding = main.shift
      elsif IRB.conf[:SINGLE_IRB]
        @binding = TOPLEVEL_BINDING
      else
        case IRB.conf[:CONTEXT_MODE]
        when 0	# binding in proc on TOPLEVEL_BINDING
          @binding = eval("proc{binding}.call",
                          TOPLEVEL_BINDING,
                          __FILE__,
                          __LINE__)
        when 1	# binding in loaded file
          require "tempfile"
          f = Tempfile.open("irb-binding")
          f.print <<EOF
      $binding = binding
EOF
          f.close
          load f.path
          @binding = $binding

        when 2	# binding in loaded file(thread use)
          unless defined? BINDING_QUEUE
            IRB.const_set(:BINDING_QUEUE, Thread::SizedQueue.new(1))
            Thread.abort_on_exception = true
            Thread.start do
              eval "require \"irb/ws-for-case-2\"", TOPLEVEL_BINDING, __FILE__, __LINE__
            end
            Thread.pass
          end
          @binding = BINDING_QUEUE.pop

        when 3	# binding in function on TOPLEVEL_BINDING(default)
          @binding = eval("def irb_binding; private; binding; end; irb_binding",
                          TOPLEVEL_BINDING,
                          __FILE__,
                          __LINE__ - 3)
        end
      end
      if main.empty?
        @main = eval("self", @binding)
      else
        @main = main[0]
        IRB.conf[:__MAIN__] = @main
        case @main
        when Module
          @binding = eval("IRB.conf[:__MAIN__].module_eval('binding', __FILE__, __LINE__)", @binding, __FILE__, __LINE__)
        else
          begin
            @binding = eval("IRB.conf[:__MAIN__].instance_eval('binding', __FILE__, __LINE__)", @binding, __FILE__, __LINE__)
          rescue TypeError
            IRB.fail CantChangeBinding, @main.inspect
          end
        end
      end
      eval("_=nil", @binding)
    end

    # The Binding of this workspace
    attr_reader :binding
    # The top-level workspace of this context, also available as
    # <code>IRB.conf[:__MAIN__]</code>
    attr_reader :main

    # Evaluate the given +statements+ within the  context of this workspace.
    def evaluate(context, statements, file = __FILE__, line = __LINE__)
      eval(statements, @binding, file, line)
    end

    # error message manipulator
    def filter_backtrace(bt)
      case IRB.conf[:CONTEXT_MODE]
      when 0
        return nil if bt =~ /\(irb_local_binding\)/
      when 1
        if(bt =~ %r!/tmp/irb-binding! or
            bt =~ %r!irb/.*\.rb! or
            bt =~ /irb\.rb/)
          return nil
        end
      when 2
        return nil if bt =~ /irb\/.*\.rb/
        return nil if bt =~ /irb\.rb/
      when 3
        return nil if bt =~ /irb\/.*\.rb/
        return nil if bt =~ /irb\.rb/
        bt = bt.sub(/:\s*in `irb_binding'/, '')
      end
      bt
    end

    def code_around_binding
      file, pos = @binding.eval('[__FILE__, __LINE__]')

      unless defined?(::SCRIPT_LINES__[file]) && lines = ::SCRIPT_LINES__[file]
        begin
          lines = File.readlines(file)
        rescue SystemCallError
          return
        end
      end
      pos -= 1

      start_pos = [pos - 5, 0].max
      end_pos   = [pos + 5, lines.size - 1].min

      fmt = " %2s %#{end_pos.to_s.length}d: %s"
      body = (start_pos..end_pos).map do |current_pos|
        sprintf(fmt, pos == current_pos ? '=>' : '', current_pos + 1, lines[current_pos])
      end.join("")
      "\nFrom: #{file} @ line #{pos + 1} :\n\n#{body}\n"
    end

    def IRB.delete_caller
    end
  end
end