summaryrefslogtreecommitdiff
path: root/lib/irb/ext/eval_history.rb
blob: 6c21ff00eea420efbc1a04f26fd7d326a7957c61 (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
147
148
149
# frozen_string_literal: true
#
#   history.rb -
#   	by Keiju ISHITSUKA(keiju@ruby-lang.org)
#

module IRB # :nodoc:

  class Context

    NOPRINTING_IVARS.push "@eval_history_values"

    # See #set_last_value
    alias _set_last_value set_last_value

    def set_last_value(value)
      _set_last_value(value)

      if defined?(@eval_history) && @eval_history
        @eval_history_values.push @line_no, @last_value
        workspace.evaluate "__ = IRB.CurrentContext.instance_eval{@eval_history_values}"
      end

      @last_value
    end

    remove_method :eval_history= if method_defined?(:eval_history=)
    # The command result history limit. This method is not available until
    # #eval_history= was called with non-nil value (directly or via
    # setting <code>IRB.conf[:EVAL_HISTORY]</code> in <code>.irbrc</code>).
    attr_reader :eval_history
    # Sets command result history limit. Default value is set from
    # <code>IRB.conf[:EVAL_HISTORY]</code>.
    #
    # +no+ is an Integer or +nil+.
    #
    # Returns +no+ of history items if greater than 0.
    #
    # If +no+ is 0, the number of history items is unlimited.
    #
    # If +no+ is +nil+, execution result history isn't used (default).
    #
    # EvalHistory values are available via <code>__</code> variable, see
    # IRB::EvalHistory.
    def eval_history=(no)
      if no
        if defined?(@eval_history) && @eval_history
          @eval_history_values.size(no)
        else
          @eval_history_values = EvalHistory.new(no)
          IRB.conf[:__TMP__EHV__] = @eval_history_values
          workspace.evaluate("__ = IRB.conf[:__TMP__EHV__]")
          IRB.conf.delete(:__TMP_EHV__)
        end
      else
        @eval_history_values = nil
      end
      @eval_history = no
    end
  end

  # Represents history of results of previously evaluated commands.
  #
  # Available via <code>__</code> variable, only if <code>IRB.conf[:EVAL_HISTORY]</code>
  # or <code>IRB::CurrentContext().eval_history</code> is non-nil integer value
  # (by default it is +nil+).
  #
  # Example (in `irb`):
  #
  #    # Initialize history
  #    IRB::CurrentContext().eval_history = 10
  #    # => 10
  #
  #    # Perform some commands...
  #    1 + 2
  #    # => 3
  #    puts 'x'
  #    # x
  #    # => nil
  #    raise RuntimeError
  #    # ...error raised
  #
  #    # Inspect history (format is "<item number> <evaluated value>":
  #    __
  #    # => 1 10
  #    # 2 3
  #    # 3 nil
  #
  #    __[1]
  #    # => 10
  #
  class EvalHistory

    def initialize(size = 16)  # :nodoc:
      @size = size
      @contents = []
    end

    def size(size) # :nodoc:
      if size != 0 && size < @size
        @contents = @contents[@size - size .. @size]
      end
      @size = size
    end

    # Get one item of the content (both positive and negative indexes work).
    def [](idx)
      begin
        if idx >= 0
          @contents.find{|no, val| no == idx}[1]
        else
          @contents[idx][1]
        end
      rescue NameError
        nil
      end
    end

    def push(no, val)  # :nodoc:
      @contents.push [no, val]
      @contents.shift if @size != 0 && @contents.size > @size
    end

    alias real_inspect inspect

    def inspect  # :nodoc:
      if @contents.empty?
        return real_inspect
      end

      unless (last = @contents.pop)[1].equal?(self)
        @contents.push last
        last = nil
      end
      str = @contents.collect{|no, val|
        if val.equal?(self)
          "#{no} ...self-history..."
        else
          "#{no} #{val.inspect}"
        end
      }.join("\n")
      if str == ""
        str = "Empty."
      end
      @contents.push last if last
      str
    end
  end
end