katakata_irb 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e13d1b43d98e8be7595125b4e1e27533d83da1ee4d87df0dfb613d8fb8adee4
4
- data.tar.gz: ae508bc65cd1049100fe4811b1d2b27036b30131cab13821d52b637e91e2ed41
3
+ metadata.gz: 882c0834b6d52916911b26b82083e1100491dd24cafdb1c948c4c1795fe03d7e
4
+ data.tar.gz: 2250eaec5d0b1cabc236f238249e132206b62e70ada1c16429567b680ace2093
5
5
  SHA512:
6
- metadata.gz: be9ed49c8de27772f7ec154df136134e9d5319a851fe392656a67d3c49069ed040a41b2af8deb6e81577c0c59c6d5bc0918a8165e09ccab2bce15fd6f09abba8
7
- data.tar.gz: 8d9554e6532488742e156d39e7ee63c3c2461900db42badfd0d49990e35769b1e0f34a8cd746d5aab57839d3f31e894a081ec42389cd2383dafab5043d3fa578
6
+ metadata.gz: 60f3c4de13636f2948e740b3a57b6091c1f193ae7fa259d665c69a404833ef413b90aca3da7360f97be2d7a669e9a0d5fc101ff172890c4f0714c9eb63db8dd4
7
+ data.tar.gz: eb496c54819de4f1e035e261a6afffcc13d2809b316428602567614dbc5671df5ead97fb4a0696afbb34a7ade9ce8a2b3e9579ebad4ddd006d9a32b120b3e05d
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- katakata_irb (0.1.4)
4
+ katakata_irb (0.1.5)
5
5
  irb (>= 1.4.0)
6
6
  rbs
7
7
 
@@ -27,18 +27,18 @@ module KatakataIrb::Completor
27
27
  ((self_call ? type.all_methods: type.methods).map(&:to_s) - HIDDEN_METHODS) | type.constants
28
28
  in [:const, type, name]
29
29
  type.constants
30
- in [:ivar, name, _scope]
30
+ in [:ivar, name, *_scope]
31
31
  # TODO: scope
32
32
  ivars = binding.eval('self').instance_variables rescue []
33
33
  cvars = (binding.eval('self').class_variables rescue nil) if name == '@'
34
34
  ivars | (cvars || [])
35
- in [:cvar, name, _scope]
35
+ in [:cvar, name, *_scope]
36
36
  # TODO: scope
37
37
  binding.eval('self').class_variables rescue []
38
38
  in [:gvar, name]
39
39
  global_variables
40
40
  in [:symbol, name]
41
- Symbol.all_symbols
41
+ Symbol.all_symbols.map { _1.inspect[1..] }
42
42
  in [:call, type, name, self_call]
43
43
  (self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS
44
44
  in [:lvar_or_method, name, scope]
@@ -67,15 +67,15 @@ module KatakataIrb::Completor
67
67
  method_doc = -> type do
68
68
  type = type.types.find { _1.all_methods.include? name.to_sym }
69
69
  if type in KatakataIrb::Types::SingletonType
70
- "#{type.module_or_class.name}.#{name}"
70
+ "#{KatakataIrb::Types.class_name_of(type.module_or_class)}.#{name}"
71
71
  elsif type in KatakataIrb::Types::InstanceType
72
- "#{type.klass.name}##{name}"
72
+ "#{KatakataIrb::Types.class_name_of(type.klass)}##{name}"
73
73
  end
74
74
  end
75
75
  call_or_const_doc = -> type do
76
76
  if name =~ /\A[A-Z]/
77
77
  type = type.types.grep(KatakataIrb::Types::SingletonType).find { _1.module_or_class.const_defined?(name) }
78
- type.module_or_class == Object ? name : "#{type.module_or_class.name}::#{name}" if type
78
+ type.module_or_class == Object ? name : "#{KatakataIrb::Types.class_name_of(type.module_or_class)}::#{name}" if type
79
79
  else
80
80
  method_doc.call(type)
81
81
  end
@@ -97,9 +97,50 @@ module KatakataIrb::Completor
97
97
  end
98
98
  end
99
99
  }
100
+ setup_type_dialog
100
101
  end
101
102
 
102
- def self.analyze(code, binding = Kernel.binding)
103
+ def self.setup_type_dialog
104
+ type_dialog_proc = -> {
105
+ return if just_cursor_moving && completion_journey_data
106
+ cursor_pos_to_render, _result, pointer, autocomplete_dialog = context.last(4)
107
+ return unless cursor_pos_to_render && autocomplete_dialog&.width && pointer.nil?
108
+ receiver_type = (
109
+ case KatakataIrb::Completor.prev_analyze_result
110
+ in [:call_or_const, type, name, _self_call] if name.empty?
111
+ type
112
+ in [:call, type, name, _self_call] if name.empty?
113
+ type
114
+ else
115
+ return
116
+ end
117
+ )
118
+ return unless receiver_type
119
+ types = type.types
120
+ contents = types.filter_map do |type|
121
+ case type
122
+ when KatakataIrb::Types::InstanceType
123
+ KatakataIrb::Types.class_name_of type.klass
124
+ when KatakataIrb::Types::SingletonType
125
+ module_name = KatakataIrb::Types.class_name_of type.module_or_class
126
+ "#{module_name}.itself" if module_name
127
+ end
128
+ end.uniq
129
+ return if contents.empty?
130
+
131
+ width = contents.map { Reline::Unicode.calculate_width _1 }.max
132
+ x = cursor_pos_to_render.x + autocomplete_dialog.width
133
+ y = cursor_pos_to_render.y
134
+ Reline::DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: 44, fg_color: 37)
135
+ }
136
+ Reline.add_dialog_proc(:show_type, type_dialog_proc, Reline::DEFAULT_DIALOG_CONTEXT)
137
+ end
138
+
139
+ def self.empty_binding()
140
+ Kernel.binding
141
+ end
142
+
143
+ def self.analyze(code, binding = empty_binding)
103
144
  lvars_code = binding.local_variables.map do |name|
104
145
  "#{name}="
105
146
  end.join + "nil;\n"
@@ -124,10 +165,20 @@ module KatakataIrb::Completor
124
165
  t.tok
125
166
  when /\A<<[~-]?(?:"(?<s>.+)"|'(?<s>.+)'|(?<s>.+))/
126
167
  $/ + ($1 || $2 || $3) + $/
168
+ when ':"', ":'", ':'
169
+ t.tok[1]
170
+ when '?'
171
+ # ternary operator
172
+ ' : value'
173
+ when '|'
174
+ # block args
175
+ '|'
127
176
  else
128
177
  $/ + 'end'
129
178
  end
130
179
  end
180
+ # remove error tokens
181
+ tokens.pop while tokens&.last&.tok&.empty?
131
182
 
132
183
  case tokens.last
133
184
  in { event: :on_ignored_by_ripper, tok: '.' }
@@ -136,6 +187,9 @@ module KatakataIrb::Completor
136
187
  in { dot: true }
137
188
  suffix = 'method'
138
189
  name = ''
190
+ in { event: :on_symbeg }
191
+ suffix = 'symbol'
192
+ name = ''
139
193
  in { event: :on_ident | :on_kw, tok: }
140
194
  return unless code.delete_suffix! tok
141
195
  suffix = 'method'
@@ -148,10 +202,21 @@ module KatakataIrb::Completor
148
202
  return unless code.delete_suffix! tok
149
203
  suffix = 'string'
150
204
  name = tok.rstrip
205
+ in { event: :on_gvar, tok: }
206
+ return unless code.delete_suffix! tok
207
+ suffix = '$gvar'
208
+ name = tok
209
+ in { event: :on_ivar, tok: }
210
+ return unless code.delete_suffix! tok
211
+ suffix = '@ivar'
212
+ name = tok
213
+ in { event: :on_cvar, tok: }
214
+ return unless code.delete_suffix! tok
215
+ suffix = '@@cvar'
216
+ name = tok
151
217
  else
152
218
  return
153
219
  end
154
-
155
220
  sexp = Ripper.sexp code + suffix + closings.reverse.join
156
221
  lines = code.lines
157
222
  line_no = lines.size
@@ -175,16 +240,26 @@ module KatakataIrb::Completor
175
240
  return [:cvar, name] if icvar_available
176
241
  end
177
242
  return unless expression
243
+ calculate_scope = -> { KatakataIrb::TypeSimulator.calculate_binding_scope binding, parents, expression }
244
+ calculate_receiver = -> receiver { KatakataIrb::TypeSimulator.calculate_receiver binding, parents, receiver }
245
+
178
246
  if (target in [:@tstring_content,]) && (parents[-4] in [:command, [:@ident, 'require' | 'require_relative' => require_method,],])
247
+ # `require 'target'`
179
248
  return [require_method.to_sym, name.rstrip]
180
249
  end
181
- calculate_scope = -> { KatakataIrb::TypeSimulator.calculate_binding_scope binding, parents, expression }
182
- calculate_receiver = -> receiver { KatakataIrb::TypeSimulator.calculate_receiver binding, parents, receiver }
250
+ if (target in [:@ident,]) && (expression in [:symbol,]) && (parents[-2] in [:args_add_block, Array => args, [:symbol_literal, ^expression]])
251
+ # `method(&:target)`
252
+ receiver_ref = [:var_ref, [:@ident, '_1', [0, 0]]]
253
+ block_statements = [receiver_ref]
254
+ parents[-1] = parents[-2][-1] = [:brace_block, nil, block_statements]
255
+ parents << block_statements
256
+ return [:call, calculate_receiver.call(receiver_ref), name, false]
257
+ end
183
258
  case expression
184
259
  in [:vcall | :var_ref, [:@ident,]]
185
260
  [:lvar_or_method, name, calculate_scope.call]
186
261
  in [:symbol, [:@ident | :@const | :@op | :@kw,]]
187
- [:symbol, name]
262
+ [:symbol, name] unless name.empty?
188
263
  in [:var_ref | :const_ref, [:@const,]]
189
264
  # TODO
190
265
  [:const, KatakataIrb::Types::SingletonType.new(Object), name]
@@ -26,6 +26,8 @@ module KatakataIrb::NestingParser
26
26
  interpolated
27
27
  end
28
28
 
29
+ IGNOREABLE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end]
30
+
29
31
  def self.parse(tokens)
30
32
  opens = []
31
33
  pending_heredocs = []
@@ -42,7 +44,7 @@ module KatakataIrb::NestingParser
42
44
  when :in_lambda_head
43
45
  opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do')
44
46
  when :in_method_head
45
- unless %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end].include?(t.event)
47
+ unless IGNOREABLE_TOKENS.include?(t.event)
46
48
  next_args = []
47
49
  body = nil
48
50
  if args.include?(:receiver)
@@ -127,13 +129,27 @@ module KatakataIrb::NestingParser
127
129
  skip = true if t.event == :on_kw && t.tok == 'do'
128
130
  opens[-1] = [last_tok, nil]
129
131
  end
132
+ when :in_block_head
133
+ if t.event == :on_op && t.tok == '|'
134
+ opens[-1] = [last_tok, nil]
135
+ opens << [t, :in_block_args]
136
+ elsif !IGNOREABLE_TOKENS.include?(t.event)
137
+ opens[-1] = [last_tok, nil]
138
+ end
139
+ when :in_block_args
140
+ if t.event == :on_op && t.tok == '|' && t.state.allbits?(Ripper::EXPR_BEG)
141
+ opens.pop
142
+ skip = true
143
+ end
130
144
  end
131
145
 
132
146
  unless skip
133
147
  case t.event
134
148
  when :on_kw
135
149
  case t.tok
136
- when 'begin', 'class', 'module', 'do', 'case'
150
+ when 'do'
151
+ opens << [t, :in_block_head]
152
+ when 'begin', 'class', 'module', 'case'
137
153
  opens << [t, nil]
138
154
  when 'end'
139
155
  opens.pop
@@ -163,9 +179,15 @@ module KatakataIrb::NestingParser
163
179
  opens << [t, nil]
164
180
  end
165
181
  end
182
+ when :on_lbrace
183
+ if t.state.allbits?(Ripper::EXPR_LABEL)
184
+ opens << [t, nil]
185
+ else
186
+ opens << [t, :in_block_head]
187
+ end
166
188
  when :on_tlambda
167
189
  opens << [t, :in_lambda_head]
168
- when :on_lparen, :on_lbracket, :on_lbrace, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg
190
+ when :on_lparen, :on_lbracket, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg
169
191
  opens << [t, nil]
170
192
  when :on_rparen, :on_rbracket, :on_rbrace, :on_embexpr_end, :on_embdoc_end
171
193
  opens.pop
@@ -179,6 +201,15 @@ module KatakataIrb::NestingParser
179
201
  opens << [t, nil]
180
202
  when :on_tstring_end, :on_regexp_end, :on_label_end
181
203
  opens.pop
204
+ when :on_op
205
+ case t.tok
206
+ when '?'
207
+ # opening of `cond ? value : value``
208
+ opens << [t, nil]
209
+ when ':'
210
+ # closing of `cond ? value : value``
211
+ opens.pop
212
+ end
182
213
  when :on_symbeg
183
214
  if t.tok == ':'
184
215
  opens << [t, :in_unquoted_symbol]
@@ -0,0 +1,248 @@
1
+ require 'set'
2
+ require_relative 'types'
3
+
4
+ module KatakataIrb
5
+ class BaseScope
6
+ SELF = '%self'
7
+ BREAK_RESULT = '%break'
8
+ NEXT_RESULT = '%next'
9
+ RETURN_RESULT = '%return'
10
+ PATTERNMATCH_BREAK = '%match'
11
+ RAISE_BREAK = '%raise'
12
+
13
+ def initialize(binding, self_object)
14
+ @binding, @self_object = binding, self_object
15
+ @cache = { SELF => KatakataIrb::Types.type_from_object(self_object) }
16
+ @local_variables = binding.local_variables.map(&:to_s).to_set
17
+ end
18
+
19
+ def level() = 0
20
+
21
+ def level_of(name)
22
+ has?(name) ? 0 : nil
23
+ end
24
+
25
+ def mutable?() = false
26
+
27
+ def [](name)
28
+ @cache[name] ||= (
29
+ fallback = KatakataIrb::Types::NIL
30
+ case BaseScope.type_by_name name
31
+ when :cvar
32
+ BaseScope.type_of(fallback: fallback) { @self_object.class_variable_get name }
33
+ when :ivar
34
+ BaseScope.type_of(fallback: fallback) { @self_object.instance_variable_get name }
35
+ when :lvar
36
+ BaseScope.type_of(fallback: fallback) { @binding.local_variable_get(name) }
37
+ when :const
38
+ BaseScope.type_of(fallback: fallback) { @binding.eval name }
39
+ end
40
+ )
41
+ end
42
+
43
+ def self_type
44
+ self[SELF]
45
+ end
46
+
47
+ def local_variables
48
+ @local_variables.to_a
49
+ end
50
+
51
+ def self.type_of(fallback: KatakataIrb::Types::OBJECT)
52
+ begin
53
+ KatakataIrb::Types.type_from_object yield
54
+ rescue
55
+ fallback
56
+ end
57
+ end
58
+
59
+ def self.type_by_name(name)
60
+ if name.start_with? '@@'
61
+ :cvar
62
+ elsif name.start_with? '@'
63
+ :ivar
64
+ elsif name.start_with? '$'
65
+ :gvar
66
+ elsif name.start_with? '%'
67
+ :internal
68
+ elsif name[0].downcase != name[0]
69
+ :const
70
+ else
71
+ :lvar
72
+ end
73
+ end
74
+
75
+ def has?(name)
76
+ case BaseScope.type_by_name name
77
+ when :lvar
78
+ @local_variables.include? name
79
+ when :const
80
+ @binding.eval("#{name};true") rescue false
81
+ when :gvar, :cvar, :ivar
82
+ true
83
+ when :internal
84
+ true
85
+ end
86
+ end
87
+ end
88
+
89
+ class Scope < BaseScope
90
+ attr_reader :parent, :jump_branches, :mergeable_changes, :level
91
+
92
+ def self.from_binding(binding) = new(BaseScope.new(binding, binding.eval('self')))
93
+
94
+ def initialize(parent, table = {}, trace_cvar: true, trace_ivar: true, trace_lvar: true, passthrough: false)
95
+ @table = table
96
+ @parent = parent
97
+ @level = parent.level + (passthrough ? 0 : 1)
98
+ @trace_cvar = trace_cvar
99
+ @trace_ivar = trace_ivar
100
+ @trace_lvar = trace_lvar
101
+ @passthrough = passthrough
102
+ @terminated = false
103
+ @jump_branches = []
104
+ @mergeable_changes = @changes = table.transform_values { [level, _1] }
105
+ end
106
+
107
+ def mutable? = true
108
+
109
+ def terminated?
110
+ @terminated
111
+ end
112
+
113
+ def terminate_with(type, value)
114
+ return if terminated?
115
+ store_jump type, value, @mergeable_changes
116
+ terminate
117
+ end
118
+
119
+ def store_jump(type, value, changes)
120
+ return if terminated?
121
+ if has_own?(type)
122
+ changes[type] = [level, value]
123
+ @jump_branches << changes
124
+ elsif @parent.mutable?
125
+ @parent.store_jump(type, value, changes)
126
+ end
127
+ end
128
+
129
+ def terminate
130
+ return if terminated?
131
+ @terminated = true
132
+ @changes = @mergeable_changes.dup
133
+ end
134
+
135
+ def branch_table_clone() = @tables.last.dup
136
+
137
+ def trace?(name)
138
+ return false unless @parent
139
+ type = BaseScope.type_by_name(name)
140
+ type == :cvar ? @trace_cvar : type == :ivar ? @trace_ivar : type == :lvar ? @trace_lvar : true
141
+ end
142
+
143
+ def level_of(name)
144
+ variable_level, = @changes[name]
145
+ variable_level || parent.level_of(name)
146
+ end
147
+
148
+ def [](name)
149
+ level, value = @changes[name]
150
+ if level
151
+ value
152
+ elsif trace? name
153
+ @parent[name] if trace? name
154
+ end
155
+ end
156
+
157
+ def []=(name, value)
158
+ variable_level = level_of(name) || level
159
+ @changes[name] = [variable_level, value]
160
+ end
161
+
162
+ def self_type
163
+ self[SELF]
164
+ end
165
+
166
+ def local_variables
167
+ lvar_keys = @changes.keys.select do |name|
168
+ BaseScope.type_by_name(name) == :lvar
169
+ end
170
+ lvar_keys |= @parent.local_variables if @trace_lvar
171
+ lvar_keys
172
+ end
173
+
174
+ def merge_jumps
175
+ if terminated?
176
+ @terminated = false
177
+ @changes = @mergeable_changes
178
+ merge @jump_branches
179
+ @terminated = true
180
+ else
181
+ merge [*@jump_branches, {}]
182
+ end
183
+ end
184
+
185
+ def conditional(&block)
186
+ run_branches(block, ->(_s) {}).first || KatakataIrb::Types::NIL
187
+ end
188
+
189
+ def never(&block)
190
+ block.call Scope.new(self, { BREAK_RESULT => nil, NEXT_RESULT => nil, PATTERNMATCH_BREAK => nil, RETURN_RESULT => nil, RAISE_BREAK => nil }, passthrough: true)
191
+ end
192
+
193
+ def run(*args, **option)
194
+ scope = Scope.new(self, *args, **option)
195
+ yield scope
196
+ merge_jumps
197
+ update scope
198
+ end
199
+
200
+ def run_branches(*blocks)
201
+ results = []
202
+ branches = []
203
+ blocks.each do |block|
204
+ scope = Scope.new self, passthrough: true
205
+ result = block.call scope
206
+ next if scope.terminated?
207
+ results << result
208
+ branches << scope.mergeable_changes
209
+ end
210
+ terminate if branches.empty?
211
+ merge branches
212
+ results
213
+ end
214
+
215
+ def has?(name)
216
+ has_own?(name) || (trace?(name) && @parent.has?(name))
217
+ end
218
+
219
+ def has_own?(name)
220
+ @changes.key? name
221
+ end
222
+
223
+ def update(child_scope)
224
+ current_level = level
225
+ child_scope.mergeable_changes.each do |name, (level, value)|
226
+ self[name] = value if level <= current_level
227
+ end
228
+ end
229
+
230
+ protected
231
+
232
+ def merge(branches)
233
+ current_level = level
234
+ merge = {}
235
+ branches.each do |changes|
236
+ changes.each do |name, (level, value)|
237
+ next if current_level < level
238
+ (merge[name] ||= []) << value
239
+ end
240
+ end
241
+ merge.each do |name, values|
242
+ values << self[name] unless values.size == branches.size
243
+ values.compact!
244
+ self[name] = KatakataIrb::Types::UnionType[*values.compact] unless values.empty?
245
+ end
246
+ end
247
+ end
248
+ end