irb 1.15.2 → 1.17.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.
- checksums.yaml +4 -4
- data/.rdoc_options +5 -0
- data/CONTRIBUTING.md +52 -0
- data/EXTEND_IRB.md +3 -0
- data/Gemfile +9 -1
- data/doc/COMMAND_LINE_OPTIONS.md +69 -0
- data/doc/COMPARED_WITH_PRY.md +22 -0
- data/doc/Configurations.md +274 -0
- data/doc/EXTEND_IRB.md +122 -0
- data/doc/Index.md +705 -0
- data/doc/irb/irb.rd.ja +1 -1
- data/lib/irb/color.rb +198 -167
- data/lib/irb/command/copy.rb +11 -1
- data/lib/irb/completion.rb +31 -36
- data/lib/irb/context.rb +9 -8
- data/lib/irb/debug.rb +3 -3
- data/lib/irb/easter-egg.rb +3 -1
- data/lib/irb/ext/multi-irb.rb +2 -0
- data/lib/irb/input-method.rb +2 -2
- data/lib/irb/inspector.rb +1 -1
- data/lib/irb/lc/help-message +2 -2
- data/lib/irb/lc/ja/help-message +1 -1
- data/lib/irb/nesting_parser.rb +361 -213
- data/lib/irb/ruby-lex.rb +67 -41
- data/lib/irb/source_finder.rb +7 -4
- data/lib/irb/version.rb +2 -2
- data/lib/irb/workspace.rb +9 -0
- data/lib/irb.rb +61 -28
- metadata +25 -7
- data/Rakefile +0 -52
- data/bin/console +0 -6
- data/bin/setup +0 -6
- data/irb.gemspec +0 -48
data/lib/irb/ext/multi-irb.rb
CHANGED
|
@@ -65,6 +65,7 @@ module IRB
|
|
|
65
65
|
@current_job = irb
|
|
66
66
|
th.run
|
|
67
67
|
Thread.stop
|
|
68
|
+
Thread.pass
|
|
68
69
|
@current_job = irb(Thread.current)
|
|
69
70
|
end
|
|
70
71
|
|
|
@@ -220,6 +221,7 @@ module IRB
|
|
|
220
221
|
end
|
|
221
222
|
end
|
|
222
223
|
Thread.stop
|
|
224
|
+
Thread.pass
|
|
223
225
|
@JobManager.current_job = @JobManager.irb(Thread.current)
|
|
224
226
|
end
|
|
225
227
|
|
data/lib/irb/input-method.rb
CHANGED
|
@@ -223,7 +223,7 @@ module IRB
|
|
|
223
223
|
Readline.input = @stdin
|
|
224
224
|
Readline.output = @stdout
|
|
225
225
|
if l = Readline.readline(@prompt, false)
|
|
226
|
-
Readline::HISTORY.push(l) if !l.empty?
|
|
226
|
+
Readline::HISTORY.push(l) if !l.empty? && l != Readline::HISTORY.to_a.last
|
|
227
227
|
@line[@line_no += 1] = l + "\n"
|
|
228
228
|
else
|
|
229
229
|
@eof = true
|
|
@@ -480,7 +480,7 @@ module IRB
|
|
|
480
480
|
Reline.prompt_proc = @prompt_proc
|
|
481
481
|
Reline.auto_indent_proc = @auto_indent_proc if @auto_indent_proc
|
|
482
482
|
if l = Reline.readmultiline(@prompt, false, &@check_termination_proc)
|
|
483
|
-
Reline::HISTORY.push(l) if !l.empty?
|
|
483
|
+
Reline::HISTORY.push(l) if !l.empty? && l != Reline::HISTORY.to_a.last
|
|
484
484
|
@line[@line_no += 1] = l + "\n"
|
|
485
485
|
else
|
|
486
486
|
@eof = true
|
data/lib/irb/inspector.rb
CHANGED
|
@@ -46,7 +46,7 @@ module IRB # :nodoc:
|
|
|
46
46
|
# Determines the inspector to use where +inspector+ is one of the keys passed
|
|
47
47
|
# during inspector definition.
|
|
48
48
|
def keys_with_inspector(inspector)
|
|
49
|
-
INSPECTORS.
|
|
49
|
+
INSPECTORS.filter_map {|k, v| k if v == inspector}
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
# Example
|
data/lib/irb/lc/help-message
CHANGED
|
@@ -6,10 +6,10 @@ Usage: irb.rb [options] [programfile] [arguments]
|
|
|
6
6
|
-U Set external and internal encodings to UTF-8.
|
|
7
7
|
-E ex[:in] Set default external (ex) and internal (in) encodings
|
|
8
8
|
(same as 'ruby -E').
|
|
9
|
-
-w
|
|
9
|
+
-w Enable warnings (same as 'ruby -w' or 'ruby -W1').
|
|
10
10
|
-W[level=2] Set warning level: 0=silence, 1=medium, 2=verbose
|
|
11
11
|
(same as 'ruby -W').
|
|
12
|
-
--context-mode n Set n[0-
|
|
12
|
+
--context-mode n Set n[0-5] to method to create Binding Object,
|
|
13
13
|
when new workspace was created.
|
|
14
14
|
--extra-doc-dir Add an extra doc dir for the doc dialog.
|
|
15
15
|
--echo Show result (default).
|
data/lib/irb/lc/ja/help-message
CHANGED
|
@@ -8,7 +8,7 @@ Usage: irb.rb [options] [programfile] [arguments]
|
|
|
8
8
|
-w ruby -w と同じ.
|
|
9
9
|
-W[level=2] ruby -W と同じ.
|
|
10
10
|
--context-mode n 新しいワークスペースを作成した時に関連する Binding
|
|
11
|
-
オブジェクトの作成方法を 0 から
|
|
11
|
+
オブジェクトの作成方法を 0 から 5 のいずれかに設定する.
|
|
12
12
|
--extra-doc-dir 指定したディレクトリのドキュメントを追加で読み込む.
|
|
13
13
|
--echo 実行結果を表示する(デフォルト).
|
|
14
14
|
--noecho 実行結果を表示しない.
|
data/lib/irb/nesting_parser.rb
CHANGED
|
@@ -1,238 +1,386 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'prism'
|
|
4
|
+
|
|
2
5
|
module IRB
|
|
3
6
|
module NestingParser
|
|
4
|
-
|
|
7
|
+
NestingElem = Struct.new(:pos, :event, :tok)
|
|
5
8
|
|
|
6
|
-
class
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
when :in_alias_undef
|
|
17
|
-
skip = t.event == :on_kw
|
|
18
|
-
when :in_unquoted_symbol
|
|
19
|
-
unless IGNORE_TOKENS.include?(t.event)
|
|
20
|
-
opens.pop
|
|
21
|
-
skip = true
|
|
22
|
-
end
|
|
23
|
-
when :in_lambda_head
|
|
24
|
-
opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do')
|
|
25
|
-
when :in_method_head
|
|
26
|
-
unless IGNORE_TOKENS.include?(t.event)
|
|
27
|
-
next_args = []
|
|
28
|
-
body = nil
|
|
29
|
-
if args.include?(:receiver)
|
|
30
|
-
case t.event
|
|
31
|
-
when :on_lparen, :on_ivar, :on_gvar, :on_cvar
|
|
32
|
-
# def (receiver). | def @ivar. | def $gvar. | def @@cvar.
|
|
33
|
-
next_args << :dot
|
|
34
|
-
when :on_kw
|
|
35
|
-
case t.tok
|
|
36
|
-
when 'self', 'true', 'false', 'nil'
|
|
37
|
-
# def self(arg) | def self.
|
|
38
|
-
next_args.push(:arg, :dot)
|
|
39
|
-
else
|
|
40
|
-
# def if(arg)
|
|
41
|
-
skip = true
|
|
42
|
-
next_args << :arg
|
|
43
|
-
end
|
|
44
|
-
when :on_op, :on_backtick
|
|
45
|
-
# def +(arg)
|
|
46
|
-
skip = true
|
|
47
|
-
next_args << :arg
|
|
48
|
-
when :on_ident, :on_const
|
|
49
|
-
# def a(arg) | def a.
|
|
50
|
-
next_args.push(:arg, :dot)
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
if args.include?(:dot)
|
|
54
|
-
# def receiver.name
|
|
55
|
-
next_args << :name if t.event == :on_period || (t.event == :on_op && t.tok == '::')
|
|
56
|
-
end
|
|
57
|
-
if args.include?(:name)
|
|
58
|
-
if %i[on_ident on_const on_op on_kw on_backtick].include?(t.event)
|
|
59
|
-
# def name(arg) | def receiver.name(arg)
|
|
60
|
-
next_args << :arg
|
|
61
|
-
skip = true
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
if args.include?(:arg)
|
|
65
|
-
case t.event
|
|
66
|
-
when :on_nl, :on_semicolon
|
|
67
|
-
# def receiver.f;
|
|
68
|
-
body = :normal
|
|
69
|
-
when :on_lparen
|
|
70
|
-
# def receiver.f()
|
|
71
|
-
next_args << :eq
|
|
72
|
-
else
|
|
73
|
-
if t.event == :on_op && t.tok == '='
|
|
74
|
-
# def receiver.f =
|
|
75
|
-
body = :oneliner
|
|
76
|
-
else
|
|
77
|
-
# def receiver.f arg
|
|
78
|
-
next_args << :arg_without_paren
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
if args.include?(:eq)
|
|
83
|
-
if t.event == :on_op && t.tok == '='
|
|
84
|
-
body = :oneliner
|
|
85
|
-
else
|
|
86
|
-
body = :normal
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
if args.include?(:arg_without_paren)
|
|
90
|
-
if %i[on_semicolon on_nl].include?(t.event)
|
|
91
|
-
# def f a;
|
|
92
|
-
body = :normal
|
|
93
|
-
else
|
|
94
|
-
# def f a, b
|
|
95
|
-
next_args << :arg_without_paren
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
if body == :oneliner
|
|
99
|
-
opens.pop
|
|
100
|
-
elsif body
|
|
101
|
-
opens[-1] = [last_tok, nil]
|
|
102
|
-
else
|
|
103
|
-
opens[-1] = [last_tok, :in_method_head, next_args]
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
when :in_for_while_until_condition
|
|
107
|
-
if t.event == :on_semicolon || t.event == :on_nl || (t.event == :on_kw && t.tok == 'do')
|
|
108
|
-
skip = true if t.event == :on_kw && t.tok == 'do'
|
|
109
|
-
opens[-1] = [last_tok, nil]
|
|
110
|
-
end
|
|
111
|
-
end
|
|
9
|
+
class NestingVisitor < Prism::Visitor
|
|
10
|
+
def initialize
|
|
11
|
+
# Array of [column, priority(+1/-1), NestingElem(open) or nil(close)] per line.
|
|
12
|
+
# priority is +1 for open, -1 for close so that close comes before open when sorted.
|
|
13
|
+
# Example:
|
|
14
|
+
# if cond
|
|
15
|
+
# else
|
|
16
|
+
# end
|
|
17
|
+
# `else` closes `if` at column 0 first and then opens `else` at column 0 next.
|
|
18
|
+
@lines = []
|
|
112
19
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
end
|
|
127
|
-
when 'while', 'until'
|
|
128
|
-
unless t.state.allbits?(Ripper::EXPR_LABEL)
|
|
129
|
-
opens << [t, :in_for_while_until_condition]
|
|
130
|
-
end
|
|
131
|
-
when 'ensure', 'rescue'
|
|
132
|
-
unless t.state.allbits?(Ripper::EXPR_LABEL)
|
|
133
|
-
opens.pop
|
|
134
|
-
opens << [t, nil]
|
|
135
|
-
end
|
|
136
|
-
when 'alias'
|
|
137
|
-
opens << [t, :in_alias_undef, 2]
|
|
138
|
-
when 'undef'
|
|
139
|
-
opens << [t, :in_alias_undef, 1]
|
|
140
|
-
when 'elsif', 'else', 'when'
|
|
141
|
-
opens.pop
|
|
142
|
-
opens << [t, nil]
|
|
143
|
-
when 'for'
|
|
144
|
-
opens << [t, :in_for_while_until_condition]
|
|
145
|
-
when 'in'
|
|
146
|
-
if last_tok&.event == :on_kw && %w[case in].include?(last_tok.tok) && first_token_on_line
|
|
147
|
-
opens.pop
|
|
148
|
-
opens << [t, nil]
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
when :on_tlambda
|
|
152
|
-
opens << [t, :in_lambda_head]
|
|
153
|
-
when :on_lparen, :on_lbracket, :on_lbrace, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg
|
|
154
|
-
opens << [t, nil]
|
|
155
|
-
when :on_rparen, :on_rbracket, :on_rbrace, :on_embexpr_end, :on_embdoc_end
|
|
156
|
-
opens.pop
|
|
157
|
-
when :on_heredoc_beg
|
|
158
|
-
pending_heredocs << t
|
|
159
|
-
when :on_heredoc_end
|
|
160
|
-
opens.pop
|
|
161
|
-
when :on_backtick
|
|
162
|
-
opens << [t, nil] unless t.state == Ripper::EXPR_ARG
|
|
163
|
-
when :on_tstring_beg, :on_words_beg, :on_qwords_beg, :on_symbols_beg, :on_qsymbols_beg, :on_regexp_beg
|
|
164
|
-
opens << [t, nil]
|
|
165
|
-
when :on_tstring_end, :on_regexp_end, :on_label_end
|
|
166
|
-
opens.pop
|
|
167
|
-
when :on_symbeg
|
|
168
|
-
if t.tok == ':'
|
|
169
|
-
opens << [t, :in_unquoted_symbol]
|
|
170
|
-
else
|
|
171
|
-
opens << [t, nil]
|
|
172
|
-
end
|
|
20
|
+
# Array of open heredoc NestingElem per line
|
|
21
|
+
@heredocs = []
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def nestings
|
|
25
|
+
size = [@lines.size, @heredocs.size].max
|
|
26
|
+
nesting = []
|
|
27
|
+
size.times.map do |line_index|
|
|
28
|
+
@lines[line_index]&.sort_by { |col, pri| [col, pri] }&.each do |col, pri, elem|
|
|
29
|
+
if elem
|
|
30
|
+
nesting << elem
|
|
31
|
+
else
|
|
32
|
+
nesting.pop
|
|
173
33
|
end
|
|
174
34
|
end
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
35
|
+
@heredocs[line_index]&.sort_by { |elem| elem.pos[1] }&.reverse_each do |elem|
|
|
36
|
+
nesting << elem
|
|
37
|
+
end
|
|
38
|
+
nesting.dup
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def heredoc_open(node)
|
|
43
|
+
elem = NestingElem.new([node.location.start_line, node.location.start_column], :on_heredoc_beg, node.opening)
|
|
44
|
+
(@heredocs[node.location.start_line - 1] ||= []) << elem
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def open(line, column, elem)
|
|
48
|
+
(@lines[line - 1] ||= []) << [column, +1, elem]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def close(line, column)
|
|
52
|
+
(@lines[line - 1] ||= []) << [column, -1]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Checks if a node (if, while, etc) is a modifier form that does not need `end` closing.
|
|
56
|
+
# modifier node: `a if b`, non-modifier node: `if a; b; end`
|
|
57
|
+
def modifier_node?(node, keyword_loc)
|
|
58
|
+
!(keyword_loc && node.location.start_line == keyword_loc.start_line && node.location.start_column == keyword_loc.start_column)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def open_location(location, type, tok)
|
|
62
|
+
open(location.start_line, location.start_column, NestingElem.new([location.start_line, location.start_column], type, tok))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def close_location(location)
|
|
66
|
+
close(location.end_line, location.end_column)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def close_location_start(location)
|
|
70
|
+
close(location.start_line, location.start_column)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def close_end_keyword_loc(node)
|
|
74
|
+
close_location(node.end_keyword_loc) if node.end_keyword == 'end'
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def close_closing_loc(node)
|
|
78
|
+
close_location(node.closing_loc) if node.closing_loc && !node.closing.empty?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def visit_for_node(node)
|
|
82
|
+
super
|
|
83
|
+
open_location(node.location, :on_kw, 'for')
|
|
84
|
+
close_end_keyword_loc(node)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def visit_while_node(node)
|
|
88
|
+
super
|
|
89
|
+
return if modifier_node?(node, node.keyword_loc)
|
|
90
|
+
|
|
91
|
+
open_location(node.location, :on_kw, 'while')
|
|
92
|
+
close_closing_loc(node)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def visit_until_node(node)
|
|
96
|
+
super
|
|
97
|
+
return if modifier_node?(node, node.keyword_loc)
|
|
98
|
+
|
|
99
|
+
open_location(node.location, :on_kw, 'until')
|
|
100
|
+
close_closing_loc(node)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def visit_if_node(node)
|
|
104
|
+
super
|
|
105
|
+
return if !node.if_keyword || modifier_node?(node, node.if_keyword_loc)
|
|
106
|
+
|
|
107
|
+
open_location(node.location, :on_kw, node.if_keyword)
|
|
108
|
+
if node.subsequent
|
|
109
|
+
close_location_start(node.subsequent.location)
|
|
110
|
+
else
|
|
111
|
+
close_end_keyword_loc(node)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def visit_unless_node(node)
|
|
116
|
+
super
|
|
117
|
+
return if modifier_node?(node, node.keyword_loc)
|
|
118
|
+
|
|
119
|
+
open_location(node.location, :on_kw, 'unless')
|
|
120
|
+
if node.else_clause
|
|
121
|
+
close_location_start(node.else_clause.location)
|
|
122
|
+
else
|
|
123
|
+
close_end_keyword_loc(node)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def visit_case_node(node)
|
|
128
|
+
super
|
|
129
|
+
open_location(node.location, :on_kw, 'case')
|
|
130
|
+
if node.else_clause
|
|
131
|
+
close_location_start(node.else_clause.location)
|
|
132
|
+
else
|
|
133
|
+
close_end_keyword_loc(node)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
alias visit_case_match_node visit_case_node
|
|
137
|
+
|
|
138
|
+
def visit_when_node(node)
|
|
139
|
+
super
|
|
140
|
+
close_location_start(node.location)
|
|
141
|
+
open_location(node.location, :on_kw, 'when')
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def visit_in_node(node)
|
|
145
|
+
super
|
|
146
|
+
close_location_start(node.location)
|
|
147
|
+
open_location(node.location, :on_kw, 'in')
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def visit_else_node(node)
|
|
151
|
+
super
|
|
152
|
+
if node.else_keyword == 'else'
|
|
153
|
+
open_location(node.location, :on_kw, 'else')
|
|
154
|
+
close_end_keyword_loc(node)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def visit_ensure_node(node)
|
|
159
|
+
super
|
|
160
|
+
return if modifier_node?(node, node.ensure_keyword_loc)
|
|
161
|
+
|
|
162
|
+
close_location_start(node.location)
|
|
163
|
+
open_location(node.location, :on_kw, 'ensure')
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def visit_rescue_node(node)
|
|
167
|
+
super
|
|
168
|
+
return if modifier_node?(node, node.keyword_loc)
|
|
169
|
+
|
|
170
|
+
close_location_start(node.location)
|
|
171
|
+
open_location(node.location, :on_kw, 'rescue')
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def visit_begin_node(node)
|
|
175
|
+
super
|
|
176
|
+
if node.begin_keyword
|
|
177
|
+
open_location(node.location, :on_kw, 'begin')
|
|
178
|
+
close_end_keyword_loc(node)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def visit_block_node(node)
|
|
183
|
+
super
|
|
184
|
+
open_location(node.location, node.opening == '{' ? :on_lbrace : :on_kw, node.opening)
|
|
185
|
+
close_closing_loc(node)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def visit_array_node(node)
|
|
189
|
+
super
|
|
190
|
+
type =
|
|
191
|
+
case node.opening
|
|
192
|
+
when nil
|
|
193
|
+
# `x = 1, 2` doesn't have opening
|
|
194
|
+
nil
|
|
195
|
+
when '['
|
|
196
|
+
:bracket
|
|
197
|
+
when /\A%W/
|
|
198
|
+
:on_words_beg
|
|
199
|
+
when /\A%w/
|
|
200
|
+
:on_qwords_beg
|
|
201
|
+
when /\A%I/
|
|
202
|
+
:on_symbols_beg
|
|
203
|
+
when /\A%i/
|
|
204
|
+
:on_qsymbols_beg
|
|
179
205
|
end
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
206
|
+
|
|
207
|
+
if type
|
|
208
|
+
open_location(node.location, type, node.opening)
|
|
209
|
+
close_closing_loc(node)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def visit_hash_node(node)
|
|
214
|
+
super
|
|
215
|
+
open_location(node.location, :on_lbrace, '{')
|
|
216
|
+
close_closing_loc(node)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def heredoc_string_like(node, type)
|
|
220
|
+
if node.opening&.start_with?('<<')
|
|
221
|
+
heredoc_open(node)
|
|
222
|
+
# Heredoc closing contains trailing newline. We need to exclude it
|
|
223
|
+
close_location_start(node.closing_loc) if node.closing_loc && !node.closing.empty?
|
|
224
|
+
elsif node.opening
|
|
225
|
+
open_location(node.location, type, node.opening)
|
|
226
|
+
if node.closing && node.closing != ''
|
|
227
|
+
# Closing of `"#{\n` is "\n". We need to treat it as not-closed.
|
|
228
|
+
close_location_start(node.closing_loc) if node.opening.match?(/\n\z/) || node.closing != "\n"
|
|
183
229
|
end
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def visit_embedded_statements_node(node)
|
|
234
|
+
super
|
|
235
|
+
open_location(node.location, :on_embexpr_beg, '#{')
|
|
236
|
+
close_closing_loc(node)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def visit_interpolated_string_node(node)
|
|
240
|
+
super
|
|
241
|
+
heredoc_string_like(node, :on_tstring_beg)
|
|
242
|
+
end
|
|
243
|
+
alias visit_string_node visit_interpolated_string_node
|
|
244
|
+
|
|
245
|
+
def visit_interpolated_x_string_node(node)
|
|
246
|
+
super
|
|
247
|
+
heredoc_string_like(node, :on_backtick)
|
|
248
|
+
end
|
|
249
|
+
alias visit_x_string_node visit_interpolated_x_string_node
|
|
250
|
+
|
|
251
|
+
def visit_symbol_node(node)
|
|
252
|
+
super
|
|
253
|
+
unless node.opening.nil? || node.opening.empty? || node.opening == ':'
|
|
254
|
+
# :"sym" or %s[sym]
|
|
255
|
+
open_location(node.location, :on_symbeg, node.opening)
|
|
256
|
+
close_closing_loc(node)
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
alias visit_interpolated_symbol_node visit_symbol_node
|
|
260
|
+
|
|
261
|
+
def visit_regular_expression_node(node)
|
|
262
|
+
super
|
|
263
|
+
open_location(node.location, :on_regexp_beg, node.opening)
|
|
264
|
+
close_closing_loc(node)
|
|
265
|
+
end
|
|
266
|
+
alias visit_interpolated_regular_expression_node visit_regular_expression_node
|
|
267
|
+
|
|
268
|
+
def visit_parentheses_node(node)
|
|
269
|
+
super
|
|
270
|
+
open_location(node.location, :on_lparen, '(')
|
|
271
|
+
close_closing_loc(node)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def visit_call_node(node)
|
|
275
|
+
super
|
|
276
|
+
type =
|
|
277
|
+
case node.opening
|
|
278
|
+
when '('
|
|
279
|
+
:on_lparen
|
|
280
|
+
when '['
|
|
281
|
+
:on_lbracket
|
|
187
282
|
end
|
|
188
|
-
|
|
283
|
+
|
|
284
|
+
if type
|
|
285
|
+
open_location(node.opening_loc, type, node.opening)
|
|
286
|
+
close_closing_loc(node)
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def visit_block_parameters_node(node)
|
|
291
|
+
super
|
|
292
|
+
if node.opening == '('
|
|
293
|
+
open_location(node.location, :on_lparen, '(')
|
|
294
|
+
close_closing_loc(node)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def visit_lambda_node(node)
|
|
299
|
+
super
|
|
300
|
+
open_location(node.opening_loc, :on_tlambeg, node.opening)
|
|
301
|
+
close_closing_loc(node)
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def visit_super_node(node)
|
|
305
|
+
super
|
|
306
|
+
if node.lparen
|
|
307
|
+
open_location(node.lparen_loc, :on_lparen, '(')
|
|
308
|
+
close_location(node.rparen_loc) if node.rparen == ')'
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
alias visit_yield_node visit_super_node
|
|
312
|
+
alias visit_defined_node visit_super_node
|
|
313
|
+
|
|
314
|
+
def visit_def_node(node)
|
|
315
|
+
super
|
|
316
|
+
open_location(node.location, :on_kw, 'def')
|
|
317
|
+
if node.lparen == '('
|
|
318
|
+
open_location(node.lparen_loc, :on_lparen, '(')
|
|
319
|
+
close_location(node.rparen_loc) if node.rparen == ')'
|
|
189
320
|
end
|
|
190
|
-
|
|
321
|
+
if node.equal
|
|
322
|
+
close_location(node.equal_loc)
|
|
323
|
+
else
|
|
324
|
+
close_end_keyword_loc(node)
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def visit_class_node(node)
|
|
329
|
+
super
|
|
330
|
+
open_location(node.location, :on_kw, 'class')
|
|
331
|
+
close_end_keyword_loc(node)
|
|
332
|
+
end
|
|
333
|
+
alias visit_singleton_class_node visit_class_node
|
|
334
|
+
|
|
335
|
+
def visit_module_node(node)
|
|
336
|
+
super
|
|
337
|
+
open_location(node.location, :on_kw, 'module')
|
|
338
|
+
close_end_keyword_loc(node)
|
|
191
339
|
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
class << self
|
|
192
343
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
344
|
+
# Return a list of open nesting elements at EOF position
|
|
345
|
+
def open_nestings(parse_lex_result)
|
|
346
|
+
parse_by_line(parse_lex_result).last[1]
|
|
196
347
|
end
|
|
197
348
|
|
|
198
|
-
# Calculates
|
|
349
|
+
# Calculates nesting information [prev_opens, next_opens, min_depth] for each line.
|
|
199
350
|
# Example code
|
|
200
351
|
# ["hello
|
|
201
352
|
# world"+(
|
|
202
353
|
# First line
|
|
203
|
-
#
|
|
204
|
-
#
|
|
205
|
-
#
|
|
206
|
-
# min_depth: 0 (minimum at beginning of line)
|
|
354
|
+
# prev_opens: []
|
|
355
|
+
# next_opens: [lbracket, tstring_beg]
|
|
356
|
+
# min_depth: 0 (minimum at beginning of line)
|
|
207
357
|
# Second line
|
|
208
|
-
#
|
|
209
|
-
#
|
|
210
|
-
#
|
|
211
|
-
|
|
212
|
-
def parse_by_line(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
line_tokens << [t, line]
|
|
223
|
-
next if line[-1] != "\n"
|
|
224
|
-
next_opens = opens.map(&:first)
|
|
225
|
-
output << [line_tokens, prev_opens, next_opens, min_depth]
|
|
226
|
-
prev_opens = next_opens
|
|
227
|
-
min_depth = prev_opens.size
|
|
228
|
-
line_tokens = []
|
|
229
|
-
end
|
|
230
|
-
else
|
|
231
|
-
line_tokens << [t, t.tok]
|
|
358
|
+
# prev_opens: [lbracket, tstring_beg]
|
|
359
|
+
# next_opens: [lbracket, lparen]
|
|
360
|
+
# min_depth: 1 (minimum just after tstring_end)
|
|
361
|
+
|
|
362
|
+
def parse_by_line(parse_lex_result)
|
|
363
|
+
visitor = NestingVisitor.new
|
|
364
|
+
node, tokens = parse_lex_result.value
|
|
365
|
+
node.accept(visitor)
|
|
366
|
+
tokens.each do |token,|
|
|
367
|
+
case token.type
|
|
368
|
+
when :EMBDOC_BEGIN
|
|
369
|
+
visitor.open_location(token.location, :on_embdoc_beg, '=begin')
|
|
370
|
+
when :EMBDOC_END
|
|
371
|
+
visitor.close_location_start(token.location)
|
|
232
372
|
end
|
|
233
373
|
end
|
|
234
|
-
|
|
235
|
-
|
|
374
|
+
nestings = visitor.nestings
|
|
375
|
+
last_nesting = nestings.last || []
|
|
376
|
+
|
|
377
|
+
num_lines = parse_lex_result.source.source.lines.size
|
|
378
|
+
num_lines.times.map do |i|
|
|
379
|
+
prev_opens = i == 0 ? [] : nestings[i - 1] || last_nesting
|
|
380
|
+
opens = nestings[i] || last_nesting
|
|
381
|
+
min_depth = prev_opens.zip(opens).take_while { |s, e| s == e }.size
|
|
382
|
+
[prev_opens, opens, min_depth]
|
|
383
|
+
end
|
|
236
384
|
end
|
|
237
385
|
end
|
|
238
386
|
end
|