katakata_irb 0.1.4 → 0.1.6

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: df817c7e7806e6ea21aa82e14a694130b0db5dd68c74ea84790b90ad3b895fb7
4
+ data.tar.gz: 9faa1d72b27f35bccc5b4d2dcc5c9f50b4dd9946ba5d63374d3083e933e62c38
5
5
  SHA512:
6
- metadata.gz: be9ed49c8de27772f7ec154df136134e9d5319a851fe392656a67d3c49069ed040a41b2af8deb6e81577c0c59c6d5bc0918a8165e09ccab2bce15fd6f09abba8
7
- data.tar.gz: 8d9554e6532488742e156d39e7ee63c3c2461900db42badfd0d49990e35769b1e0f34a8cd746d5aab57839d3f31e894a081ec42389cd2383dafab5043d3fa578
6
+ metadata.gz: 9bec77d0fd1f9484278541e48a964c683ef2546b8a5b107e731d7979b634c4c8fb9ce2b68261603fc5f4ad8eb83cfb3018e11881a2ecd4096d268e7be0e97405
7
+ data.tar.gz: b6520a88ad8644423f24e7c02e783bf1cb388b3a00d2c16151b97e08598f1fd80f55f8346562d00c9d2ec0e2b619d4f4db23eeba5834c39b9ebfc72ce765cbdb
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.6)
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,256 @@
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, :lvars
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
+ @lvars = Set.new
105
+ @mergeable_changes = @changes = table.transform_values { [level, _1] }
106
+ end
107
+
108
+ def mutable? = true
109
+
110
+ def terminated?
111
+ @terminated
112
+ end
113
+
114
+ def terminate_with(type, value)
115
+ return if terminated?
116
+ store_jump type, value, @mergeable_changes
117
+ terminate
118
+ end
119
+
120
+ def store_jump(type, value, changes)
121
+ return if terminated?
122
+ if has_own?(type)
123
+ changes[type] = [level, value]
124
+ @jump_branches << changes
125
+ elsif @parent.mutable?
126
+ @parent.store_jump(type, value, changes)
127
+ end
128
+ end
129
+
130
+ def terminate
131
+ return if terminated?
132
+ @terminated = true
133
+ @changes = @mergeable_changes.dup
134
+ end
135
+
136
+ def branch_table_clone() = @tables.last.dup
137
+
138
+ def trace?(name)
139
+ return false unless @parent
140
+ type = BaseScope.type_by_name(name)
141
+ type == :cvar ? @trace_cvar : type == :ivar ? @trace_ivar : type == :lvar ? @trace_lvar : true
142
+ end
143
+
144
+ def level_of(name)
145
+ variable_level, = @changes[name]
146
+ variable_level || parent.level_of(name)
147
+ end
148
+
149
+ def [](name)
150
+ level, value = @changes[name]
151
+ if level
152
+ value
153
+ elsif trace? name
154
+ @parent[name] if trace? name
155
+ end
156
+ end
157
+
158
+ def []=(name, value)
159
+ variable_level = level_of(name) || level
160
+ @lvars << name if level == variable_level && BaseScope.type_by_name(name) == :lvar
161
+ @changes[name] = [variable_level, value]
162
+ end
163
+
164
+ def self_type
165
+ self[SELF]
166
+ end
167
+
168
+ def local_variables
169
+ lvar_keys = @changes.keys.select do |name|
170
+ BaseScope.type_by_name(name) == :lvar
171
+ end
172
+ lvar_keys |= @parent.local_variables if @trace_lvar
173
+ lvar_keys
174
+ end
175
+
176
+ def merge_jumps
177
+ if terminated?
178
+ @terminated = false
179
+ @changes = @mergeable_changes
180
+ merge @jump_branches
181
+ @terminated = true
182
+ else
183
+ merge [*@jump_branches, {}]
184
+ end
185
+ end
186
+
187
+ def conditional(&block)
188
+ run_branches(block, ->(_s) {}).first || KatakataIrb::Types::NIL
189
+ end
190
+
191
+ def never(&block)
192
+ block.call Scope.new(self, { BREAK_RESULT => nil, NEXT_RESULT => nil, PATTERNMATCH_BREAK => nil, RETURN_RESULT => nil, RAISE_BREAK => nil }, passthrough: true)
193
+ end
194
+
195
+ def run(*args, **option)
196
+ scope = Scope.new(self, *args, **option)
197
+ yield scope
198
+ merge_jumps
199
+ update scope
200
+ end
201
+
202
+ def touch_lvars(lvars)
203
+ lvars.each { self[_1] = self[_1] || KatakataIrb::Types::NIL }
204
+ end
205
+
206
+ def run_branches(*blocks)
207
+ results = []
208
+ branches = []
209
+ blocks.each do |block|
210
+ scope = Scope.new self, passthrough: true
211
+ result = block.call scope
212
+ touch_lvars scope.lvars if scope.level == level
213
+ next if scope.terminated?
214
+ results << result
215
+ branches << scope.mergeable_changes
216
+ end
217
+ terminate if branches.empty?
218
+ merge branches
219
+ results
220
+ end
221
+
222
+ def has?(name)
223
+ has_own?(name) || (trace?(name) && @parent.has?(name))
224
+ end
225
+
226
+ def has_own?(name)
227
+ @changes.key? name
228
+ end
229
+
230
+ def update(child_scope)
231
+ current_level = level
232
+ touch_lvars child_scope.lvars if child_scope.level == current_level
233
+ child_scope.mergeable_changes.each do |name, (level, value)|
234
+ self[name] = value if level <= current_level
235
+ end
236
+ end
237
+
238
+ protected
239
+
240
+ def merge(branches)
241
+ current_level = level
242
+ merge = {}
243
+ branches.each do |changes|
244
+ changes.each do |name, (level, value)|
245
+ next if current_level < level
246
+ (merge[name] ||= []) << value
247
+ end
248
+ end
249
+ merge.each do |name, values|
250
+ values << self[name] unless values.size == branches.size
251
+ values.compact!
252
+ self[name] = KatakataIrb::Types::UnionType[*values.compact] unless values.empty?
253
+ end
254
+ end
255
+ end
256
+ end