dietrb 0.1.2 → 0.2.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/LICENSE +3 -0
- data/README.rdoc +99 -0
- data/Rakefile +6 -1
- data/VERSION +1 -1
- data/bin/dietrb +1 -1
- data/dietrb.gemspec +10 -5
- data/lib/irb/context.rb +29 -5
- data/lib/irb/ext/completion.rb +140 -0
- data/lib/irb/source.rb +50 -7
- data/lib/irb.rb +3 -3
- data/spec/completion_spec.rb +221 -0
- data/spec/context_spec.rb +35 -14
- data/spec/irb_spec.rb +2 -2
- data/spec/source_spec.rb +50 -7
- metadata +9 -4
- data/README +0 -1
data/LICENSE
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
= IRB on a diet, for MacRuby / Ruby 1.9
|
2
|
+
|
3
|
+
The goal is to have a small and cleaned up version of IRB. Trimmed down to only
|
4
|
+
do the stuff I, and most people I know, actually use.
|
5
|
+
|
6
|
+
Trimming down the core code is done mainly by using Ripper, which comes with
|
7
|
+
Ruby 1.9, instead of shipping it's own parser etc.
|
8
|
+
|
9
|
+
There's still lots to be done, but the ‘basic functionality’ as is now, should
|
10
|
+
not grow too much more. For now my things to-do are .irbrc support, completion,
|
11
|
+
and investigate what else people really really need. After that it's time to
|
12
|
+
polish.
|
13
|
+
|
14
|
+
== Differences
|
15
|
+
|
16
|
+
* This IRB version specifically targets MacRuby, for now, and allows Cocoa
|
17
|
+
development to be done from the command-line. Dietrb will automatically
|
18
|
+
override the normal runloop to be ran in a thread and start a NSRunLoop on
|
19
|
+
the main thread.
|
20
|
+
|
21
|
+
* Dietrb will try to warn about syntax errors as soon as a line is entered and
|
22
|
+
only reset the buffer to the previous line. This means that you don't need to
|
23
|
+
loose any previous work:
|
24
|
+
|
25
|
+
IRB:
|
26
|
+
|
27
|
+
irb(main):001:0> class A
|
28
|
+
irb(main):002:1> def foo
|
29
|
+
irb(main):003:2> } p :ok
|
30
|
+
irb(main):004:1> end
|
31
|
+
SyntaxError: compile error
|
32
|
+
(irb):3: syntax error, unexpected '}'
|
33
|
+
} p :ok
|
34
|
+
^
|
35
|
+
(irb):4: syntax error, unexpected $end, expecting kEND
|
36
|
+
from (irb):4
|
37
|
+
from :0
|
38
|
+
irb(main):005:0> A.new.foo
|
39
|
+
NameError: uninitialized constant A
|
40
|
+
from (irb):5
|
41
|
+
from :0
|
42
|
+
|
43
|
+
Dietrb:
|
44
|
+
|
45
|
+
irb(main):001:0> class A
|
46
|
+
irb(main):002:1> def foo
|
47
|
+
irb(main):003:2> } p :ok
|
48
|
+
SyntaxError: compile error
|
49
|
+
(irb):3: syntax error, unexpected '}'
|
50
|
+
irb(main):004:2> p :ok
|
51
|
+
irb(main):005:2> end
|
52
|
+
irb(main):006:1> end
|
53
|
+
=> nil
|
54
|
+
irb(main):007:0> A.new.foo
|
55
|
+
:ok
|
56
|
+
=> :ok
|
57
|
+
|
58
|
+
== Play
|
59
|
+
|
60
|
+
Normal usage:
|
61
|
+
|
62
|
+
irb(main):001:0> class A
|
63
|
+
irb(main):002:1> def foo
|
64
|
+
irb(main):003:2> :ok
|
65
|
+
irb(main):004:2> end
|
66
|
+
irb(main):005:1> end
|
67
|
+
=> nil
|
68
|
+
irb(main):006:0> irb A.new
|
69
|
+
irb(#<#<Class:…>::A:…>):001:0> foo
|
70
|
+
=> :ok
|
71
|
+
irb(#<#<Class:…>::A:…>):002:0> quit
|
72
|
+
=> nil
|
73
|
+
irb(main):007:0> quit
|
74
|
+
|
75
|
+
Or on MacRuby, try:
|
76
|
+
|
77
|
+
irb(main):001:0> win = NSWindow.alloc.initWithContentRect([200, 300, 250, 100],
|
78
|
+
irb(main):002:0> styleMask: NSTitledWindowMask|NSResizableWindowMask,
|
79
|
+
irb(main):003:0> backing: NSBackingStoreBuffered,
|
80
|
+
irb(main):004:0> defer: false)
|
81
|
+
=> #<NSWindow:0x20023eb00>
|
82
|
+
irb(main):005:0> win.orderFrontRegardless
|
83
|
+
=> #<NSWindow:0x20023eb00>
|
84
|
+
irb(main):006:0> win.title = 'Hello World'
|
85
|
+
=> "Hello World"
|
86
|
+
irb(main):007:0> bye = NSButton.alloc.initWithFrame([10, 10, 80, 80])
|
87
|
+
=> #<NSButton:0x20027f820>
|
88
|
+
irb(main):008:0> win.contentView.addSubview(bye)
|
89
|
+
=> #<NSView:0x200210320>
|
90
|
+
irb(main):009:0> bye.bezelStyle = NSThickerSquareBezelStyle
|
91
|
+
=> 4
|
92
|
+
irb(main):010:0> bye.title = 'Goodbye!'
|
93
|
+
=> "Goodbye!"
|
94
|
+
irb(main):011:0> bye.target = NSApp
|
95
|
+
=> #<NSApplication:0x200257fe0>
|
96
|
+
irb(main):012:0> bye.action = 'terminate:'
|
97
|
+
=> "terminate:"
|
98
|
+
irb(main):013:0> bye.sound = NSSound.soundNamed('Basso')
|
99
|
+
=> #<NSSound:0x200248b20>
|
data/Rakefile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
task :default => :
|
1
|
+
task :default => :run
|
2
2
|
|
3
3
|
desc "Run the specs"
|
4
4
|
task :spec do
|
@@ -10,6 +10,11 @@ task :kick do
|
|
10
10
|
sh "kicker -e 'rake spec' lib spec"
|
11
11
|
end
|
12
12
|
|
13
|
+
desc "Run dietrb with ruby19"
|
14
|
+
task :run do
|
15
|
+
sh "ruby19 -Ilib ./bin/dietrb"
|
16
|
+
end
|
17
|
+
|
13
18
|
begin
|
14
19
|
require 'jeweler'
|
15
20
|
Jeweler::Tasks.new do |gemspec|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/bin/dietrb
CHANGED
data/dietrb.gemspec
CHANGED
@@ -5,29 +5,33 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{dietrb}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.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-
|
12
|
+
s.date = %q{2010-02-18}
|
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}
|
16
16
|
s.executables = ["dietrb"]
|
17
17
|
s.extra_rdoc_files = [
|
18
|
-
"
|
18
|
+
"LICENSE",
|
19
|
+
"README.rdoc"
|
19
20
|
]
|
20
21
|
s.files = [
|
21
22
|
".gitignore",
|
22
|
-
"
|
23
|
+
"LICENSE",
|
24
|
+
"README.rdoc",
|
23
25
|
"Rakefile",
|
24
26
|
"VERSION",
|
25
27
|
"bin/dietrb",
|
26
28
|
"dietrb.gemspec",
|
27
29
|
"lib/irb.rb",
|
28
30
|
"lib/irb/context.rb",
|
31
|
+
"lib/irb/ext/completion.rb",
|
29
32
|
"lib/irb/ext/macruby.rb",
|
30
33
|
"lib/irb/source.rb",
|
34
|
+
"spec/completion_spec.rb",
|
31
35
|
"spec/context_spec.rb",
|
32
36
|
"spec/irb_spec.rb",
|
33
37
|
"spec/source_spec.rb",
|
@@ -40,7 +44,8 @@ Gem::Specification.new do |s|
|
|
40
44
|
s.rubygems_version = %q{1.3.5}
|
41
45
|
s.summary = %q{IRB on a diet, for MacRuby / Ruby 1.9}
|
42
46
|
s.test_files = [
|
43
|
-
"spec/
|
47
|
+
"spec/completion_spec.rb",
|
48
|
+
"spec/context_spec.rb",
|
44
49
|
"spec/irb_spec.rb",
|
45
50
|
"spec/source_spec.rb",
|
46
51
|
"spec/spec_helper.rb"
|
data/lib/irb/context.rb
CHANGED
@@ -2,6 +2,17 @@ require 'readline'
|
|
2
2
|
|
3
3
|
module IRB
|
4
4
|
class Context
|
5
|
+
class << self
|
6
|
+
attr_accessor :current
|
7
|
+
|
8
|
+
def make_current(context)
|
9
|
+
before, @current = @current, context
|
10
|
+
yield
|
11
|
+
ensure
|
12
|
+
@current = before
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
5
16
|
attr_reader :object, :binding, :line, :source
|
6
17
|
|
7
18
|
def initialize(object)
|
@@ -11,8 +22,12 @@ module IRB
|
|
11
22
|
clear_buffer
|
12
23
|
end
|
13
24
|
|
25
|
+
def __evaluate__(source)
|
26
|
+
eval(source, @binding)
|
27
|
+
end
|
28
|
+
|
14
29
|
def evaluate(source)
|
15
|
-
result =
|
30
|
+
result = __evaluate__("_ = (#{source})")
|
16
31
|
puts format_result(result)
|
17
32
|
result
|
18
33
|
rescue Exception => e
|
@@ -24,9 +39,11 @@ module IRB
|
|
24
39
|
end
|
25
40
|
|
26
41
|
def run
|
27
|
-
|
28
|
-
|
29
|
-
|
42
|
+
self.class.make_current(self) do
|
43
|
+
while line = readline
|
44
|
+
continue = process_line(line)
|
45
|
+
break unless continue
|
46
|
+
end
|
30
47
|
end
|
31
48
|
end
|
32
49
|
|
@@ -46,7 +63,10 @@ module IRB
|
|
46
63
|
@source << line
|
47
64
|
return false if @source.to_s == "quit"
|
48
65
|
|
49
|
-
if @source.
|
66
|
+
if @source.syntax_error?
|
67
|
+
puts format_syntax_error(@source.syntax_error)
|
68
|
+
@source.pop
|
69
|
+
elsif @source.code_block?
|
50
70
|
evaluate(@source)
|
51
71
|
clear_buffer
|
52
72
|
end
|
@@ -69,6 +89,10 @@ module IRB
|
|
69
89
|
"#{e.class.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
70
90
|
end
|
71
91
|
|
92
|
+
def format_syntax_error(e)
|
93
|
+
"SyntaxError: compile error\n(irb):#{@line}: #{e}"
|
94
|
+
end
|
95
|
+
|
72
96
|
private
|
73
97
|
|
74
98
|
def clear_buffer
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'ripper'
|
2
|
+
|
3
|
+
module IRB
|
4
|
+
class Completion
|
5
|
+
# Convenience constants for sexp access of Ripper::SexpBuilder.
|
6
|
+
TYPE = 0
|
7
|
+
VALUE = 1
|
8
|
+
CALLEE = 3
|
9
|
+
|
10
|
+
# Returns an array of possible completion results, with the current
|
11
|
+
# IRB::Context.
|
12
|
+
#
|
13
|
+
# This is meant to be used with Readline which takes a completion proc.
|
14
|
+
def self.call(source)
|
15
|
+
new(IRB::Context.current, source).results
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :context, :source
|
19
|
+
|
20
|
+
def initialize(context, source)
|
21
|
+
@context, @source = context, source
|
22
|
+
end
|
23
|
+
|
24
|
+
def evaluate(s)
|
25
|
+
@context.__evaluate__(s)
|
26
|
+
end
|
27
|
+
|
28
|
+
def local_variables
|
29
|
+
evaluate('local_variables').map(&:to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
def instance_methods
|
33
|
+
@context.object.methods.map(&:to_s)
|
34
|
+
end
|
35
|
+
|
36
|
+
# TODO: test and or fix the fact that we need to get constants from the
|
37
|
+
# singleton class.
|
38
|
+
def constants
|
39
|
+
evaluate('Object.constants + self.class.constants + (class << self; constants; end)').map(&:to_s)
|
40
|
+
end
|
41
|
+
|
42
|
+
def results
|
43
|
+
source = @source
|
44
|
+
filter = nil
|
45
|
+
|
46
|
+
# if ends with period, remove it to remove the syntax error it causes
|
47
|
+
call = (source[-1,1] == '.')
|
48
|
+
receiver = source = source[0..-2] if call
|
49
|
+
|
50
|
+
if sexp = Ripper::SexpBuilder.new(source).parse
|
51
|
+
# [:program, [:stmts_add, [:stmts_new], [x, …]]]
|
52
|
+
# ^
|
53
|
+
root = sexp[1][2]
|
54
|
+
|
55
|
+
# [:call, [:hash, nil], :".", [:@ident, x, …]]
|
56
|
+
if root[TYPE] == :call
|
57
|
+
call = true
|
58
|
+
filter = root[CALLEE][VALUE]
|
59
|
+
receiver = source[0..-(filter.length + 2)]
|
60
|
+
root = root[VALUE]
|
61
|
+
end
|
62
|
+
|
63
|
+
if call
|
64
|
+
format_methods(receiver, methods_of_object(root), filter)
|
65
|
+
else
|
66
|
+
match_methods_vars_or_consts_in_scope(root)
|
67
|
+
end.sort
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def match_methods_vars_or_consts_in_scope(symbol)
|
72
|
+
var = symbol[VALUE]
|
73
|
+
filter = var[VALUE]
|
74
|
+
case var[TYPE]
|
75
|
+
when :@ident
|
76
|
+
local_variables + instance_methods
|
77
|
+
when :@gvar
|
78
|
+
global_variables.map(&:to_s)
|
79
|
+
when :@const
|
80
|
+
if symbol[TYPE] == :top_const_ref
|
81
|
+
filter = "::#{filter}"
|
82
|
+
Object.constants.map { |c| "::#{c}" }
|
83
|
+
else
|
84
|
+
constants
|
85
|
+
end
|
86
|
+
end.grep(/^#{Regexp.quote(filter)}/)
|
87
|
+
end
|
88
|
+
|
89
|
+
def format_methods(receiver, methods, filter)
|
90
|
+
(filter ? methods.grep(/^#{filter}/) : methods).map { |m| "#{receiver}.#{m}" }
|
91
|
+
end
|
92
|
+
|
93
|
+
def methods_of_object(root)
|
94
|
+
result = case root[TYPE]
|
95
|
+
# [:unary, :-@, [x, …]]
|
96
|
+
# ^
|
97
|
+
when :unary then return methods_of_object(root[2]) # TODO: do we really need this?
|
98
|
+
when :var_ref, :top_const_ref then return methods_of_object_in_variable(root)
|
99
|
+
when :array, :words_add, :qwords_add then Array
|
100
|
+
when :@int then Fixnum
|
101
|
+
when :@float then Float
|
102
|
+
when :hash then Hash
|
103
|
+
when :lambda then Proc
|
104
|
+
when :dot2, :dot3 then Range
|
105
|
+
when :regexp_literal then Regexp
|
106
|
+
when :string_literal then String
|
107
|
+
when :symbol_literal, :dyna_symbol then Symbol
|
108
|
+
end.instance_methods
|
109
|
+
end
|
110
|
+
|
111
|
+
def methods_of_object_in_variable(var)
|
112
|
+
subtype, name = var[VALUE][0..1]
|
113
|
+
|
114
|
+
if var[TYPE] == :top_const_ref
|
115
|
+
if subtype == :@const && Object.constants.include?(name.to_sym)
|
116
|
+
evaluate("::#{name}").methods
|
117
|
+
end
|
118
|
+
else
|
119
|
+
case subtype
|
120
|
+
when :@ident
|
121
|
+
evaluate(name).methods if local_variables.include?(name)
|
122
|
+
when :@gvar
|
123
|
+
eval(name).methods if global_variables.include?(name.to_sym)
|
124
|
+
when :@const
|
125
|
+
evaluate(name).methods if constants.include?(name)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
if defined?(Readline)
|
133
|
+
if Readline.respond_to?("basic_word_break_characters=")
|
134
|
+
# IRB adds a few breaking chars. that would break literals for us:
|
135
|
+
# * String: " and '
|
136
|
+
# * Hash: = and >
|
137
|
+
Readline.basic_word_break_characters= " \t\n`<;|&("
|
138
|
+
end
|
139
|
+
Readline.completion_proc = IRB::Completion
|
140
|
+
end
|
data/lib/irb/source.rb
CHANGED
@@ -14,6 +14,12 @@ module IRB
|
|
14
14
|
@buffer << source.chomp
|
15
15
|
end
|
16
16
|
|
17
|
+
# Removes the last line from the buffer and flushes the cached reflection.
|
18
|
+
def pop
|
19
|
+
@reflection = nil
|
20
|
+
@buffer.pop
|
21
|
+
end
|
22
|
+
|
17
23
|
# Returns the accumulated source as a string, joined by newlines.
|
18
24
|
def source
|
19
25
|
@buffer.join("\n")
|
@@ -28,8 +34,19 @@ module IRB
|
|
28
34
|
end
|
29
35
|
|
30
36
|
# Reflects on the accumulated source to see if it's a valid code block.
|
31
|
-
def
|
32
|
-
reflect.
|
37
|
+
def code_block?
|
38
|
+
reflect.code_block?
|
39
|
+
end
|
40
|
+
|
41
|
+
# Reflects on the accumulated source to see if it contains a syntax error.
|
42
|
+
def syntax_error?
|
43
|
+
reflect.syntax_error?
|
44
|
+
end
|
45
|
+
|
46
|
+
# Reflects on the accumulated source and returns the actual syntax error
|
47
|
+
# message if one occurs.
|
48
|
+
def syntax_error
|
49
|
+
reflect.syntax_error
|
33
50
|
end
|
34
51
|
|
35
52
|
# Returns a Reflector for the accumulated source and caches it.
|
@@ -37,11 +54,11 @@ module IRB
|
|
37
54
|
@reflection ||= Reflector.new(source)
|
38
55
|
end
|
39
56
|
|
40
|
-
class Reflector < Ripper
|
57
|
+
class Reflector < Ripper
|
41
58
|
def initialize(source)
|
42
59
|
super
|
43
60
|
@level = 0
|
44
|
-
|
61
|
+
parse
|
45
62
|
end
|
46
63
|
|
47
64
|
# Returns the code block indentation level.
|
@@ -53,8 +70,12 @@ module IRB
|
|
53
70
|
# Reflector.new("class Foo; def foo; end; end").level # => 0
|
54
71
|
attr_reader :level
|
55
72
|
|
73
|
+
# Returns the actual syntax error message if one occurs.
|
74
|
+
attr_reader :syntax_error
|
75
|
+
|
56
76
|
# Returns whether or not the source is a valid code block, but does not
|
57
|
-
# take syntax errors into account.
|
77
|
+
# take syntax errors into account. In short, it's a valid code block if
|
78
|
+
# after parsing the level is at zero.
|
58
79
|
#
|
59
80
|
# For example, this is not a valid full code block:
|
60
81
|
#
|
@@ -63,8 +84,30 @@ module IRB
|
|
63
84
|
# This however is:
|
64
85
|
#
|
65
86
|
# def foo; p :ok; end
|
66
|
-
def
|
67
|
-
@
|
87
|
+
def code_block?
|
88
|
+
@level == 0
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns whether or not the source contains a syntax error. However, it
|
92
|
+
# ignores a syntax error resulting in a code block not ending yet.
|
93
|
+
#
|
94
|
+
# For example, this contains a syntax error:
|
95
|
+
#
|
96
|
+
# def; foo
|
97
|
+
#
|
98
|
+
# This does not:
|
99
|
+
#
|
100
|
+
# def foo
|
101
|
+
def syntax_error?
|
102
|
+
!@syntax_error.nil?
|
103
|
+
end
|
104
|
+
|
105
|
+
UNEXPECTED_END = "syntax error, unexpected $end"
|
106
|
+
|
107
|
+
def on_parse_error(error) #:nodoc:
|
108
|
+
if code_block? || !error.start_with?(UNEXPECTED_END)
|
109
|
+
@syntax_error = error
|
110
|
+
end
|
68
111
|
end
|
69
112
|
|
70
113
|
INCREASE_LEVEL_KEYWORDS = %w{ class module def begin if unless case while for do }
|
data/lib/irb.rb
CHANGED
@@ -0,0 +1,221 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'irb/ext/completion'
|
3
|
+
|
4
|
+
module CompletionHelper
|
5
|
+
def complete(str)
|
6
|
+
IRB::Completion.new(@context, str).results
|
7
|
+
end
|
8
|
+
|
9
|
+
def imethods(klass, receiver = nil)
|
10
|
+
klass.instance_methods.map { |m| [receiver, m.to_s].compact.join('.') }.sort
|
11
|
+
end
|
12
|
+
|
13
|
+
def methods(object, receiver = nil)
|
14
|
+
object.methods.map { |m| [receiver, m.to_s].compact.join('.') }.sort
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Bacon::Context.send(:include, CompletionHelper)
|
19
|
+
|
20
|
+
class CompletionStub
|
21
|
+
def self.a_cmethod
|
22
|
+
end
|
23
|
+
|
24
|
+
def an_imethod
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Playground
|
29
|
+
CompletionStub = Object.new
|
30
|
+
def CompletionStub.a_singleton_method; end
|
31
|
+
|
32
|
+
def a_local_method; end
|
33
|
+
end
|
34
|
+
|
35
|
+
$a_completion_stub = CompletionStub.new
|
36
|
+
|
37
|
+
describe "IRB::Completion" do
|
38
|
+
before do
|
39
|
+
@context = IRB::Context.new(Playground.new)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "quacks like a Proc" do
|
43
|
+
IRB::Completion.call('//.').should == imethods(Regexp, '//')
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "when doing a method call on an explicit receiver," do
|
47
|
+
describe "and the source ends with a period," do
|
48
|
+
describe "returns *all* public methods of the receiver that" do
|
49
|
+
it "matches as a local variable" do
|
50
|
+
@context.__evaluate__('foo = ::CompletionStub.new')
|
51
|
+
complete('foo.').should == imethods(::CompletionStub, 'foo')
|
52
|
+
|
53
|
+
@context.__evaluate__('def foo.singleton_method; end')
|
54
|
+
complete('foo.').should.include('foo.singleton_method')
|
55
|
+
end
|
56
|
+
|
57
|
+
it "matches as a global variable" do
|
58
|
+
complete('$a_completion_stub.').should == imethods(::CompletionStub, '$a_completion_stub')
|
59
|
+
end
|
60
|
+
|
61
|
+
# TODO: fix
|
62
|
+
# it "matches as a local constant" do
|
63
|
+
# complete('CompletionStub.').should == methods(Playground::CompletionStub)
|
64
|
+
# end
|
65
|
+
|
66
|
+
it "matches as a top level constant" do
|
67
|
+
complete('::CompletionStub.').should == methods(::CompletionStub, '::CompletionStub')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "returns *all* public instance methods of the class (the receiver) that" do
|
72
|
+
it "matches as a Regexp literal" do
|
73
|
+
complete('//.').should == imethods(Regexp, '//')
|
74
|
+
complete('/^(:[^:.]+)\.([^.]*)$/.').should == imethods(Regexp, '/^(:[^:.]+)\.([^.]*)$/')
|
75
|
+
complete('/^(#{oops})\.([^.]*)$/.').should == imethods(Regexp, '/^(#{oops})\.([^.]*)$/')
|
76
|
+
complete('%r{/foo/.*/bar}.').should == imethods(Regexp, '%r{/foo/.*/bar}')
|
77
|
+
end
|
78
|
+
|
79
|
+
it "matches as an Array literal" do
|
80
|
+
complete('[].').should == imethods(Array, '[]')
|
81
|
+
complete('[:ok, {}, "foo",].').should == imethods(Array, '[:ok, {}, "foo",]')
|
82
|
+
complete('[*foo].').should == imethods(Array, '[*foo]')
|
83
|
+
complete('%w{foo}.').should == imethods(Array, '%w{foo}')
|
84
|
+
complete('%W{#{:foo}}.').should == imethods(Array, '%W{#{:foo}}')
|
85
|
+
end
|
86
|
+
|
87
|
+
# fails on MacRuby
|
88
|
+
it "matches as a lambda literal" do
|
89
|
+
complete('->{}.').should == imethods(Proc, '->{}')
|
90
|
+
complete('->{x=:ok}.').should == imethods(Proc, '->{x=:ok}')
|
91
|
+
complete('->(x){x=:ok}.').should == imethods(Proc, '->(x){x=:ok}')
|
92
|
+
end
|
93
|
+
|
94
|
+
it "matches as a Hash literal" do
|
95
|
+
complete('{}.').should == imethods(Hash, '{}')
|
96
|
+
complete('{:foo=>:bar,}.').should == imethods(Hash, '{:foo=>:bar,}')
|
97
|
+
complete('{foo:"bar"}.').should == imethods(Hash, '{foo:"bar"}')
|
98
|
+
end
|
99
|
+
|
100
|
+
it "matches as a Symbol literal" do
|
101
|
+
complete(':foo.').should == imethods(Symbol, ':foo')
|
102
|
+
complete(':"foo.bar".').should == imethods(Symbol, ':"foo.bar"')
|
103
|
+
complete(':"foo.#{"bar"}".').should == imethods(Symbol, ':"foo.#{"bar"}"')
|
104
|
+
complete(':\'foo.#{"bar"}\'.').should == imethods(Symbol, ':\'foo.#{"bar"}\'')
|
105
|
+
complete('%s{foo.bar}.').should == imethods(Symbol, '%s{foo.bar}')
|
106
|
+
end
|
107
|
+
|
108
|
+
it "matches as a String literal" do
|
109
|
+
complete("'foo\\'bar'.").should == imethods(String, "'foo\\'bar'")
|
110
|
+
complete('"foo\"bar".').should == imethods(String, '"foo\"bar"')
|
111
|
+
complete('"foo#{"bar"}".').should == imethods(String, '"foo#{"bar"}"')
|
112
|
+
complete('%{foobar}.').should == imethods(String, '%{foobar}')
|
113
|
+
complete('%q{foo#{:bar}}.').should == imethods(String, '%q{foo#{:bar}}')
|
114
|
+
complete('%Q{foo#{:bar}}.').should == imethods(String, '%Q{foo#{:bar}}')
|
115
|
+
end
|
116
|
+
|
117
|
+
it "matches as a Range literal" do
|
118
|
+
complete('1..10.').should == imethods(Range, '1..10')
|
119
|
+
complete('1...10.').should == imethods(Range, '1...10')
|
120
|
+
complete('"a".."z".').should == imethods(Range, '"a".."z"')
|
121
|
+
complete('"a"..."z".').should == imethods(Range, '"a"..."z"')
|
122
|
+
end
|
123
|
+
|
124
|
+
it "matches as a Fixnum literal" do
|
125
|
+
complete('42.').should == imethods(Fixnum, '42')
|
126
|
+
complete('+42.').should == imethods(Fixnum, '+42')
|
127
|
+
complete('-42.').should == imethods(Fixnum, '-42')
|
128
|
+
complete('42_000.').should == imethods(Fixnum, '42_000')
|
129
|
+
end
|
130
|
+
|
131
|
+
it "matches as a Bignum literal as a Fixnum" do
|
132
|
+
complete('100_000_000_000_000_000_000.').should == imethods(Fixnum, '100_000_000_000_000_000_000')
|
133
|
+
complete('-100_000_000_000_000_000_000.').should == imethods(Fixnum, '-100_000_000_000_000_000_000')
|
134
|
+
complete('+100_000_000_000_000_000_000.').should == imethods(Fixnum, '+100_000_000_000_000_000_000')
|
135
|
+
end
|
136
|
+
|
137
|
+
it "matches as a Float with exponential literal" do
|
138
|
+
complete('1.2e-3.').should == imethods(Float, '1.2e-3')
|
139
|
+
complete('+1.2e-3.').should == imethods(Float, '+1.2e-3')
|
140
|
+
complete('-1.2e-3.').should == imethods(Float, '-1.2e-3')
|
141
|
+
end
|
142
|
+
|
143
|
+
it "matches as a hex literal as a Fixnum" do
|
144
|
+
complete('0xffff.').should == imethods(Fixnum, '0xffff')
|
145
|
+
complete('+0xffff.').should == imethods(Fixnum, '+0xffff')
|
146
|
+
complete('-0xffff.').should == imethods(Fixnum, '-0xffff')
|
147
|
+
end
|
148
|
+
|
149
|
+
it "matches as a binary literal as a Fixnum" do
|
150
|
+
complete('0b01011.').should == imethods(Fixnum, '0b01011')
|
151
|
+
complete('-0b01011.').should == imethods(Fixnum, '-0b01011')
|
152
|
+
complete('+0b01011.').should == imethods(Fixnum, '+0b01011')
|
153
|
+
end
|
154
|
+
|
155
|
+
it "matches as an octal literal as a Fixnum" do
|
156
|
+
complete('0377.').should == imethods(Fixnum, '0377')
|
157
|
+
complete('-0377.').should == imethods(Fixnum, '-0377')
|
158
|
+
complete('+0377.').should == imethods(Fixnum, '+0377')
|
159
|
+
end
|
160
|
+
|
161
|
+
it "matches as a Float literal" do
|
162
|
+
complete('42.0.').should == imethods(Float, '42.0')
|
163
|
+
complete('-42.0.').should == imethods(Float, '-42.0')
|
164
|
+
complete('+42.0.').should == imethods(Float, '+42.0')
|
165
|
+
complete('42_000.0.').should == imethods(Float, '42_000.0')
|
166
|
+
end
|
167
|
+
|
168
|
+
it "matches as a Bignum float literal as a Float" do
|
169
|
+
complete('100_000_000_000_000_000_000.0.').should == imethods(Float, '100_000_000_000_000_000_000.0')
|
170
|
+
complete('+100_000_000_000_000_000_000.0.').should == imethods(Float, '+100_000_000_000_000_000_000.0')
|
171
|
+
complete('-100_000_000_000_000_000_000.0.').should == imethods(Float, '-100_000_000_000_000_000_000.0')
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "and the source does *not* end with a period," do
|
177
|
+
it "filters the methods, of the literal receiver, by the given method name" do
|
178
|
+
complete('//.nam').should == %w{ //.named_captures //.names }
|
179
|
+
complete('//.named').should == %w{ //.named_captures }
|
180
|
+
end
|
181
|
+
|
182
|
+
it "filters the methods, of the variable receiver, by the given method name" do
|
183
|
+
@context.__evaluate__('foo = ::CompletionStub.new')
|
184
|
+
complete('foo.an_im').should == %w{ foo.an_imethod }
|
185
|
+
complete('$a_completion_stub.an_im').should == %w{ $a_completion_stub.an_imethod }
|
186
|
+
# TODO: fix
|
187
|
+
# complete('CompletionStub.a_sing').should == %w{ CompletionStub.a_singleton_method }
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe "when *not* doing a method call on an explicit receiver" do
|
193
|
+
before do
|
194
|
+
@context.__evaluate__("a_local_variable = :ok")
|
195
|
+
end
|
196
|
+
|
197
|
+
it "matches local variables" do
|
198
|
+
complete("a_local_v").should == %w{ a_local_variable }
|
199
|
+
end
|
200
|
+
|
201
|
+
it "matches instance methods of the context object" do
|
202
|
+
complete("a_local_m").should == %w{ a_local_method }
|
203
|
+
end
|
204
|
+
|
205
|
+
it "matches local variables and instance method of the context object" do
|
206
|
+
complete("a_loc").should == %w{ a_local_method a_local_variable }
|
207
|
+
end
|
208
|
+
|
209
|
+
it "matches global variables" do
|
210
|
+
complete("$a_completion_s").should == %w{ $a_completion_stub }
|
211
|
+
end
|
212
|
+
|
213
|
+
it "matches constants" do
|
214
|
+
complete("Playgr").should == %w{ Playground }
|
215
|
+
end
|
216
|
+
|
217
|
+
it "matches top level constants" do
|
218
|
+
complete("::CompletionSt").should == %w{ ::CompletionStub }
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
data/spec/context_spec.rb
CHANGED
@@ -1,6 +1,19 @@
|
|
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
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
4
17
|
main = self
|
5
18
|
|
6
19
|
describe "IRB::Context" do
|
@@ -45,6 +58,16 @@ describe "IRB::Context" do
|
|
45
58
|
@context.format_exception(exception).should ==
|
46
59
|
"NameError: uninitialized constant Bacon::Context::DoesNotExist\n\t#{exception.backtrace.join("\n\t")}"
|
47
60
|
end
|
61
|
+
|
62
|
+
it "makes itself the current running context during the runloop and resigns once it's done" do
|
63
|
+
IRB::Context.current.should == nil
|
64
|
+
|
65
|
+
Readline.stub_input("current_during_run = IRB::Context.current")
|
66
|
+
@context.run
|
67
|
+
eval('current_during_run', @context.binding).should == @context
|
68
|
+
|
69
|
+
IRB::Context.current.should == nil
|
70
|
+
end
|
48
71
|
end
|
49
72
|
|
50
73
|
describe "IRB::Context, when evaluating source" do
|
@@ -54,7 +77,7 @@ describe "IRB::Context, when evaluating source" do
|
|
54
77
|
end
|
55
78
|
|
56
79
|
it "evaluates code with the object's binding" do
|
57
|
-
@context.
|
80
|
+
@context.__evaluate__("self").should == main
|
58
81
|
end
|
59
82
|
|
60
83
|
it "prints the result" do
|
@@ -89,19 +112,6 @@ describe "IRB::Context, when evaluating source" do
|
|
89
112
|
end
|
90
113
|
end
|
91
114
|
|
92
|
-
class << Readline
|
93
|
-
attr_reader :received
|
94
|
-
|
95
|
-
def stub_input(*input)
|
96
|
-
@input = input
|
97
|
-
end
|
98
|
-
|
99
|
-
def readline(prompt, history)
|
100
|
-
@received = [prompt, history]
|
101
|
-
@input.shift
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
115
|
describe "IRB::Context, when receiving input" do
|
106
116
|
before do
|
107
117
|
@context = IRB::Context.new(main)
|
@@ -145,6 +155,17 @@ describe "IRB::Context, when receiving input" do
|
|
145
155
|
source.to_s.should == "def foo\n:ok\nend; p foo"
|
146
156
|
end
|
147
157
|
|
158
|
+
it "prints that a syntax error occurred on the last line and reset the buffer to the previous line" do
|
159
|
+
def @context.puts(str); @printed = str; end
|
160
|
+
|
161
|
+
@context.process_line("def foo")
|
162
|
+
@context.process_line(" };")
|
163
|
+
|
164
|
+
@context.source.to_s.should == "def foo"
|
165
|
+
printed = @context.instance_variable_get(:@printed)
|
166
|
+
printed.should == "SyntaxError: compile error\n(irb):2: syntax error, unexpected '}'"
|
167
|
+
end
|
168
|
+
|
148
169
|
it "returns whether or not the runloop should continue, but only if the level is 0" do
|
149
170
|
@context.process_line("def foo").should == true
|
150
171
|
@context.process_line("quit").should == true
|
data/spec/irb_spec.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require File.expand_path('../spec_helper', __FILE__)
|
2
2
|
|
3
|
-
describe "Kernel::
|
3
|
+
describe "Kernel::irb" do
|
4
4
|
it "creates a new context for the given object and runs it" do
|
5
5
|
Readline.stub_input("::IRBRan = self")
|
6
6
|
o = Object.new
|
7
|
-
|
7
|
+
irb(o)
|
8
8
|
IRBRan.should == o
|
9
9
|
end
|
10
10
|
end
|
data/spec/source_spec.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
require File.expand_path('../spec_helper', __FILE__)
|
2
2
|
|
3
|
+
class Should
|
4
|
+
alias have be
|
5
|
+
end
|
6
|
+
|
3
7
|
describe "IRB::Source" do
|
4
8
|
before do
|
5
9
|
@source = IRB::Source.new
|
@@ -15,6 +19,13 @@ describe "IRB::Source" do
|
|
15
19
|
@source.buffer.should == %w{ foo bar }
|
16
20
|
end
|
17
21
|
|
22
|
+
it "removes the last line from the buffer" do
|
23
|
+
@source << "foo\n"
|
24
|
+
@source << "bar\r\n"
|
25
|
+
@source.pop.should == "bar"
|
26
|
+
@source.buffer.should == %w{ foo }
|
27
|
+
end
|
28
|
+
|
18
29
|
it "returns the full buffered source, joined by newlines" do
|
19
30
|
@source.source.should == ""
|
20
31
|
@source << "foo\n"
|
@@ -34,7 +45,7 @@ describe "IRB::Source" do
|
|
34
45
|
["def foo", "p :ok", "end"],
|
35
46
|
["class A; def", "foo(x); p x", "end; end"]
|
36
47
|
].each do |buffer|
|
37
|
-
IRB::Source.new(buffer).should.be.
|
48
|
+
IRB::Source.new(buffer).should.be.code_block
|
38
49
|
end
|
39
50
|
end
|
40
51
|
|
@@ -43,10 +54,18 @@ describe "IRB::Source" do
|
|
43
54
|
["def foo", "p :ok"],
|
44
55
|
["class A; def", "foo(x); p x", "end"]
|
45
56
|
].each do |buffer|
|
46
|
-
IRB::Source.new(buffer).should.not.be.
|
57
|
+
IRB::Source.new(buffer).should.not.be.code_block
|
47
58
|
end
|
48
59
|
end
|
49
60
|
|
61
|
+
it "returns whether or not the accumulated source contains a syntax error" do
|
62
|
+
@source.should.not.have.syntax_error
|
63
|
+
@source << "def foo"
|
64
|
+
@source.should.not.have.syntax_error
|
65
|
+
@source << " def;"
|
66
|
+
@source.should.have.syntax_error
|
67
|
+
end
|
68
|
+
|
50
69
|
it "returns the current code block indentation level" do
|
51
70
|
@source.level.should == 0
|
52
71
|
@source << "class A"
|
@@ -71,14 +90,23 @@ describe "IRB::Source" do
|
|
71
90
|
@source << "def foo"
|
72
91
|
reflection = @source.reflect
|
73
92
|
@source.level
|
74
|
-
@source.
|
93
|
+
@source.code_block?
|
75
94
|
@source.reflect.should == reflection
|
76
95
|
|
77
96
|
@source << "end"
|
78
97
|
@source.level
|
79
98
|
new_reflection = @source.reflect
|
80
99
|
new_reflection.should.not == reflection
|
81
|
-
@source.
|
100
|
+
@source.code_block?
|
101
|
+
@source.reflect.should == new_reflection
|
102
|
+
|
103
|
+
reflection = new_reflection
|
104
|
+
|
105
|
+
@source.pop
|
106
|
+
@source.level
|
107
|
+
new_reflection = @source.reflect
|
108
|
+
new_reflection.should.not == reflection
|
109
|
+
@source.syntax_error?
|
82
110
|
@source.reflect.should == new_reflection
|
83
111
|
end
|
84
112
|
end
|
@@ -89,9 +117,24 @@ describe "IRB::Source::Reflector" do
|
|
89
117
|
end
|
90
118
|
|
91
119
|
it "returns whether or not the source is a valid code block" do
|
92
|
-
reflect("def foo").should.not.be.
|
93
|
-
reflect("def foo; p :ok").should.not.be.
|
94
|
-
reflect("def foo; p :ok; end").should.be.
|
120
|
+
reflect("def foo").should.not.be.code_block
|
121
|
+
reflect("def foo; p :ok").should.not.be.code_block
|
122
|
+
reflect("def foo; p :ok; end").should.be.code_block
|
123
|
+
end
|
124
|
+
|
125
|
+
it "returns whether or not the source contains a syntax error, except a code block not ending" do
|
126
|
+
reflect("def;").should.have.syntax_error
|
127
|
+
reflect("def;").should.have.syntax_error
|
128
|
+
reflect("def foo").should.not.have.syntax_error
|
129
|
+
reflect("class A; }").should.have.syntax_error
|
130
|
+
reflect("class A; {" ).should.not.have.syntax_error
|
131
|
+
reflect("class A def foo").should.have.syntax_error
|
132
|
+
reflect("class A; def foo" ).should.not.have.syntax_error
|
133
|
+
end
|
134
|
+
|
135
|
+
it "returns the actual syntax error message if one occurs" do
|
136
|
+
reflect("def foo").syntax_error.should == nil
|
137
|
+
reflect("}").syntax_error.should == "syntax error, unexpected '}'"
|
95
138
|
end
|
96
139
|
|
97
140
|
it "returns the code block indentation level" do
|
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.
|
4
|
+
version: 0.2.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-
|
12
|
+
date: 2010-02-18 00:00:00 +01:00
|
13
13
|
default_executable: dietrb
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -20,18 +20,22 @@ executables:
|
|
20
20
|
extensions: []
|
21
21
|
|
22
22
|
extra_rdoc_files:
|
23
|
-
-
|
23
|
+
- LICENSE
|
24
|
+
- README.rdoc
|
24
25
|
files:
|
25
26
|
- .gitignore
|
26
|
-
-
|
27
|
+
- LICENSE
|
28
|
+
- README.rdoc
|
27
29
|
- Rakefile
|
28
30
|
- VERSION
|
29
31
|
- bin/dietrb
|
30
32
|
- dietrb.gemspec
|
31
33
|
- lib/irb.rb
|
32
34
|
- lib/irb/context.rb
|
35
|
+
- lib/irb/ext/completion.rb
|
33
36
|
- lib/irb/ext/macruby.rb
|
34
37
|
- lib/irb/source.rb
|
38
|
+
- spec/completion_spec.rb
|
35
39
|
- spec/context_spec.rb
|
36
40
|
- spec/irb_spec.rb
|
37
41
|
- spec/source_spec.rb
|
@@ -65,6 +69,7 @@ signing_key:
|
|
65
69
|
specification_version: 3
|
66
70
|
summary: IRB on a diet, for MacRuby / Ruby 1.9
|
67
71
|
test_files:
|
72
|
+
- spec/completion_spec.rb
|
68
73
|
- spec/context_spec.rb
|
69
74
|
- spec/irb_spec.rb
|
70
75
|
- spec/source_spec.rb
|
data/README
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
IRB on a diet, for MacRuby / Ruby 1.9
|