dietrb 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ end
12
12
 
13
13
  desc "Run dietrb with ruby19"
14
14
  task :run do
15
- sh "ruby19 -Ilib ./bin/dietrb"
15
+ sh "ruby19 -Ilib ./bin/dietrb -r irb/ext/completion"
16
16
  end
17
17
 
18
18
  begin
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.3.0
data/bin/dietrb CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'irb'
4
+
3
5
  unless ARGV.empty?
4
6
  require 'optparse'
5
7
 
@@ -9,6 +11,8 @@ unless ARGV.empty?
9
11
  opt.on("-r load-lib", "Loads the given library (same as `ruby -r')") { |lib| require lib }
10
12
  opt.on("-d", "Set $DEBUG to true (same as `ruby -d')") { $DEBUG = true }
11
13
  opt.on("-I path", "Add path to $LOAD_PATH") { |path| $LOAD_PATH.unshift(path) }
14
+ opt.on("--simple-prompt", "Simple prompt mode") { IRB.formatter.prompt = :simple }
15
+ opt.on("--noprompt", "No prompt mode") { IRB.formatter.prompt = nil }
12
16
  opt.on("-v", "--version", "Print the version of #{bin}") do
13
17
  puts File.read(File.expand_path('../../VERSION', __FILE__))
14
18
  exit
@@ -16,5 +20,17 @@ unless ARGV.empty?
16
20
  end.parse!(ARGV)
17
21
  end
18
22
 
19
- require 'irb'
20
- irb(self, TOPLEVEL_BINDING)
23
+ IRB.formatter.filter_from_backtrace << /^#{__FILE__}/
24
+
25
+ if ARGV.empty?
26
+ irb(self, TOPLEVEL_BINDING.dup)
27
+ else
28
+ path = ARGV.shift
29
+ context = IRB::Context.new(self, TOPLEVEL_BINDING.dup)
30
+ File.open(path, 'r') do |file|
31
+ file.each_line do |line|
32
+ puts IRB.formatter.prompt(context) + line
33
+ context.process_line(line)
34
+ end
35
+ end
36
+ end
data/dietrb.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{dietrb}
8
- s.version = "0.2.1"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Eloy Duran"]
12
- s.date = %q{2010-02-18}
12
+ s.date = %q{2010-02-24}
13
13
  s.default_executable = %q{dietrb}
14
14
  s.description = %q{IRB on a diet, for MacRuby / Ruby 1.9}
15
15
  s.email = %q{eloy.de.enige@gmail.com}
@@ -30,9 +30,11 @@ Gem::Specification.new do |s|
30
30
  "lib/irb/context.rb",
31
31
  "lib/irb/ext/completion.rb",
32
32
  "lib/irb/ext/macruby.rb",
33
+ "lib/irb/formatter.rb",
33
34
  "lib/irb/source.rb",
34
35
  "spec/completion_spec.rb",
35
36
  "spec/context_spec.rb",
37
+ "spec/formatter_spec.rb",
36
38
  "spec/irb_spec.rb",
37
39
  "spec/source_spec.rb",
38
40
  "spec/spec_helper.rb"
@@ -46,6 +48,7 @@ Gem::Specification.new do |s|
46
48
  s.test_files = [
47
49
  "spec/completion_spec.rb",
48
50
  "spec/context_spec.rb",
51
+ "spec/formatter_spec.rb",
49
52
  "spec/irb_spec.rb",
50
53
  "spec/source_spec.rb",
51
54
  "spec/spec_helper.rb"
data/lib/irb/context.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'irb/formatter'
1
2
  require 'readline'
2
3
 
3
4
  module IRB
@@ -22,20 +23,23 @@ module IRB
22
23
  clear_buffer
23
24
  end
24
25
 
25
- def __evaluate__(source)
26
- eval(source, @binding)
26
+ def __evaluate__(source, file = __FILE__, line = __LINE__)
27
+ eval(source, @binding, file, line)
27
28
  end
28
29
 
29
30
  def evaluate(source)
30
- result = __evaluate__("_ = (#{source})")
31
- puts format_result(result)
31
+ result = __evaluate__("_ = (#{source})", '(irb)', @line - @source.buffer.size + 1)
32
+ puts formatter.result(result)
32
33
  result
33
34
  rescue Exception => e
34
- puts format_exception(e)
35
+ puts formatter.exception(e)
35
36
  end
36
37
 
37
38
  def readline
38
- Readline.readline(prompt, true)
39
+ Readline.readline(formatter.prompt(self), true)
40
+ rescue Interrupt
41
+ clear_buffer
42
+ ""
39
43
  end
40
44
 
41
45
  def run
@@ -64,7 +68,7 @@ module IRB
64
68
  return false if @source.to_s == "quit"
65
69
 
66
70
  if @source.syntax_error?
67
- puts format_syntax_error(@source.syntax_error)
71
+ puts formatter.syntax_error(@line, @source.syntax_error)
68
72
  @source.pop
69
73
  elsif @source.code_block?
70
74
  evaluate(@source)
@@ -75,26 +79,12 @@ module IRB
75
79
  true
76
80
  end
77
81
 
78
- PROMPT = "irb(%s):%03d:%d> "
79
-
80
- def prompt
81
- PROMPT % [@object.inspect, @line, @source.level]
82
- end
83
-
84
- def format_result(result)
85
- "=> #{result.inspect}"
86
- end
87
-
88
- def format_exception(e)
89
- "#{e.class.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
90
- end
82
+ private
91
83
 
92
- def format_syntax_error(e)
93
- "SyntaxError: compile error\n(irb):#{@line}: #{e}"
84
+ def formatter
85
+ IRB.formatter
94
86
  end
95
87
 
96
- private
97
-
98
88
  def clear_buffer
99
89
  @source = Source.new
100
90
  end
@@ -7,6 +7,29 @@ module IRB
7
7
  VALUE = 1
8
8
  CALLEE = 3
9
9
 
10
+ RESERVED_UPCASE_WORDS = %w{
11
+ BEGIN END
12
+ }
13
+
14
+ RESERVED_DOWNCASE_WORDS = %w{
15
+ alias and
16
+ begin break
17
+ case class
18
+ def defined do
19
+ else elsif end ensure
20
+ false for
21
+ if in
22
+ module
23
+ next nil not
24
+ or
25
+ redo rescue retry return
26
+ self super
27
+ then true
28
+ undef unless until
29
+ when while
30
+ yield
31
+ }
32
+
10
33
  # Returns an array of possible completion results, with the current
11
34
  # IRB::Context.
12
35
  #
@@ -33,6 +56,10 @@ module IRB
33
56
  @context.object.methods.map(&:to_s)
34
57
  end
35
58
 
59
+ def instance_methods_of(klass)
60
+ evaluate(klass).instance_methods
61
+ end
62
+
36
63
  # TODO: test and or fix the fact that we need to get constants from the
37
64
  # singleton class.
38
65
  def constants
@@ -54,18 +81,39 @@ module IRB
54
81
 
55
82
  # [:call, [:hash, nil], :".", [:@ident, x, …]]
56
83
  if root[TYPE] == :call
57
- call = true
58
- filter = root[CALLEE][VALUE]
59
- receiver = source[0..-(filter.length + 2)]
60
- root = root[VALUE]
84
+ call = true
85
+ stack = unwind_callstack(root)
86
+ # [[:var_ref, [:@const, "Klass", [1, 0]]], [:call, "new"]]
87
+ # [[:var_ref, [:@ident, "klass", [1, 0]]], [:call, "new"], [:call, "filter"]]
88
+ if stack[1][VALUE] == 'new'
89
+ klass = stack[0][VALUE][VALUE]
90
+ filter = stack[2][VALUE] if stack[2]
91
+ receiver = "#{klass}.new"
92
+ methods = instance_methods_of(klass)
93
+ else
94
+ filter = root[CALLEE][VALUE]
95
+ filter = stack[1][VALUE]
96
+ receiver = source[0..-(filter.length + 2)]
97
+ root = root[VALUE]
98
+ end
61
99
  end
62
100
 
63
101
  if call
64
- format_methods(receiver, methods_of_object(root), filter)
102
+ format_methods(receiver, methods || methods_of_object(root), filter)
65
103
  else
66
104
  match_methods_vars_or_consts_in_scope(root)
67
- end.sort
105
+ end.sort.uniq
106
+ end
107
+ end
108
+
109
+ def unwind_callstack(root, stack = [])
110
+ if root[TYPE] == :call
111
+ stack.unshift [:call, root[CALLEE][VALUE]]
112
+ unwind_callstack(root[VALUE], stack)
113
+ else
114
+ stack.unshift root
68
115
  end
116
+ stack
69
117
  end
70
118
 
71
119
  def match_methods_vars_or_consts_in_scope(symbol)
@@ -73,7 +121,7 @@ module IRB
73
121
  filter = var[VALUE]
74
122
  case var[TYPE]
75
123
  when :@ident
76
- local_variables + instance_methods
124
+ local_variables + instance_methods + RESERVED_DOWNCASE_WORDS
77
125
  when :@gvar
78
126
  global_variables.map(&:to_s)
79
127
  when :@const
@@ -81,7 +129,7 @@ module IRB
81
129
  filter = "::#{filter}"
82
130
  Object.constants.map { |c| "::#{c}" }
83
131
  else
84
- constants
132
+ constants + RESERVED_UPCASE_WORDS
85
133
  end
86
134
  end.grep(/^#{Regexp.quote(filter)}/)
87
135
  end
@@ -0,0 +1,49 @@
1
+ module IRB
2
+ def self.formatter
3
+ @formatter ||= Formatter.new
4
+ end
5
+
6
+ class Formatter
7
+ DEFAULT_PROMPT = "irb(%s):%03d:%d> "
8
+ SIMPLE_PROMPT = ">> "
9
+ NO_PROMPT = ""
10
+ SYNTAX_ERROR = "SyntaxError: compile error\n(irb):%d: %s"
11
+ SOURCE_ROOT = /^#{File.expand_path('../../../', __FILE__)}/
12
+
13
+ attr_writer :prompt
14
+ attr_reader :filter_from_backtrace
15
+
16
+ def initialize
17
+ @prompt = :default
18
+ @filter_from_backtrace = [SOURCE_ROOT]
19
+ end
20
+
21
+ def prompt(context)
22
+ case @prompt
23
+ when :default then DEFAULT_PROMPT % [context.object.inspect, context.line, context.source.level]
24
+ when :simple then SIMPLE_PROMPT
25
+ else
26
+ NO_PROMPT
27
+ end
28
+ end
29
+
30
+ def result(object)
31
+ "=> #{object.inspect}"
32
+ end
33
+
34
+ def syntax_error(line, message)
35
+ SYNTAX_ERROR % [line, message]
36
+ end
37
+
38
+ def exception(exception)
39
+ backtrace = $DEBUG ? exception.backtrace : filter_backtrace(exception.backtrace)
40
+ "#{exception.class.name}: #{exception.message}\n\t#{backtrace.join("\n\t")}"
41
+ end
42
+
43
+ def filter_backtrace(backtrace)
44
+ backtrace.reject do |line|
45
+ @filter_from_backtrace.any? { |pattern| pattern.match(line) }
46
+ end
47
+ end
48
+ end
49
+ end
data/lib/irb/source.rb CHANGED
@@ -10,8 +10,11 @@ module IRB
10
10
 
11
11
  # Adds a source line to the buffer and flushes the cached reflection.
12
12
  def <<(source)
13
- @reflection = nil
14
- @buffer << source.chomp
13
+ source = source.strip
14
+ unless source.empty?
15
+ @reflection = nil
16
+ @buffer << source
17
+ end
15
18
  end
16
19
 
17
20
  # Removes the last line from the buffer and flushes the cached reflection.
@@ -54,11 +57,11 @@ module IRB
54
57
  @reflection ||= Reflector.new(source)
55
58
  end
56
59
 
57
- class Reflector < Ripper
60
+ class Reflector < Ripper::SexpBuilder
58
61
  def initialize(source)
59
62
  super
60
63
  @level = 0
61
- parse
64
+ @code_block = !parse.nil?
62
65
  end
63
66
 
64
67
  # Returns the code block indentation level.
@@ -85,7 +88,7 @@ module IRB
85
88
  #
86
89
  # def foo; p :ok; end
87
90
  def code_block?
88
- @level == 0
91
+ @code_block
89
92
  end
90
93
 
91
94
  # Returns whether or not the source contains a syntax error. However, it
@@ -132,6 +135,11 @@ module IRB
132
135
  super
133
136
  end
134
137
 
138
+ def on_embexpr_beg(token) #:nodoc:
139
+ @level += 1
140
+ super
141
+ end
142
+
135
143
  def on_lbrace(token) #:nodoc:
136
144
  @level += 1
137
145
  super
@@ -171,6 +171,15 @@ describe "IRB::Completion" do
171
171
  complete('-100_000_000_000_000_000_000.0.').should == imethods(Float, '-100_000_000_000_000_000_000.0')
172
172
  end
173
173
  end
174
+
175
+ it "returns *all* public instance methods of the class (the receiver) that ::new is called on" do
176
+ complete("Playground.new.").should == imethods(Playground, 'Playground.new')
177
+ complete("Playground.new.a_local_m").should == %w{ Playground.new.a_local_method }
178
+
179
+ @context.__evaluate__("klass = Playground")
180
+ complete("klass.new.").should == imethods(Playground, 'klass.new')
181
+ complete("klass.new.a_local_m").should == %w{ klass.new.a_local_method }
182
+ end
174
183
  end
175
184
 
176
185
  describe "and the source does *not* end with a period," do
@@ -218,4 +227,11 @@ describe "IRB::Completion" do
218
227
  complete("::CompletionSt").should == %w{ ::CompletionStub }
219
228
  end
220
229
  end
230
+
231
+ it "completes reserved words as variables or constants" do
232
+ (IRB::Completion::RESERVED_DOWNCASE_WORDS +
233
+ IRB::Completion::RESERVED_UPCASE_WORDS).each do |word|
234
+ complete(word[0..-2]).should.include word
235
+ end
236
+ end
221
237
  end
data/spec/context_spec.rb CHANGED
@@ -1,18 +1,21 @@
1
1
  require File.expand_path('../spec_helper', __FILE__)
2
2
  require 'tempfile'
3
3
 
4
- class << Readline
5
- attr_reader :received
6
-
7
- def stub_input(*input)
8
- @input = input
9
- end
10
-
11
- def readline(prompt, history)
12
- @received = [prompt, history]
13
- @input.shift
4
+ def stub_Readline
5
+ class << Readline
6
+ attr_reader :received
7
+
8
+ def stub_input(*input)
9
+ @input = input
10
+ end
11
+
12
+ def readline(prompt, history)
13
+ @received = [prompt, history]
14
+ @input.shift
15
+ end
14
16
  end
15
17
  end
18
+ stub_Readline
16
19
 
17
20
  main = self
18
21
 
@@ -45,26 +48,6 @@ describe "IRB::Context" do
45
48
  lambda { eval("x", @context.binding) }.should.raise NameError
46
49
  end
47
50
 
48
- it "returns a prompt string, displaying line number and code indentation level" do
49
- @context.prompt.should == "irb(main):001:0> "
50
- @context.instance_variable_set(:@line, 23)
51
- @context.prompt.should == "irb(main):023:0> "
52
- @context.source << "def foo"
53
- @context.prompt.should == "irb(main):023:1> "
54
- end
55
-
56
- it "describes the context's object in the prompt" do
57
- @context.prompt.should == "irb(main):001:0> "
58
- o = Object.new
59
- IRB::Context.new(o).prompt.should == "irb(#{o.inspect}):001:0> "
60
- end
61
-
62
- it "returns a formatted exception message" do
63
- begin; DoesNotExist; rescue NameError => e; exception = e; end
64
- @context.format_exception(exception).should ==
65
- "NameError: uninitialized constant Bacon::Context::DoesNotExist\n\t#{exception.backtrace.join("\n\t")}"
66
- end
67
-
68
51
  it "makes itself the current running context during the runloop and resigns once it's done" do
69
52
  IRB::Context.current.should == nil
70
53
 
@@ -80,6 +63,7 @@ describe "IRB::Context, when evaluating source" do
80
63
  before do
81
64
  @context = IRB::Context.new(main)
82
65
  def @context.puts(string); @printed = string; end
66
+ def @context.printed; @printed; end
83
67
  end
84
68
 
85
69
  it "evaluates code with the object's binding" do
@@ -113,8 +97,16 @@ describe "IRB::Context, when evaluating source" do
113
97
 
114
98
  it "prints the exception that occurs" do
115
99
  @context.evaluate("DoesNotExist")
116
- printed = @context.instance_variable_get(:@printed)
117
- printed.should.match /^NameError:.+DoesNotExist/
100
+ @context.printed.should.match /^NameError:.+DoesNotExist/
101
+ end
102
+
103
+ it "uses the line number of the *first* line in the buffer, for the line parameter of eval" do
104
+ @context.process_line("DoesNotExist")
105
+ @context.printed.should.match /\(irb\):1:in/
106
+ @context.process_line("class A")
107
+ @context.process_line("DoesNotExist")
108
+ @context.process_line("end")
109
+ @context.printed.should.match /\(irb\):3:in.+\(irb\):2:in/m
118
110
  end
119
111
  end
120
112
 
@@ -142,6 +134,24 @@ describe "IRB::Context, when receiving input" do
142
134
  @context.source.to_s.should == "def foo\np :ok"
143
135
  end
144
136
 
137
+ it "clears the source buffer when an Interrupt signal is received" do
138
+ begin
139
+ @context.process_line("def foo")
140
+
141
+ def Readline.readline(*args)
142
+ unless @raised
143
+ @raised = true
144
+ raise Interrupt
145
+ end
146
+ end
147
+
148
+ lambda { @context.run }.should.not.raise Interrupt
149
+ @context.source.to_s.should == ""
150
+ ensure
151
+ stub_Readline
152
+ end
153
+ end
154
+
145
155
  it "increases the current line number" do
146
156
  @context.line.should == 1
147
157
  @context.process_line("def foo")
@@ -0,0 +1,61 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ main = self
4
+
5
+ describe "IRB::Formatter" do
6
+ before do
7
+ @formatter = IRB::Formatter.new
8
+ @context = IRB::Context.new(main)
9
+ end
10
+
11
+ it "returns a prompt string, displaying line number and code indentation level" do
12
+ @formatter.prompt(@context).should == "irb(main):001:0> "
13
+ @context.instance_variable_set(:@line, 23)
14
+ @formatter.prompt(@context).should == "irb(main):023:0> "
15
+ @context.source << "def foo"
16
+ @formatter.prompt(@context).should == "irb(main):023:1> "
17
+ end
18
+
19
+ it "describes the context's object in the prompt" do
20
+ o = Object.new
21
+ @formatter.prompt(IRB::Context.new(o)).should == "irb(#{o.inspect}):001:0> "
22
+ end
23
+
24
+ it "returns a very simple prompt if specified" do
25
+ @formatter.prompt = :simple
26
+ @formatter.prompt(@context).should == ">> "
27
+ end
28
+
29
+ it "returns no prompt if specified" do
30
+ @formatter.prompt = nil
31
+ @formatter.prompt(@context).should == ""
32
+ end
33
+
34
+ it "returns a formatted exception message, with the lines, regarding dietrb, filtered out of the backtrace" do
35
+ begin; @context.__evaluate__('DoesNotExist'); rescue NameError => e; exception = e; end
36
+ backtrace = exception.backtrace.reject { |f| f =~ /#{ROOT}/ }
37
+ @formatter.exception(exception).should ==
38
+ "NameError: uninitialized constant IRB::Context::DoesNotExist\n\t#{backtrace.join("\n\t")}"
39
+ end
40
+
41
+ it "does not filter the backtrace if $DEBUG is true" do
42
+ begin
43
+ before, $DEBUG = $DEBUG, true
44
+
45
+ begin; @context.__evaluate__('DoesNotExist'); rescue NameError => e; exception = e; end
46
+ @formatter.exception(exception).should ==
47
+ "NameError: uninitialized constant IRB::Context::DoesNotExist\n\t#{exception.backtrace.join("\n\t")}"
48
+ ensure
49
+ $DEBUG = before
50
+ end
51
+ end
52
+
53
+ it "prints the result" do
54
+ @formatter.result(:foo => :foo).should == "=> {:foo=>:foo}"
55
+ end
56
+
57
+ it "prints that a syntax error occurred on the last line and reset the buffer to the previous line" do
58
+ @formatter.syntax_error(2, "syntax error, unexpected '}'").should ==
59
+ "SyntaxError: compile error\n(irb):2: syntax error, unexpected '}'"
60
+ end
61
+ end
data/spec/source_spec.rb CHANGED
@@ -19,6 +19,12 @@ describe "IRB::Source" do
19
19
  @source.buffer.should == %w{ foo bar }
20
20
  end
21
21
 
22
+ it "ignores empty strings" do
23
+ @source << ""
24
+ @source << " \n"
25
+ @source.buffer.should == []
26
+ end
27
+
22
28
  it "removes the last line from the buffer" do
23
29
  @source << "foo\n"
24
30
  @source << "bar\r\n"
@@ -120,6 +126,9 @@ describe "IRB::Source::Reflector" do
120
126
  reflect("def foo").should.not.be.code_block
121
127
  reflect("def foo; p :ok").should.not.be.code_block
122
128
  reflect("def foo; p :ok; end").should.be.code_block
129
+
130
+ reflect("if true").should.not.be.code_block
131
+ reflect("p :ok if true").should.be.code_block
123
132
  end
124
133
 
125
134
  it "returns whether or not the source contains a syntax error, except a code block not ending" do
@@ -171,6 +180,7 @@ describe "IRB::Source::Reflector" do
171
180
  [
172
181
  ["lambda { |x|", "}"],
173
182
  ["{", "}"],
183
+ ['"#{', '}"'],
174
184
  ["[", "]"]
175
185
  ].each do |open, close|
176
186
  reflect(open).level.should == 1
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dietrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eloy Duran
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-18 00:00:00 +01:00
12
+ date: 2010-02-24 00:00:00 +01:00
13
13
  default_executable: dietrb
14
14
  dependencies: []
15
15
 
@@ -34,9 +34,11 @@ files:
34
34
  - lib/irb/context.rb
35
35
  - lib/irb/ext/completion.rb
36
36
  - lib/irb/ext/macruby.rb
37
+ - lib/irb/formatter.rb
37
38
  - lib/irb/source.rb
38
39
  - spec/completion_spec.rb
39
40
  - spec/context_spec.rb
41
+ - spec/formatter_spec.rb
40
42
  - spec/irb_spec.rb
41
43
  - spec/source_spec.rb
42
44
  - spec/spec_helper.rb
@@ -71,6 +73,7 @@ summary: IRB on a diet, for MacRuby / Ruby 1.9
71
73
  test_files:
72
74
  - spec/completion_spec.rb
73
75
  - spec/context_spec.rb
76
+ - spec/formatter_spec.rb
74
77
  - spec/irb_spec.rb
75
78
  - spec/source_spec.rb
76
79
  - spec/spec_helper.rb