Shell-style History for irb

This script is the companion code to the entries located here (original announcement) and here (actual release). Please see there for more information, examples, and updates.

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
# Adds shell-style history display and replay to irb.  The magic happens in
# the h, h!, and hw methods.
#
# == Authors
#
# * Ben Bleything <ben@bleything.net>
#
# == Copyright
#
# Copyright (c) 2007 Ben Bleything
#
# This code released under the terms of the BSD license.
#
# == Version
#
#  $Id: history.rb 50 2007-07-30 18:55:09Z ben $
#


# Lists the last how_many lines of history (defaults to 50).  Aliased to h.
def history( how_many = 50 )
    history_size = Readline::HISTORY.size

    # no lines, get out of here
    puts "No history" and return if history_size == 0

    start_index = 0

    # not enough lines, only show what we have
    if history_size <= how_many
        how_many  = history_size - 1
        end_index = how_many
    else
        end_index = history_size - 1 # -1 to adjust for array offset
        start_index = end_index - how_many 
    end

    start_index.upto( end_index ) {|i| print_line i}
    nil
end
alias :h  :history


# replay lines from history.  Aliased to h!
#
# h! by itself will replay the most recent line.  You can also pass in a
# range, array, or any mixture of the three.
#
# We subtract 2 from HISTORY.size because -1 is the command we just issued.
def history_do( *lines )
    lines = [Readline::HISTORY.size - 2] if lines.empty?

    to_eval = get_lines( lines )

    to_eval.each {|l| Readline::HISTORY << l}

    IRB.CurrentContext.workspace.evaluate self, to_eval.join(';')
end
alias :h! :history_do


# writes history to a named file.  This is useful if you want to show somebody
# something you did in irb, or for rapid prototyping.  Aliased to hw.
#
# Uses similar calling semantics to h!, that is, you can pass in fixnums,
# ranges, arrays, or any combination thereof.
def history_write( filename, *lines )
    File.open( filename, 'w' ) do |f|
        get_lines( lines ).each do |l|
            f.puts l
        end
    end
end
alias :hw :history_write


private

# simple getter to fetch from Readline
def get_line(line_number)
    Readline::HISTORY[line_number]
end


# the code what powers the line fetcherating.  Accepts an array and iterates
# over each entry, fetching that line from the history and placing it into a
# temporary array which is ultimately returned.
def get_lines( lines )
    out = []

    lines.each do |line|
        case line
        when Fixnum
            out << get_line( line )
        when Range
            line.to_a.each do |l|
                out << get_line( l )
            end
        end
    end

    return out
end


# prints out the contents of the line from history, along with a line number,
# if desired.
def print_line(line_number, show_line_numbers = true)
    print "[%04d] " % line_number if show_line_numbers
    puts get_line(line_number)
end