dietrb 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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