solargraph 0.26.1 → 0.27.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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph.rb +5 -2
  3. data/lib/solargraph/api_map.rb +236 -234
  4. data/lib/solargraph/api_map/store.rb +18 -53
  5. data/lib/solargraph/bundle.rb +22 -0
  6. data/lib/solargraph/complex_type.rb +9 -5
  7. data/lib/solargraph/complex_type/type_methods.rb +113 -0
  8. data/lib/solargraph/complex_type/unique_type.rb +35 -0
  9. data/lib/solargraph/core_fills.rb +1 -0
  10. data/lib/solargraph/diagnostics.rb +6 -4
  11. data/lib/solargraph/diagnostics/base.rb +3 -0
  12. data/lib/solargraph/diagnostics/require_not_found.rb +2 -1
  13. data/lib/solargraph/diagnostics/rubocop.rb +21 -6
  14. data/lib/solargraph/diagnostics/type_not_defined.rb +4 -3
  15. data/lib/solargraph/diagnostics/update_errors.rb +18 -0
  16. data/lib/solargraph/language_server/host.rb +90 -222
  17. data/lib/solargraph/language_server/host/cataloger.rb +68 -0
  18. data/lib/solargraph/language_server/host/diagnoser.rb +85 -0
  19. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +35 -24
  20. data/lib/solargraph/language_server/message/text_document/completion.rb +6 -8
  21. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +1 -1
  22. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +0 -1
  23. data/lib/solargraph/language_server/transport/socket.rb +4 -6
  24. data/lib/solargraph/language_server/transport/stdio.rb +4 -6
  25. data/lib/solargraph/library.rb +152 -99
  26. data/lib/solargraph/live_map.rb +1 -1
  27. data/lib/solargraph/location.rb +28 -0
  28. data/lib/solargraph/pin.rb +2 -0
  29. data/lib/solargraph/pin/attribute.rb +26 -12
  30. data/lib/solargraph/pin/base.rb +15 -35
  31. data/lib/solargraph/pin/base_variable.rb +7 -15
  32. data/lib/solargraph/pin/block.rb +5 -9
  33. data/lib/solargraph/pin/block_parameter.rb +9 -7
  34. data/lib/solargraph/pin/conversions.rb +5 -5
  35. data/lib/solargraph/pin/duck_method.rb +1 -1
  36. data/lib/solargraph/pin/instance_variable.rb +0 -4
  37. data/lib/solargraph/pin/keyword.rb +4 -0
  38. data/lib/solargraph/pin/localized.rb +5 -3
  39. data/lib/solargraph/pin/method.rb +11 -0
  40. data/lib/solargraph/pin/namespace.rb +7 -3
  41. data/lib/solargraph/pin/proxy_type.rb +3 -7
  42. data/lib/solargraph/pin/reference.rb +2 -2
  43. data/lib/solargraph/pin/symbol.rb +1 -1
  44. data/lib/solargraph/pin/yard_pin/method.rb +2 -2
  45. data/lib/solargraph/pin/yard_pin/namespace.rb +16 -7
  46. data/lib/solargraph/position.rb +103 -0
  47. data/lib/solargraph/range.rb +70 -0
  48. data/lib/solargraph/source.rb +159 -328
  49. data/lib/solargraph/source/chain.rb +38 -55
  50. data/lib/solargraph/source/chain/call.rb +47 -29
  51. data/lib/solargraph/source/chain/class_variable.rb +2 -2
  52. data/lib/solargraph/source/chain/constant.rb +3 -3
  53. data/lib/solargraph/source/chain/definition.rb +7 -3
  54. data/lib/solargraph/source/chain/global_variable.rb +1 -1
  55. data/lib/solargraph/source/chain/head.rb +22 -9
  56. data/lib/solargraph/source/chain/instance_variable.rb +2 -2
  57. data/lib/solargraph/source/chain/link.rb +4 -4
  58. data/lib/solargraph/source/chain/literal.rb +1 -1
  59. data/lib/solargraph/source/chain/variable.rb +2 -2
  60. data/lib/solargraph/source/change.rb +0 -6
  61. data/lib/solargraph/source/cursor.rb +161 -0
  62. data/lib/solargraph/source/encoding_fixes.rb +1 -1
  63. data/lib/solargraph/source/node_chainer.rb +28 -21
  64. data/lib/solargraph/source/node_methods.rb +1 -1
  65. data/lib/solargraph/source/source_chainer.rb +217 -0
  66. data/lib/solargraph/source_map.rb +138 -0
  67. data/lib/solargraph/source_map/clip.rb +123 -0
  68. data/lib/solargraph/{source → source_map}/completion.rb +3 -3
  69. data/lib/solargraph/{source → source_map}/mapper.rb +143 -41
  70. data/lib/solargraph/version.rb +1 -1
  71. data/lib/solargraph/workspace.rb +13 -20
  72. data/lib/solargraph/yard_map.rb +77 -48
  73. metadata +17 -11
  74. data/lib/solargraph/basic_type.rb +0 -33
  75. data/lib/solargraph/basic_type_methods.rb +0 -111
  76. data/lib/solargraph/source/call_chainer.rb +0 -273
  77. data/lib/solargraph/source/fragment.rb +0 -342
  78. data/lib/solargraph/source/location.rb +0 -23
  79. data/lib/solargraph/source/position.rb +0 -95
  80. data/lib/solargraph/source/range.rb +0 -64
@@ -6,7 +6,8 @@ module Solargraph
6
6
  class TypeNotDefined < Base
7
7
  def diagnose source, api_map
8
8
  result = []
9
- source.pins.select{|p| p.kind == Pin::METHOD or p.kind == Pin::ATTRIBUTE}.each do |pin|
9
+ api_map.document_symbols(source.filename).each do |pin|
10
+ next unless pin.kind == Pin::METHOD or pin.kind == Pin::ATTRIBUTE
10
11
  result.concat check_return_type(pin, api_map, source)
11
12
  result.concat check_param_types(pin, api_map, source)
12
13
  result.concat check_param_tags(pin, api_map, source)
@@ -95,11 +96,11 @@ module Solargraph
95
96
  tags.any?{|t| t.name == param}
96
97
  end
97
98
 
98
- # @param position [Solargraph::Source::Position]
99
+ # @param position [Solargraph::Position]
99
100
  # @param source [Solargraph::Source]
100
101
  # @return [Integer]
101
102
  def last_character position, source
102
- cursor = Source::Position.to_offset(source.code, position)
103
+ cursor = Position.to_offset(source.code, position)
103
104
  source.code.index(/[\r\n]/, cursor) || source.code.length
104
105
  end
105
106
  end
@@ -0,0 +1,18 @@
1
+ module Solargraph
2
+ module Diagnostics
3
+ class UpdateErrors < Base
4
+ def diagnose source, api_map
5
+ result = []
6
+ source.error_ranges.each do |range|
7
+ result.push(
8
+ range: range.to_hash,
9
+ severity: Diagnostics::Severities::ERROR,
10
+ source: 'Solargraph',
11
+ message: 'Syntax error'
12
+ )
13
+ end
14
+ result
15
+ end
16
+ end
17
+ end
18
+ end
@@ -8,23 +8,21 @@ module Solargraph
8
8
  # safety for multi-threaded transports.
9
9
  #
10
10
  class Host
11
+ autoload :Diagnoser, 'solargraph/language_server/host/diagnoser'
12
+ autoload :Cataloger, 'solargraph/language_server/host/cataloger'
13
+
11
14
  include Solargraph::LanguageServer::UriHelpers
12
15
 
13
16
  def initialize
14
- @change_semaphore = Mutex.new
15
17
  @cancel_semaphore = Mutex.new
16
18
  @buffer_semaphore = Mutex.new
17
19
  @register_semaphore = Mutex.new
18
- @change_queue = []
19
- @diagnostics_queue = []
20
20
  @cancel = []
21
21
  @buffer = ''
22
22
  @stopped = false
23
23
  @next_request_id = 0
24
24
  @dynamic_capabilities = Set.new
25
25
  @registered_capabilities = Set.new
26
- start_change_thread
27
- start_diagnostics_thread
28
26
  end
29
27
 
30
28
  # Update the configuration options with the provided hash.
@@ -97,24 +95,19 @@ module Solargraph
97
95
  # @param uri [String] The file uri.
98
96
  def create uri
99
97
  filename = uri_to_file(uri)
100
- @change_semaphore.synchronize do
101
- library.create_from_disk filename
102
- end
98
+ library.create_from_disk filename
103
99
  end
104
100
 
105
101
  # Delete the specified file from the library.
106
102
  #
107
103
  # @param uri [String] The file uri.
108
104
  def delete uri
109
- @change_semaphore.synchronize do
110
- filename = uri_to_file(uri)
111
- library.delete filename
112
- # Remove diagnostics for deleted files
113
- send_notification "textDocument/publishDiagnostics", {
114
- uri: uri,
115
- diagnostics: []
116
- }
117
- end
105
+ filename = uri_to_file(uri)
106
+ library.delete filename
107
+ send_notification "textDocument/publishDiagnostics", {
108
+ uri: uri,
109
+ diagnostics: []
110
+ }
118
111
  end
119
112
 
120
113
  # Open the specified file in the library.
@@ -123,10 +116,8 @@ module Solargraph
123
116
  # @param text [String] The contents of the file.
124
117
  # @param version [Integer] A version number.
125
118
  def open uri, text, version
126
- @change_semaphore.synchronize do
127
- library.open uri_to_file(uri), text, version
128
- @diagnostics_queue.push uri
129
- end
119
+ library.open uri_to_file(uri), text, version
120
+ diagnoser.schedule uri
130
121
  end
131
122
 
132
123
  # True if the specified file is currently open in the library.
@@ -134,52 +125,33 @@ module Solargraph
134
125
  # @param uri [String]
135
126
  # @return [Boolean]
136
127
  def open? uri
137
- result = nil
138
- @change_semaphore.synchronize do
139
- result = unsafe_open?(uri)
140
- end
141
- result
128
+ unsafe_open?(uri)
142
129
  end
143
130
 
144
131
  # Close the file specified by the URI.
145
132
  #
146
133
  # @param uri [String]
147
134
  def close uri
148
- @change_semaphore.synchronize do
149
- library.close uri_to_file(uri)
150
- @diagnostics_queue.push uri
151
- end
135
+ library.close uri_to_file(uri)
136
+ diagnoser.schedule uri
152
137
  end
153
138
 
154
139
  def save params
155
- @change_semaphore.synchronize do
156
- uri = params['textDocument']['uri']
157
- filename = uri_to_file(uri)
158
- version = params['textDocument']['version']
159
- @change_queue.delete_if do |change|
160
- return true if change['textDocument']['uri'] == uri and change['textDocument']['version'] <= version
161
- false
162
- end
163
- library.overwrite filename, version
164
- end
140
+ uri = params['textDocument']['uri']
141
+ filename = uri_to_file(uri)
142
+ version = params['textDocument']['version']
143
+ library.overwrite filename, version
144
+ end
145
+
146
+ def diagnose uri
147
+ library.diagnose uri_to_file(uri)
165
148
  end
166
149
 
167
150
  def change params
168
- @change_semaphore.synchronize do
169
- if unsafe_changing? params['textDocument']['uri']
170
- @change_queue.push params
171
- else
172
- source = library.checkout(uri_to_file(params['textDocument']['uri']))
173
- @change_queue.push params
174
- if params['textDocument']['version'] == source.version + params['contentChanges'].length
175
- updater = generate_updater(params)
176
- library.synchronize updater
177
- library.refresh
178
- @change_queue.pop
179
- @diagnostics_queue.push params['textDocument']['uri']
180
- end
181
- end
182
- end
151
+ updater = generate_updater(params)
152
+ library.update updater
153
+ cataloger.ping unless library.synchronized?
154
+ diagnoser.schedule params['textDocument']['uri']
183
155
  end
184
156
 
185
157
  # Queue a message to be sent to the client.
@@ -209,17 +181,17 @@ module Solargraph
209
181
  def prepare directory
210
182
  path = nil
211
183
  path = normalize_separators(directory) unless directory.nil?
212
- @change_semaphore.synchronize do
213
- begin
214
- @library = Solargraph::Library.load(path)
215
- rescue WorkspaceTooLargeError => e
216
- send_notification 'window/showMessage', {
217
- 'type' => Solargraph::LanguageServer::MessageTypes::WARNING,
218
- 'message' => "The workspace is too large to index (#{e.size} files, max #{e.max})"
219
- }
220
- @library = Solargraph::Library.load(nil)
221
- end
184
+ begin
185
+ @library = Solargraph::Library.load(path)
186
+ rescue WorkspaceTooLargeError => e
187
+ send_notification 'window/showMessage', {
188
+ 'type' => Solargraph::LanguageServer::MessageTypes::WARNING,
189
+ 'message' => "The workspace is too large to index (#{e.size} files, max #{e.max})"
190
+ }
191
+ @library = Solargraph::Library.load(nil)
222
192
  end
193
+ diagnoser.start
194
+ cataloger.start
223
195
  end
224
196
 
225
197
  # Send a notification to the client.
@@ -325,15 +297,17 @@ module Solargraph
325
297
  #
326
298
  # @return [Boolean]
327
299
  def changing? file_uri
328
- result = false
329
- @change_semaphore.synchronize do
330
- result = unsafe_changing?(file_uri)
331
- end
332
- result
300
+ unsafe_changing?(file_uri)
301
+ end
302
+
303
+ def synchronizing?
304
+ cataloger.synchronizing?
333
305
  end
334
306
 
335
307
  def stop
336
308
  @stopped = true
309
+ cataloger.stop
310
+ diagnoser.stop
337
311
  end
338
312
 
339
313
  def stopped?
@@ -342,12 +316,22 @@ module Solargraph
342
316
 
343
317
  def locate_pin params
344
318
  pin = nil
345
- @change_semaphore.synchronize do
346
- pin = library.locate_pin(params['data']['location']).first unless params['data']['location'].nil?
347
- # @todo Improve pin location
348
- if pin.nil? or pin.path != params['data']['path']
349
- pin = library.path_pins(params['data']['path']).first
350
- end
319
+ pin = nil
320
+ unless params['data']['location'].nil?
321
+ location = Location.new(
322
+ params['data']['location']['filename'],
323
+ Range.from_to(
324
+ params['data']['location']['range']['start']['line'],
325
+ params['data']['location']['range']['start']['character'],
326
+ params['data']['location']['range']['end']['line'],
327
+ params['data']['location']['range']['end']['character']
328
+ )
329
+ )
330
+ pin = library.locate_pin(location)
331
+ end
332
+ # @todo Improve pin location
333
+ if pin.nil? or pin.path != params['data']['path']
334
+ pin = library.path_pins(params['data']['path']).first
351
335
  end
352
336
  pin
353
337
  end
@@ -356,11 +340,7 @@ module Solargraph
356
340
  # @return [String]
357
341
  def read_text uri
358
342
  filename = uri_to_file(uri)
359
- text = nil
360
- @change_semaphore.synchronize do
361
- text = library.read_text(filename)
362
- end
363
- text
343
+ library.read_text(filename)
364
344
  end
365
345
 
366
346
  # @param filename [String]
@@ -369,9 +349,7 @@ module Solargraph
369
349
  # @return [Solargraph::ApiMap::Completion]
370
350
  def completions_at filename, line, column
371
351
  result = nil
372
- @change_semaphore.synchronize do
373
- result = library.completions_at filename, line, column
374
- end
352
+ result = library.completions_at filename, line, column
375
353
  result
376
354
  end
377
355
 
@@ -380,11 +358,7 @@ module Solargraph
380
358
  # @param column [Integer]
381
359
  # @return [Array<Solargraph::Pin::Base>]
382
360
  def definitions_at filename, line, column
383
- result = []
384
- @change_semaphore.synchronize do
385
- result = library.definitions_at(filename, line, column)
386
- end
387
- result
361
+ library.definitions_at(filename, line, column)
388
362
  end
389
363
 
390
364
  # @param filename [String]
@@ -392,53 +366,39 @@ module Solargraph
392
366
  # @param column [Integer]
393
367
  # @return [Array<Solargraph::Pin::Base>]
394
368
  def signatures_at filename, line, column
395
- result = nil
396
- @change_semaphore.synchronize do
397
- result = library.signatures_at(filename, line, column)
398
- end
399
- result
369
+ library.signatures_at(filename, line, column)
400
370
  end
401
371
 
402
372
  # @param filename [String]
403
373
  # @param line [Integer]
404
374
  # @param column [Integer]
405
- # @return [Array<Solargraph::Source::Range>]
375
+ # @return [Array<Solargraph::Range>]
406
376
  def references_from filename, line, column
407
- result = nil
408
- @change_semaphore.synchronize do
409
- result = library.references_from(filename, line, column)
410
- end
411
- result
377
+ result = library.references_from(filename, line, column)
412
378
  end
413
379
 
414
380
  # @param query [String]
415
381
  # @return [Array<Solargraph::Pin::Base>]
416
382
  def query_symbols query
417
- result = nil
418
- @change_semaphore.synchronize { result = library.query_symbols(query) }
419
- result
383
+ library.query_symbols(query)
420
384
  end
421
385
 
422
386
  # @param query [String]
423
387
  # @return [Array<String>]
424
388
  def search query
425
- result = nil
426
- @change_semaphore.synchronize { result = library.search(query) }
427
- result
389
+ library.search(query)
428
390
  end
429
391
 
430
392
  # @param query [String]
431
393
  # @return [String]
432
394
  def document query
433
- result = nil
434
- @change_semaphore.synchronize { result = library.document(query) }
435
- result
395
+ library.document(query)
436
396
  end
437
397
 
438
398
  # @param uri [String]
439
399
  # @return [Array<Solargraph::Pin::Base>]
440
- def file_symbols uri
441
- library.file_symbols(uri_to_file(uri))
400
+ def document_symbols uri
401
+ library.document_symbols(uri_to_file(uri))
442
402
  end
443
403
 
444
404
  # Send a notification to the client.
@@ -490,6 +450,13 @@ module Solargraph
490
450
  }
491
451
  end
492
452
 
453
+ # Catalog the library.
454
+ #
455
+ # @return [void]
456
+ def catalog
457
+ library.catalog
458
+ end
459
+
493
460
  private
494
461
 
495
462
  # @return [Solargraph::Library]
@@ -497,10 +464,20 @@ module Solargraph
497
464
  @library
498
465
  end
499
466
 
467
+ # @return [Diagnoser]
468
+ def diagnoser
469
+ @diagnoser ||= Diagnoser.new(self)
470
+ end
471
+
472
+ # @return [Cataloger]
473
+ def cataloger
474
+ @cataloger ||= Cataloger.new(self)
475
+ end
476
+
500
477
  # @param file_uri [String]
501
478
  # @return [Boolean]
502
479
  def unsafe_changing? file_uri
503
- @change_queue.any?{|change| change['textDocument']['uri'] == file_uri}
480
+ file = uri_to_file(file_uri)
504
481
  end
505
482
 
506
483
  def unsafe_open? uri
@@ -511,115 +488,6 @@ module Solargraph
511
488
  @requests ||= {}
512
489
  end
513
490
 
514
- def start_change_thread
515
- Thread.new do
516
- until stopped?
517
- @change_semaphore.synchronize do
518
- begin
519
- changed = false
520
- @change_queue.sort!{|a, b| a['textDocument']['version'] <=> b['textDocument']['version']}
521
- pending = {}
522
- @change_queue.each do |obj|
523
- pending[obj['textDocument']['uri']] ||= 0
524
- pending[obj['textDocument']['uri']] += 1
525
- end
526
- @change_queue.delete_if do |change|
527
- filename = uri_to_file(change['textDocument']['uri'])
528
- source = library.checkout(filename)
529
- if change['textDocument']['version'] == source.version + change['contentChanges'].length
530
- pending[change['textDocument']['uri']] -= 1
531
- updater = generate_updater(change)
532
- library.synchronize updater, pending[change['textDocument']['uri']] == 0
533
- @diagnostics_queue.push change['textDocument']['uri']
534
- changed = true
535
- next true
536
- elsif change['textDocument']['version'] == source.version + 1
537
- # HACK: This condition fixes the fact that certain changes
538
- # increment the version by one regardless of the number of
539
- # changes
540
- STDERR.puts "Warning: change applied to #{uri_to_file(change['textDocument']['uri'])} is possibly out of sync"
541
- pending[change['textDocument']['uri']] -= 1
542
- updater = generate_updater(change)
543
- library.synchronize updater, pending[change['textDocument']['uri']] == 0
544
- @diagnostics_queue.push change['textDocument']['uri']
545
- changed = true
546
- next true
547
- elsif change['textDocument']['version'] <= source.version
548
- # @todo Is deleting outdated changes correct behavior?
549
- STDERR.puts "Warning: outdated change to #{change['textDocument']['uri']} was ignored"
550
- @diagnostics_queue.push change['textDocument']['uri']
551
- next true
552
- else
553
- if unsafe_open?(change['textDocument']['uri'])
554
- STDERR.puts "Skipping out of order change to #{change['textDocument']['uri']}"
555
- next false
556
- else
557
- STDERR.puts "Deleting out of order change to closed file #{change['textDocument']['uri']}"
558
- next true
559
- end
560
- end
561
- end
562
- refreshable = changed and @change_queue.empty?
563
- library.refresh if refreshable
564
- rescue Exception => e
565
- # Trying to get anything out of the error except its class
566
- # hangs the thread for some reason
567
- STDERR.puts "An error occurred in the change thread: #{e.class}"
568
- STDERR.puts e.backtrace
569
- @change_queue.clear
570
- end
571
- end
572
- sleep 0.01
573
- end
574
- end
575
- end
576
-
577
- def start_diagnostics_thread
578
- Thread.new do
579
- until stopped?
580
- sleep 0.1
581
- if !options['diagnostics']
582
- @change_semaphore.synchronize { @diagnostics_queue.clear }
583
- next
584
- end
585
- begin
586
- # Diagnosis is broken into two parts to reduce the number of
587
- # times it runs while a document is changing
588
- current = nil
589
- already_changing = nil
590
- @change_semaphore.synchronize do
591
- current = @diagnostics_queue.shift
592
- break if current.nil?
593
- already_changing = unsafe_changing?(current)
594
- @diagnostics_queue.delete current unless already_changing
595
- end
596
- next if current.nil? or already_changing
597
- filename = uri_to_file(current)
598
- results = library.diagnose(filename)
599
- @change_semaphore.synchronize do
600
- already_changing = (unsafe_changing?(current) or @diagnostics_queue.include?(current))
601
- unless already_changing
602
- send_notification "textDocument/publishDiagnostics", {
603
- uri: current,
604
- diagnostics: results
605
- }
606
- end
607
- end
608
- rescue DiagnosticsError => e
609
- STDERR.puts "Error in diagnostics: #{e.message}"
610
- options['diagnostics'] = false
611
- send_notification 'window/showMessage', {
612
- type: LanguageServer::MessageTypes::ERROR,
613
- message: "Error in diagnostics: #{e.message}"
614
- }
615
- rescue Exception => e
616
- STDERR.puts "#{e.message}"
617
- STDERR.puts "#{e.backtrace}"
618
- end
619
- end
620
- end
621
- end
622
-
623
491
  def normalize_separators path
624
492
  return path if File::ALT_SEPARATOR.nil?
625
493
  path.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
@@ -631,7 +499,7 @@ module Solargraph
631
499
  changes.push Solargraph::Source::Change.new(
632
500
  (chng['range'].nil? ?
633
501
  nil :
634
- Solargraph::Source::Range.from_to(chng['range']['start']['line'], chng['range']['start']['character'], chng['range']['end']['line'], chng['range']['end']['character'])
502
+ Solargraph::Range.from_to(chng['range']['start']['line'], chng['range']['start']['character'], chng['range']['end']['line'], chng['range']['end']['character'])
635
503
  ),
636
504
  chng['text']
637
505
  )