summaryrefslogtreecommitdiff
path: root/lib/irb/workspace.rb
blob: ff8f5da64fa31e663d9c866ed2315dad9f562dc2 (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
136
137
138
139
140
141
142
143
144
145
146
# 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("self.class.remove_method(:irb_binding) if defined?(irb_binding); 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
      @binding.local_variable_set(:_, nil)
    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

    def local_variable_set(name, value)
      @binding.local_variable_set(name, value)
    end

    def local_variable_get(name)
      @binding.local_variable_get(name)
    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.source_location

      if defined?(::SCRIPT_LINES__[file]) && lines = ::SCRIPT_LINES__[file]
        code = ::SCRIPT_LINES__[file].join('')
      else
        begin
          code = File.read(file)
        rescue SystemCallError
          return
        end
      end
      lines = Color.colorize_code(code).lines
      pos -= 1

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

      fmt = " %2s #{Color.colorize("%#{end_pos.to_s.length}d", [:BLUE, :BOLD])}: %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}#{Color.clear}\n"
    end

    def IRB.delete_caller
    end
  end
end