irb 1.6.4 → 1.8.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/lib/irb/locale.rb CHANGED
@@ -15,7 +15,11 @@ module IRB # :nodoc:
15
15
  ]x
16
16
  LOCALE_DIR = "/lc/"
17
17
 
18
- @@legacy_encoding_alias_map = {}.freeze
18
+ LEGACY_ENCODING_ALIAS_MAP = {
19
+ 'ujis' => Encoding::EUC_JP,
20
+ 'euc' => Encoding::EUC_JP
21
+ }
22
+
19
23
  @@loaded = []
20
24
 
21
25
  def initialize(locale = nil)
@@ -26,11 +30,11 @@ module IRB # :nodoc:
26
30
  @lang, @territory, @encoding_name, @modifier = m[:language], m[:territory], m[:codeset], m[:modifier]
27
31
 
28
32
  if @encoding_name
29
- begin load 'irb/encoding_aliases.rb'; rescue LoadError; end
30
- if @encoding = @@legacy_encoding_alias_map[@encoding_name]
33
+ if @encoding = LEGACY_ENCODING_ALIAS_MAP[@encoding_name]
31
34
  warn(("%s is obsolete. use %s" % ["#{@lang}_#{@territory}.#{@encoding_name}", "#{@lang}_#{@territory}.#{@encoding.name}"]), uplevel: 1)
35
+ else
36
+ @encoding = Encoding.find(@encoding_name) rescue nil
32
37
  end
33
- @encoding = Encoding.find(@encoding_name) rescue nil
34
38
  end
35
39
  end
36
40
  @encoding ||= (Encoding.find('locale') rescue Encoding::ASCII_8BIT)
@@ -78,39 +82,12 @@ module IRB # :nodoc:
78
82
  super(*ary)
79
83
  end
80
84
 
81
- def require(file, priv = nil)
82
- rex = Regexp.new("lc/#{Regexp.quote(file)}\.(so|o|sl|rb)?")
83
- return false if $".find{|f| f =~ rex}
84
-
85
- case file
86
- when /\.rb$/
87
- begin
88
- load(file, priv)
89
- $".push file
90
- return true
91
- rescue LoadError
92
- end
93
- when /\.(so|o|sl)$/
94
- return super
95
- end
96
-
97
- begin
98
- load(f = file + ".rb")
99
- $".push f #"
100
- return true
101
- rescue LoadError
102
- return ruby_require(file)
103
- end
104
- end
105
-
106
- alias toplevel_load load
107
-
108
- def load(file, priv=nil)
85
+ def load(file)
109
86
  found = find(file)
110
87
  if found
111
88
  unless @@loaded.include?(found)
112
89
  @@loaded << found # cache
113
- return real_load(found, priv)
90
+ Kernel.load(found)
114
91
  end
115
92
  else
116
93
  raise LoadError, "No such file to load -- #{file}"
@@ -129,16 +106,6 @@ module IRB # :nodoc:
129
106
  end
130
107
  end
131
108
 
132
- private
133
- def real_load(path, priv)
134
- src = MagicFile.open(path){|f| f.read}
135
- if priv
136
- eval("self", TOPLEVEL_BINDING).extend(Module.new {eval(src, nil, path)})
137
- else
138
- eval(src, TOPLEVEL_BINDING, path)
139
- end
140
- end
141
-
142
109
  # @param paths load paths in which IRB find a localized file.
143
110
  # @param dir directory
144
111
  # @param file basename to be localized
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+ module IRB
3
+ module NestingParser
4
+ IGNORE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end]
5
+
6
+ # Scan each token and call the given block with array of token and other information for parsing
7
+ def self.scan_opens(tokens)
8
+ opens = []
9
+ pending_heredocs = []
10
+ first_token_on_line = true
11
+ tokens.each do |t|
12
+ skip = false
13
+ last_tok, state, args = opens.last
14
+ case state
15
+ when :in_unquoted_symbol
16
+ unless IGNORE_TOKENS.include?(t.event)
17
+ opens.pop
18
+ skip = true
19
+ end
20
+ when :in_lambda_head
21
+ opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do')
22
+ when :in_method_head
23
+ unless IGNORE_TOKENS.include?(t.event)
24
+ next_args = []
25
+ body = nil
26
+ if args.include?(:receiver)
27
+ case t.event
28
+ when :on_lparen, :on_ivar, :on_gvar, :on_cvar
29
+ # def (receiver). | def @ivar. | def $gvar. | def @@cvar.
30
+ next_args << :dot
31
+ when :on_kw
32
+ case t.tok
33
+ when 'self', 'true', 'false', 'nil'
34
+ # def self(arg) | def self.
35
+ next_args.push(:arg, :dot)
36
+ else
37
+ # def if(arg)
38
+ skip = true
39
+ next_args << :arg
40
+ end
41
+ when :on_op, :on_backtick
42
+ # def +(arg)
43
+ skip = true
44
+ next_args << :arg
45
+ when :on_ident, :on_const
46
+ # def a(arg) | def a.
47
+ next_args.push(:arg, :dot)
48
+ end
49
+ end
50
+ if args.include?(:dot)
51
+ # def receiver.name
52
+ next_args << :name if t.event == :on_period || (t.event == :on_op && t.tok == '::')
53
+ end
54
+ if args.include?(:name)
55
+ if %i[on_ident on_const on_op on_kw on_backtick].include?(t.event)
56
+ # def name(arg) | def receiver.name(arg)
57
+ next_args << :arg
58
+ skip = true
59
+ end
60
+ end
61
+ if args.include?(:arg)
62
+ case t.event
63
+ when :on_nl, :on_semicolon
64
+ # def recever.f;
65
+ body = :normal
66
+ when :on_lparen
67
+ # def recever.f()
68
+ next_args << :eq
69
+ else
70
+ if t.event == :on_op && t.tok == '='
71
+ # def receiver.f =
72
+ body = :oneliner
73
+ else
74
+ # def recever.f arg
75
+ next_args << :arg_without_paren
76
+ end
77
+ end
78
+ end
79
+ if args.include?(:eq)
80
+ if t.event == :on_op && t.tok == '='
81
+ body = :oneliner
82
+ else
83
+ body = :normal
84
+ end
85
+ end
86
+ if args.include?(:arg_without_paren)
87
+ if %i[on_semicolon on_nl].include?(t.event)
88
+ # def f a;
89
+ body = :normal
90
+ else
91
+ # def f a, b
92
+ next_args << :arg_without_paren
93
+ end
94
+ end
95
+ if body == :oneliner
96
+ opens.pop
97
+ elsif body
98
+ opens[-1] = [last_tok, nil]
99
+ else
100
+ opens[-1] = [last_tok, :in_method_head, next_args]
101
+ end
102
+ end
103
+ when :in_for_while_until_condition
104
+ if t.event == :on_semicolon || t.event == :on_nl || (t.event == :on_kw && t.tok == 'do')
105
+ skip = true if t.event == :on_kw && t.tok == 'do'
106
+ opens[-1] = [last_tok, nil]
107
+ end
108
+ end
109
+
110
+ unless skip
111
+ case t.event
112
+ when :on_kw
113
+ case t.tok
114
+ when 'begin', 'class', 'module', 'do', 'case'
115
+ opens << [t, nil]
116
+ when 'end'
117
+ opens.pop
118
+ when 'def'
119
+ opens << [t, :in_method_head, [:receiver, :name]]
120
+ when 'if', 'unless'
121
+ unless t.state.allbits?(Ripper::EXPR_LABEL)
122
+ opens << [t, nil]
123
+ end
124
+ when 'while', 'until'
125
+ unless t.state.allbits?(Ripper::EXPR_LABEL)
126
+ opens << [t, :in_for_while_until_condition]
127
+ end
128
+ when 'ensure', 'rescue'
129
+ unless t.state.allbits?(Ripper::EXPR_LABEL)
130
+ opens.pop
131
+ opens << [t, nil]
132
+ end
133
+ when 'elsif', 'else', 'when'
134
+ opens.pop
135
+ opens << [t, nil]
136
+ when 'for'
137
+ opens << [t, :in_for_while_until_condition]
138
+ when 'in'
139
+ if last_tok&.event == :on_kw && %w[case in].include?(last_tok.tok) && first_token_on_line
140
+ opens.pop
141
+ opens << [t, nil]
142
+ end
143
+ end
144
+ when :on_tlambda
145
+ opens << [t, :in_lambda_head]
146
+ when :on_lparen, :on_lbracket, :on_lbrace, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg
147
+ opens << [t, nil]
148
+ when :on_rparen, :on_rbracket, :on_rbrace, :on_embexpr_end, :on_embdoc_end
149
+ opens.pop
150
+ when :on_heredoc_beg
151
+ pending_heredocs << t
152
+ when :on_heredoc_end
153
+ opens.pop
154
+ when :on_backtick
155
+ opens << [t, nil] if t.state.allbits?(Ripper::EXPR_BEG)
156
+ when :on_tstring_beg, :on_words_beg, :on_qwords_beg, :on_symbols_beg, :on_qsymbols_beg, :on_regexp_beg
157
+ opens << [t, nil]
158
+ when :on_tstring_end, :on_regexp_end, :on_label_end
159
+ opens.pop
160
+ when :on_symbeg
161
+ if t.tok == ':'
162
+ opens << [t, :in_unquoted_symbol]
163
+ else
164
+ opens << [t, nil]
165
+ end
166
+ end
167
+ end
168
+ if t.event == :on_nl || t.event == :on_semicolon
169
+ first_token_on_line = true
170
+ elsif t.event != :on_sp
171
+ first_token_on_line = false
172
+ end
173
+ if pending_heredocs.any? && t.tok.include?("\n")
174
+ pending_heredocs.reverse_each { |t| opens << [t, nil] }
175
+ pending_heredocs = []
176
+ end
177
+ yield t, opens if block_given?
178
+ end
179
+ opens.map(&:first) + pending_heredocs.reverse
180
+ end
181
+
182
+ def self.open_tokens(tokens)
183
+ # scan_opens without block will return a list of open tokens at last token position
184
+ scan_opens(tokens)
185
+ end
186
+
187
+ # Calculates token information [line_tokens, prev_opens, next_opens, min_depth] for each line.
188
+ # Example code
189
+ # ["hello
190
+ # world"+(
191
+ # First line
192
+ # line_tokens: [[lbracket, '['], [tstring_beg, '"'], [tstring_content("hello\nworld"), "hello\n"]]
193
+ # prev_opens: []
194
+ # next_tokens: [lbracket, tstring_beg]
195
+ # min_depth: 0 (minimum at beginning of line)
196
+ # Second line
197
+ # line_tokens: [[tstring_content("hello\nworld"), "world"], [tstring_end, '"'], [op, '+'], [lparen, '(']]
198
+ # prev_opens: [lbracket, tstring_beg]
199
+ # next_tokens: [lbracket, lparen]
200
+ # min_depth: 1 (minimum just after tstring_end)
201
+ def self.parse_by_line(tokens)
202
+ line_tokens = []
203
+ prev_opens = []
204
+ min_depth = 0
205
+ output = []
206
+ last_opens = scan_opens(tokens) do |t, opens|
207
+ depth = t == opens.last&.first ? opens.size - 1 : opens.size
208
+ min_depth = depth if depth < min_depth
209
+ if t.tok.include?("\n")
210
+ t.tok.each_line do |line|
211
+ line_tokens << [t, line]
212
+ next if line[-1] != "\n"
213
+ next_opens = opens.map(&:first)
214
+ output << [line_tokens, prev_opens, next_opens, min_depth]
215
+ prev_opens = next_opens
216
+ min_depth = prev_opens.size
217
+ line_tokens = []
218
+ end
219
+ else
220
+ line_tokens << [t, t.tok]
221
+ end
222
+ end
223
+ output << [line_tokens, prev_opens, last_opens, min_depth] if line_tokens.any?
224
+ output
225
+ end
226
+ end
227
+ end
data/lib/irb/pager.rb ADDED
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IRB
4
+ # The implementation of this class is borrowed from RDoc's lib/rdoc/ri/driver.rb.
5
+ # Please do NOT use this class directly outside of IRB.
6
+ class Pager
7
+ PAGE_COMMANDS = [ENV['RI_PAGER'], ENV['PAGER'], 'less', 'more'].compact.uniq
8
+
9
+ class << self
10
+ def page_content(content)
11
+ if content_exceeds_screen_height?(content)
12
+ page do |io|
13
+ io.puts content
14
+ end
15
+ else
16
+ $stdout.puts content
17
+ end
18
+ end
19
+
20
+ def page
21
+ if STDIN.tty? && pager = setup_pager
22
+ begin
23
+ pid = pager.pid
24
+ yield pager
25
+ ensure
26
+ pager.close
27
+ end
28
+ else
29
+ yield $stdout
30
+ end
31
+ # When user presses Ctrl-C, IRB would raise `IRB::Abort`
32
+ # But since Pager is implemented by running paging commands like `less` in another process with `IO.popen`,
33
+ # the `IRB::Abort` exception only interrupts IRB's execution but doesn't affect the pager
34
+ # So to properly terminate the pager with Ctrl-C, we need to catch `IRB::Abort` and kill the pager process
35
+ rescue IRB::Abort
36
+ Process.kill("TERM", pid) if pid
37
+ nil
38
+ rescue Errno::EPIPE
39
+ end
40
+
41
+ private
42
+
43
+ def content_exceeds_screen_height?(content)
44
+ screen_height, screen_width = begin
45
+ Reline.get_screen_size
46
+ rescue Errno::EINVAL
47
+ [24, 80]
48
+ end
49
+
50
+ pageable_height = screen_height - 3 # leave some space for previous and the current prompt
51
+
52
+ # If the content has more lines than the pageable height
53
+ content.lines.count > pageable_height ||
54
+ # Or if the content is a few long lines
55
+ pageable_height * screen_width < Reline::Unicode.calculate_width(content, true)
56
+ end
57
+
58
+ def setup_pager
59
+ require 'shellwords'
60
+
61
+ PAGE_COMMANDS.each do |pager|
62
+ pager = Shellwords.split(pager)
63
+ next if pager.empty?
64
+
65
+ if pager.first == 'less' || pager.first == 'more'
66
+ pager << '-R' unless pager.include?('-R')
67
+ end
68
+
69
+ begin
70
+ io = IO.popen(pager, 'w')
71
+ rescue
72
+ next
73
+ end
74
+
75
+ if $? && $?.pid == io.pid && $?.exited? # pager didn't work
76
+ next
77
+ end
78
+
79
+ return io
80
+ end
81
+
82
+ nil
83
+ end
84
+ end
85
+ end
86
+ end