solargraph 0.18.2 → 0.18.3

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph.rb +33 -28
  3. data/lib/solargraph/api_map.rb +997 -1044
  4. data/lib/solargraph/api_map/source_to_yard.rb +4 -3
  5. data/lib/solargraph/diagnostics/rubocop.rb +4 -3
  6. data/lib/solargraph/language_server/host.rb +140 -70
  7. data/lib/solargraph/language_server/message/base.rb +1 -0
  8. data/lib/solargraph/language_server/message/client.rb +6 -2
  9. data/lib/solargraph/language_server/message/text_document/completion.rb +34 -39
  10. data/lib/solargraph/language_server/message/text_document/definition.rb +1 -1
  11. data/lib/solargraph/language_server/message/text_document/did_close.rb +1 -0
  12. data/lib/solargraph/language_server/message/text_document/did_save.rb +1 -3
  13. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +1 -1
  14. data/lib/solargraph/language_server/message/text_document/hover.rb +25 -30
  15. data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +1 -1
  16. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +8 -7
  17. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +1 -1
  18. data/lib/solargraph/language_server/transport/socket.rb +15 -17
  19. data/lib/solargraph/library.rb +34 -16
  20. data/lib/solargraph/node_methods.rb +96 -96
  21. data/lib/solargraph/pin.rb +1 -0
  22. data/lib/solargraph/pin/base.rb +2 -1
  23. data/lib/solargraph/pin/base_variable.rb +45 -5
  24. data/lib/solargraph/pin/block_parameter.rb +5 -2
  25. data/lib/solargraph/pin/method.rb +22 -0
  26. data/lib/solargraph/pin/namespace.rb +32 -2
  27. data/lib/solargraph/pin/reference.rb +21 -0
  28. data/lib/solargraph/pin/yard_object.rb +9 -0
  29. data/lib/solargraph/shell.rb +136 -136
  30. data/lib/solargraph/source.rb +134 -188
  31. data/lib/solargraph/source/change.rb +70 -0
  32. data/lib/solargraph/source/fragment.rb +120 -66
  33. data/lib/solargraph/source/position.rb +41 -0
  34. data/lib/solargraph/source/updater.rb +48 -0
  35. data/lib/solargraph/version.rb +3 -3
  36. data/lib/solargraph/workspace/config.rb +4 -9
  37. data/lib/solargraph/yard_map/core_docs.rb +0 -1
  38. metadata +5 -2
@@ -0,0 +1,70 @@
1
+ module Solargraph
2
+ class Source
3
+ # A change to be applied to text.
4
+ #
5
+ class Change
6
+ # @return [Range]
7
+ attr_reader :range
8
+
9
+ # @return [String]
10
+ attr_reader :new_text
11
+
12
+ # @param range [Range] The starting and ending positions of the change.
13
+ # If nil, the original text will be overwritten.
14
+ # @param new_text [String] The text to be changed.
15
+ def initialize range, new_text
16
+ @range = range
17
+ @new_text = new_text
18
+ end
19
+
20
+ # Write the change to the specified text.
21
+ #
22
+ # @param text [String] The text to be changed.
23
+ # @param nullable [Boolean] If true, minor changes that could generate
24
+ # syntax errors will be repaired.
25
+ # @return [String] The updated text.
26
+ def write text, nullable = false
27
+ if nullable and !range.nil? and new_text.match(/[\.\[\{\(@\$:]$/)
28
+ commit text, "#{new_text[0..-2]} "
29
+ elsif range.nil?
30
+ new_text
31
+ else
32
+ commit text, new_text
33
+ end
34
+ end
35
+
36
+ # Repair an update by replacing the new text with similarly formatted
37
+ # whitespace.
38
+ #
39
+ # @param text [String] The text to be changed.
40
+ # @return [String] The updated text.
41
+ def repair text
42
+ fixed = new_text.gsub(/[^\s]/, ' ')
43
+ if range.nil?
44
+ fixed
45
+ else
46
+ commit text, fixed
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def commit text, insert
53
+ start_offset = get_offset(text, range.start.line, range.start.character)
54
+ end_offset = get_offset(text, range.end.line, range.end.character)
55
+ (start_offset == 0 ? '' : text[0..start_offset-1].to_s) + insert.force_encoding('utf-8') + text[end_offset..-1].to_s
56
+ end
57
+
58
+ def get_offset text, line, column
59
+ offset = 0
60
+ feed = 0
61
+ text.lines.each do |l|
62
+ break if line == feed
63
+ offset += l.length
64
+ feed += 1
65
+ end
66
+ offset + column
67
+ end
68
+ end
69
+ end
70
+ end
@@ -33,11 +33,6 @@ module Solargraph
33
33
  #
34
34
  # @return [String]
35
35
  def namespace
36
- # if @namespace.nil?
37
- # base = @source.parent_node_from(line, column, :class, :module, :def, :defs)
38
- # @namespace ||= @source.namespace_for(base)
39
- # end
40
- # @namespace
41
36
  if @namespace.nil?
42
37
  parts = []
43
38
  @tree.each do |n|
@@ -56,6 +51,17 @@ module Solargraph
56
51
  @argument ||= !signature_position.nil?
57
52
  end
58
53
 
54
+ def chained?
55
+ if @chained.nil?
56
+ @chained = false
57
+ @tree.each do |n|
58
+ @chained = true if n.type == :send
59
+ break
60
+ end
61
+ end
62
+ @chained
63
+ end
64
+
59
65
  # @return [Fragment]
60
66
  def recipient
61
67
  return nil if signature_position.nil?
@@ -90,6 +96,14 @@ module Solargraph
90
96
  @signature ||= signature_data[1]
91
97
  end
92
98
 
99
+ def valid?
100
+ @source.parsed?
101
+ end
102
+
103
+ def broken?
104
+ !valid?
105
+ end
106
+
93
107
  # Get the signature before the current word. Given the signature
94
108
  # `String.new.split`, the base is `String.new`.
95
109
  #
@@ -116,6 +130,26 @@ module Solargraph
116
130
  @base
117
131
  end
118
132
 
133
+ # @return [String]
134
+ def root
135
+ @root ||= signature.split('.').first
136
+ end
137
+
138
+ # @return [String]
139
+ def chain
140
+ @chain ||= signature.split('.')[1..-1].join('.')
141
+ end
142
+
143
+ # @return [String]
144
+ def base_chain
145
+ @base_chain ||= signature.split('.')[1..-2].join('.')
146
+ end
147
+
148
+ # @return [String]
149
+ def whole_chain
150
+ @whole_chain ||= whole_signature.split('.')[1..-1].join('.')
151
+ end
152
+
119
153
  # Get the remainder of the word after the current offset. Given the text
120
154
  # `foobar` with an offset of 3, the remainder is `bar`.
121
155
  #
@@ -159,8 +193,6 @@ module Solargraph
159
193
  #
160
194
  # @return [Boolean]
161
195
  def string?
162
- # @string = @source.string_at?(offset) if @string.nil?
163
- # @string
164
196
  @string ||= (node.type == :str or node.type == :dstr)
165
197
  end
166
198
 
@@ -191,25 +223,91 @@ module Solargraph
191
223
  # from the current offset.
192
224
  #
193
225
  # @return [Array<Solargraph::Pin::LocalVariable>]
194
- def local_variable_pins
195
- @local_variable_pins ||= @source.local_variable_pins.select{|pin| pin.visible_from?(node)}
226
+ def local_variable_pins name = nil
227
+ @local_variable_pins ||= prefer_non_nil_variables(@source.local_variable_pins.select{|pin| pin.visible_from?(node)})
228
+ return @local_variable_pins if name.nil?
229
+ @local_variable_pins.select{|pin| pin.name == name}
230
+ end
231
+
232
+ def calculated_signature
233
+ @calculated_signature ||= calculate
234
+ end
235
+
236
+ def calculated_whole_signature
237
+ @calculated_whole_signature ||= calculated_signature + remainder
238
+ end
239
+
240
+ def calculated_base
241
+ if @calculated_base.nil?
242
+ @calculated_base = calculated_signature[0..-2] if calculated_signature.end_with?('.')
243
+ @calculated_base ||= calculated_signature.split('.')[0..-2].join('.')
244
+ end
245
+ @calculated_base
196
246
  end
197
247
 
198
248
  private
199
249
 
250
+ def calculate
251
+ return signature if signature.empty? or signature.nil?
252
+ if signature.start_with?('.')
253
+ return signature if column < 2
254
+ # @todo Smelly exceptional case for arrays
255
+ return signature.sub(/^\.\[\]/, 'Array.new') if signature.start_with?('.[].')
256
+ pn = @source.node_at(line, column - 2)
257
+ unless pn.nil?
258
+ literal = infer_literal_node_type(pn)
259
+ unless literal.nil?
260
+ return "#{literal}.new#{signature}"
261
+ end
262
+ end
263
+ return signature
264
+ end
265
+ # @todo Smelly exceptional case for integers
266
+ base, rest = signature.split('.', 2)
267
+ base.sub!(/^[0-9]+?$/, 'Integer.new')
268
+ var = local_variable_pins(base).first
269
+ unless var.nil?
270
+ done = []
271
+ until var.nil?
272
+ break if done.include?(var)
273
+ done.push var
274
+ type = var.calculated_signature
275
+ break if type.nil?
276
+ base = type
277
+ var = local_variable_pins(base).first
278
+ end
279
+ end
280
+ base = @source.qualify(base, namespace)
281
+ base + (rest.nil? ? '' : ".#{rest}")
282
+ end
283
+
284
+ # @todo DRY this method. It exists in ApiMap.
285
+ # @return [Array<Solargraph::Pin::Base>]
286
+ def prefer_non_nil_variables pins
287
+ result = []
288
+ nil_pins = []
289
+ pins.each do |pin|
290
+ if pin.nil_assignment? and pin.return_type.nil?
291
+ nil_pins.push pin
292
+ else
293
+ result.push pin
294
+ end
295
+ end
296
+ result + nil_pins
297
+ end
298
+
200
299
  # @return [Integer]
201
300
  def offset
202
- if @offset.nil?
203
- @offset = 0
204
- feed = 0
205
- @code.lines.each { |l|
206
- break if line == feed
207
- @offset += l.length
208
- feed += 1
209
- }
210
- @offset += column
211
- end
212
- @offset
301
+ @offset ||= get_offset(line, column)
302
+ end
303
+
304
+ def get_offset line, column
305
+ Position.line_char_to_offset(@code, line, column)
306
+ end
307
+
308
+ def get_position_at(offset)
309
+ pos = Position.from_offset(@code, offset)
310
+ [pos.line, pos.character]
213
311
  end
214
312
 
215
313
  def signature_data
@@ -228,6 +326,7 @@ module Solargraph
228
326
  unless !in_whitespace and string?
229
327
  break if brackets > 0 or parens > 0 or squares > 0
230
328
  char = @code[index, 1]
329
+ break if char.nil? # @todo Is this the right way to handle this?
231
330
  if brackets.zero? and parens.zero? and squares.zero? and [' ', "\r", "\n", "\t"].include?(char)
232
331
  in_whitespace = true
233
332
  else
@@ -267,22 +366,7 @@ module Solargraph
267
366
  end
268
367
  index -= 1
269
368
  end
270
- if signature.start_with?('.')
271
- # @todo Smelly exceptional case for arrays
272
- if signature.start_with?('.[].')
273
- signature.sub!(/^\.\[\]/, 'Array.new')
274
- else
275
- line, col = get_position_at(index - 1)
276
- pn = @source.node_at(line, col)
277
- unless pn.nil?
278
- literal = infer_literal_node_type(pn)
279
- unless literal.nil?
280
- signature = "#{literal}.new#{signature}"
281
- # @todo Determine the index from the beginning of the literal node?
282
- end
283
- end
284
- end
285
- end
369
+ # @todo Smelly exceptional case for numbers
286
370
  [index + 1, signature]
287
371
  end
288
372
 
@@ -364,36 +448,6 @@ module Solargraph
364
448
  @code[index..cursor-1]
365
449
  end
366
450
 
367
- def get_position_at(offset)
368
- cursor = 0
369
- line = 0
370
- col = nil
371
- @code.lines.each do |l|
372
- if cursor + l.length > offset
373
- col = offset - cursor
374
- break
375
- end
376
- if cursor + l.length == offset
377
- if l.end_with?("\n")
378
- col = 0
379
- line += 1
380
- break
381
- else
382
- col = l.length
383
- break
384
- end
385
- end
386
- if cursor + l.length - 1 == offset and !l.end_with?("\n")
387
- col = l.length - 1
388
- break
389
- end
390
- cursor += l.length
391
- line += 1
392
- end
393
- raise "Invalid offset" if col.nil?
394
- [line, col]
395
- end
396
-
397
451
  def signature_position
398
452
  if @signature_position.nil?
399
453
  open_parens = 0
@@ -1,4 +1,5 @@
1
1
  module Solargraph
2
+
2
3
  class Source
3
4
  class Position
4
5
  # @return [Integer]
@@ -21,6 +22,46 @@ module Solargraph
21
22
  character: character
22
23
  }
23
24
  end
25
+
26
+ def self.to_offset text, position
27
+ result = 0
28
+ feed = 0
29
+ line = position.line
30
+ column = position.character
31
+ text.lines.each do |l|
32
+ line_length = l.length
33
+ char_length = l.chomp.length
34
+ if feed == line
35
+ result += column
36
+ break
37
+ end
38
+ result += line_length
39
+ feed += 1
40
+ end
41
+ result
42
+ end
43
+
44
+ def self.line_char_to_offset text, line, character
45
+ to_offset(text, Position.new(line, character))
46
+ end
47
+
48
+ def self.from_offset text, offset
49
+ cursor = 0
50
+ line = 0
51
+ character = nil
52
+ text.lines.each do |l|
53
+ line_length = l.length
54
+ char_length = l.chomp.length
55
+ if cursor + char_length >= offset
56
+ character = offset - cursor
57
+ break
58
+ end
59
+ cursor += line_length
60
+ line += 1
61
+ end
62
+ raise InvalidOffsetError if character.nil?
63
+ Position.new(line, character)
64
+ end
24
65
  end
25
66
  end
26
67
  end
@@ -0,0 +1,48 @@
1
+ module Solargraph
2
+ class Source
3
+ # Updaters contain changes to be applied to a source. The source applies
4
+ # the update via the Source#synchronize method.
5
+ #
6
+ class Updater
7
+ # @return [String]
8
+ attr_reader :filename
9
+
10
+ # @return [Integer]
11
+ attr_reader :version
12
+
13
+ # @return [Array<Change>]
14
+ attr_reader :changes
15
+
16
+ # @param filename [String] The file to update.
17
+ # @param version [Integer] A version number associated with this update.
18
+ # @param changes [Array<Solargraph::Source::Change>] The changes.
19
+ def initialize filename, version, changes
20
+ @filename = filename
21
+ @version = version
22
+ @changes = changes
23
+ @input = nil
24
+ @did_nullify = nil
25
+ @output = nil
26
+ end
27
+
28
+ def write text, nullable = false
29
+ can_nullify = (nullable and changes.length == 1)
30
+ return @output if @input == text and can_nullify == @did_nullify
31
+ @input = text
32
+ @output = text
33
+ @did_nullify = can_nullify
34
+ changes.each do |ch|
35
+ @output = ch.write(@output, can_nullify)
36
+ end
37
+ @output
38
+ end
39
+
40
+ def repair text
41
+ changes.each do |ch|
42
+ text = ch.repair(text)
43
+ end
44
+ text
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,3 +1,3 @@
1
- module Solargraph
2
- VERSION = '0.18.2'
3
- end
1
+ module Solargraph
2
+ VERSION = '0.18.3'
3
+ end
@@ -16,15 +16,10 @@ module Solargraph
16
16
  unless @workspace.nil?
17
17
  sfile = File.join(@workspace, '.solargraph.yml')
18
18
  if File.file?(sfile)
19
- begin
20
- @raw_data = YAML.load(File.read(sfile))
21
- conf = YAML.load(File.read(sfile))
22
- include_globs = conf['include'] || include_globs
23
- exclude_globs = conf['exclude'] || []
24
- rescue Exception => e
25
- STDERR.puts "Unable to read .solargraph.yml: #{e.class} #{e.message}"
26
- @raw_data = {}
27
- end
19
+ @raw_data = YAML.load(File.read(sfile))
20
+ conf = YAML.load(File.read(sfile))
21
+ include_globs = conf['include'] || include_globs
22
+ exclude_globs = conf['exclude'] || []
28
23
  end
29
24
  end
30
25
  @raw_data ||= {}