solargraph 0.43.0 → 0.44.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89a1a9d76094688ff7ebe1af3dec4452c379819408ffc458eddcd75f5b66d2eb
4
- data.tar.gz: 9b03143ba896ccd2d3bd0a44c9a1e90f06e3ed757a47a08e6b8aaf2a9f56e62e
3
+ metadata.gz: '0827e2be47eea923c2f61688ce4e97efa11d3d801f094ffb8a6cdb73a7fb52b3'
4
+ data.tar.gz: dcbad1eaa64acab63ccc511a93c0e1370fc469815e2e67735fc79c2f4dc08038
5
5
  SHA512:
6
- metadata.gz: a6d4e99416af96a8a11ca569d096f4ba1491155f3fde1efc734e59c73d1794007d0b7b1e936c1dfb248472df1fa679c7701866176de99119e8f6a0b8eef721d4
7
- data.tar.gz: edd612d341e6c9bb421e4bbb8db04badc855e924eca469c47943c374813025810151aca1abdd0da13100597803799a50c7355a362b8a0ae65187c0760464e840
6
+ metadata.gz: fa050d901186e2385685d2aab7934f611517d68b549f4cb064ab7c3524f99e890488675a06483c2b1f2e2cafc0417a3367972a1ea8b6c6614a3465e867313b03
7
+ data.tar.gz: 4b12d6b0f65f53afd0ccbbdfc84a9fe8159f2f3a4bb3c625a84f1a9ee80be53f8c8b1d1ecfbae9bcf01ceff691388af5d0cef7271bb5470730fc4aa7a2471df9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## 0.44.0 - September 27, 2021
2
+ - Allow opening parenthesis, space, and comma when using Diff::LCS (#465)
3
+ - Support textDocument/documentHighlight
4
+ - Library#references_from performs shallow matches
5
+
6
+ ## 0.43.3 - September 25, 2021
7
+ - Avoid Dir.chdir in Bundler processes (#481)
8
+ - Check stdlib for gems with empty yardocs
9
+ - Library#maybe_map checks for source in workspace
10
+
11
+ ## 0.43.2 - September 23, 2021
12
+ - Synchronize server requests (#461)
13
+
14
+ ## 0.43.1 - September 20, 2021
15
+ - Complete nested namespaces in open gates
16
+ - SourceMap::Mapper reports filename for encoding errors (#474)
17
+ - Handle request on a specific thread, and cancel completion when there has newer completion request (#459)
18
+ - Fix namespace links generated by views/_method.erb (#472)
19
+ - Source handles long squiggly heredocs (#460)
20
+
1
21
  ## 0.43.0 - July 25, 2021
2
22
  - Correct arity checks when restarg precedes arg (#418)
3
23
  - Improve the performance of catalog by 4 times (#457)
data/README.md CHANGED
@@ -34,7 +34,8 @@ Plug-ins and extensions are available for the following editors:
34
34
  * GitHub: https://github.com/autozimu/LanguageClient-neovim
35
35
 
36
36
  * **Emacs**
37
- * GitHub: https://github.com/guskovd/emacs-solargraph
37
+ * GitHub: `eglot.el`, https://github.com/joaotavora/eglot
38
+ * GitHub: `lsp-mode.el`, https://github.com/emacs-lsp/lsp-mode
38
39
 
39
40
  * **Eclipse**
40
41
  * Plugin: https://marketplace.eclipse.org/content/ruby-solargraph
@@ -59,18 +59,16 @@ module Solargraph
59
59
  # @return [Hash]
60
60
  def self.specs_from_bundle directory
61
61
  Solargraph.with_clean_env do
62
- Dir.chdir directory do
63
- cmd = [
64
- 'bundle', 'exec', 'ruby', '-e',
65
- "require 'bundler'; require 'json'; puts Bundler.definition.specs_for([:default]).map { |spec| [spec.name, spec.version] }.to_h.to_json"
66
- ]
67
- o, e, s = Open3.capture3(*cmd)
68
- if s.success?
69
- o && !o.empty? ? JSON.parse(o.split("\n").last) : {}
70
- else
71
- Solargraph.logger.warn e
72
- raise BundleNotFoundError, "Failed to load gems from bundle at #{directory}"
73
- end
62
+ cmd = [
63
+ 'ruby', '-e',
64
+ "require 'bundler'; require 'json'; Dir.chdir('#{directory}') { puts Bundler.definition.specs_for([:default]).map { |spec| [spec.name, spec.version] }.to_h.to_json }"
65
+ ]
66
+ o, e, s = Open3.capture3(*cmd)
67
+ if s.success?
68
+ o && !o.empty? ? JSON.parse(o.split("\n").last) : {}
69
+ else
70
+ Solargraph.logger.warn e
71
+ raise BundleNotFoundError, "Failed to load gems from bundle at #{directory}"
74
72
  end
75
73
  end
76
74
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module LanguageServer
5
+ class Host
6
+ # A serial worker Thread to handle message.
7
+ #
8
+ # this make check pending message possible, and maybe cancelled to speedup process
9
+ class MessageWorker
10
+ # @param host [Host]
11
+ def initialize(host)
12
+ @host = host
13
+ @mutex = Mutex.new
14
+ @resource = ConditionVariable.new
15
+ @stopped = true
16
+ end
17
+
18
+ # pending handle messages
19
+ def messages
20
+ @messages ||= []
21
+ end
22
+
23
+ def stopped?
24
+ @stopped
25
+ end
26
+
27
+ def stop
28
+ @stopped = true
29
+ end
30
+
31
+ # @param message [Hash] The message should be handle. will pass back to Host#receive
32
+ # @return [void]
33
+ def queue(message)
34
+ @mutex.synchronize do
35
+ messages.push(message)
36
+ @resource.signal
37
+ end
38
+ end
39
+
40
+ def start
41
+ return unless @stopped
42
+ @stopped = false
43
+ Thread.new do
44
+ tick until stopped?
45
+ end
46
+ end
47
+
48
+ def tick
49
+ message = @mutex.synchronize do
50
+ @resource.wait(@mutex) if messages.empty?
51
+ messages.shift
52
+ end
53
+ handler = @host.receive(message)
54
+ handler && handler.send_response
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -12,10 +12,11 @@ module Solargraph
12
12
  # safety for multi-threaded transports.
13
13
  #
14
14
  class Host
15
- autoload :Diagnoser, 'solargraph/language_server/host/diagnoser'
16
- autoload :Cataloger, 'solargraph/language_server/host/cataloger'
17
- autoload :Sources, 'solargraph/language_server/host/sources'
18
- autoload :Dispatch, 'solargraph/language_server/host/dispatch'
15
+ autoload :Diagnoser, 'solargraph/language_server/host/diagnoser'
16
+ autoload :Cataloger, 'solargraph/language_server/host/cataloger'
17
+ autoload :Sources, 'solargraph/language_server/host/sources'
18
+ autoload :Dispatch, 'solargraph/language_server/host/dispatch'
19
+ autoload :MessageWorker, 'solargraph/language_server/host/message_worker'
19
20
 
20
21
  include UriHelpers
21
22
  include Logging
@@ -27,7 +28,7 @@ module Solargraph
27
28
  def initialize
28
29
  @cancel_semaphore = Mutex.new
29
30
  @buffer_semaphore = Mutex.new
30
- @register_semaphore = Mutex.new
31
+ @request_mutex = Mutex.new
31
32
  @cancel = []
32
33
  @buffer = String.new
33
34
  @stopped = true
@@ -45,6 +46,7 @@ module Solargraph
45
46
  diagnoser.start
46
47
  cataloger.start
47
48
  sources.start
49
+ message_worker.start
48
50
  end
49
51
 
50
52
  # Update the configuration options with the provided hash.
@@ -89,8 +91,15 @@ module Solargraph
89
91
  @cancel_semaphore.synchronize { @cancel.delete id }
90
92
  end
91
93
 
94
+ # Called by adapter, to handle the request
95
+ # @param request [Hash]
96
+ # @return [void]
97
+ def process request
98
+ message_worker.queue(request)
99
+ end
100
+
92
101
  # Start processing a request from the client. After the message is
93
- # processed, the transport is responsible for sending the response.
102
+ # processed, caller is responsible for sending the response.
94
103
  #
95
104
  # @param request [Hash] The contents of the message.
96
105
  # @return [Solargraph::LanguageServer::Message::Base] The message handler.
@@ -355,19 +364,21 @@ module Solargraph
355
364
  # @yieldparam [Hash] The result sent by the client
356
365
  # @return [void]
357
366
  def send_request method, params, &block
358
- message = {
359
- jsonrpc: "2.0",
360
- method: method,
361
- params: params,
362
- id: @next_request_id
363
- }
364
- json = message.to_json
365
- requests[@next_request_id] = Request.new(@next_request_id, &block)
366
- envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
367
- queue envelope
368
- @next_request_id += 1
369
- logger.info "Server sent #{method}"
370
- logger.debug params
367
+ @request_mutex.synchronize do
368
+ message = {
369
+ jsonrpc: "2.0",
370
+ method: method,
371
+ params: params,
372
+ id: @next_request_id
373
+ }
374
+ json = message.to_json
375
+ requests[@next_request_id] = Request.new(@next_request_id, &block)
376
+ envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
377
+ queue envelope
378
+ @next_request_id += 1
379
+ logger.info "Server sent #{method}"
380
+ logger.debug params
381
+ end
371
382
  end
372
383
 
373
384
  # Register the methods as capabilities with the client.
@@ -378,20 +389,16 @@ module Solargraph
378
389
  # @return [void]
379
390
  def register_capabilities methods
380
391
  logger.debug "Registering capabilities: #{methods}"
381
- registrations = methods.select{|m| can_register?(m) and !registered?(m)}.map { |m|
392
+ registrations = methods.select { |m| can_register?(m) and !registered?(m) }.map do |m|
382
393
  @registered_capabilities.add m
383
394
  {
384
395
  id: m,
385
396
  method: m,
386
397
  registerOptions: dynamic_capability_options[m]
387
398
  }
388
- }
389
- return if registrations.empty?
390
- @register_semaphore.synchronize do
391
- send_request 'client/registerCapability', {
392
- registrations: registrations
393
- }
394
399
  end
400
+ return if registrations.empty?
401
+ send_request 'client/registerCapability', { registrations: registrations }
395
402
  end
396
403
 
397
404
  # Unregister the methods with the client.
@@ -410,11 +417,7 @@ module Solargraph
410
417
  }
411
418
  }
412
419
  return if unregisterations.empty?
413
- @register_semaphore.synchronize do
414
- send_request 'client/unregisterCapability', {
415
- unregisterations: unregisterations
416
- }
417
- end
420
+ send_request 'client/unregisterCapability', { unregisterations: unregisterations }
418
421
  end
419
422
 
420
423
  # Flag a method as available for dynamic registration.
@@ -422,9 +425,7 @@ module Solargraph
422
425
  # @param method [String] The method name, e.g., 'textDocument/completion'
423
426
  # @return [void]
424
427
  def allow_registration method
425
- @register_semaphore.synchronize do
426
- @dynamic_capabilities.add method
427
- end
428
+ @dynamic_capabilities.add method
428
429
  end
429
430
 
430
431
  # True if the specified LSP method can be dynamically registered.
@@ -451,6 +452,7 @@ module Solargraph
451
452
  def stop
452
453
  return if @stopped
453
454
  @stopped = true
455
+ message_worker.stop
454
456
  cataloger.stop
455
457
  diagnoser.stop
456
458
  sources.stop
@@ -513,6 +515,11 @@ module Solargraph
513
515
  library.completions_at uri_to_file(uri), line, column
514
516
  end
515
517
 
518
+ # @return [Bool] if has pending completion request
519
+ def has_pending_completions?
520
+ message_worker.messages.reverse_each.any? { |req| req['method'] == 'textDocument/completion' }
521
+ end
522
+
516
523
  # @param uri [String]
517
524
  # @param line [Integer]
518
525
  # @param column [Integer]
@@ -535,10 +542,11 @@ module Solargraph
535
542
  # @param line [Integer]
536
543
  # @param column [Integer]
537
544
  # @param strip [Boolean] Strip special characters from variable names
545
+ # @param only [Boolean] If true, search current file only
538
546
  # @return [Array<Solargraph::Range>]
539
- def references_from uri, line, column, strip: true
547
+ def references_from uri, line, column, strip: true, only: false
540
548
  library = library_for(uri)
541
- library.references_from(uri_to_file(uri), line, column, strip: strip)
549
+ library.references_from(uri_to_file(uri), line, column, strip: strip, only: only)
542
550
  end
543
551
 
544
552
  # @param query [String]
@@ -624,6 +632,7 @@ module Solargraph
624
632
  'diagnostics' => false,
625
633
  'formatting' => false,
626
634
  'folding' => true,
635
+ 'highlights' => true,
627
636
  'logLevel' => 'warn'
628
637
  }
629
638
  end
@@ -646,6 +655,11 @@ module Solargraph
646
655
 
647
656
  private
648
657
 
658
+ # @return [MessageWorker]
659
+ def message_worker
660
+ @message_worker ||= MessageWorker.new(self)
661
+ end
662
+
649
663
  # @return [Diagnoser]
650
664
  def diagnoser
651
665
  @diagnoser ||= Diagnoser.new(self)
@@ -703,7 +717,7 @@ module Solargraph
703
717
  return change if diffs.length.zero? || diffs.length > 1 || diffs.first.length > 1
704
718
  # @type [Diff::LCS::Change]
705
719
  diff = diffs.first.first
706
- return change unless diff.adding? && ['.', ':'].include?(diff.element)
720
+ return change unless diff.adding? && ['.', ':', '(', ',', ' '].include?(diff.element)
707
721
  position = Solargraph::Position.from_offset(source.code, diff.position)
708
722
  {
709
723
  'range' => {
@@ -771,6 +785,9 @@ module Solargraph
771
785
  },
772
786
  'textDocument/codeAction' => {
773
787
  codeActionProvider: true
788
+ },
789
+ 'textDocument/documentHighlight' => {
790
+ documentHighlightProvider: true
774
791
  }
775
792
  }
776
793
  end
@@ -62,10 +62,14 @@ module Solargraph
62
62
  def send_response
63
63
  return if id.nil?
64
64
  if host.cancel?(id)
65
+ # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#cancelRequest
66
+ # cancel should send response RequestCancelled
65
67
  Solargraph::Logging.logger.info "Cancelled response to #{method}"
66
- return
68
+ set_result nil
69
+ set_error ErrorCodes::REQUEST_CANCELLED, "cancelled by client"
70
+ else
71
+ Solargraph::Logging.logger.info "Sending response to #{method}"
67
72
  end
68
- Solargraph::Logging.logger.info "Sending response to #{method}"
69
73
  response = {
70
74
  jsonrpc: "2.0",
71
75
  id: id,
@@ -36,6 +36,7 @@ module Solargraph
36
36
  result[:capabilities].merge! static_references unless dynamic_registration_for?('textDocument', 'references')
37
37
  result[:capabilities].merge! static_workspace_symbols unless dynamic_registration_for?('workspace', 'symbol')
38
38
  result[:capabilities].merge! static_folding_range unless dynamic_registration_for?('textDocument', 'foldingRange')
39
+ result[:capabilities].merge! static_highlights unless dynamic_registration_for?('textDocument', 'documentHighlight')
39
40
  # @todo Temporarily disabled
40
41
  # result[:capabilities].merge! static_code_action unless dynamic_registration_for?('textDocument', 'codeAction')
41
42
  set_result result
@@ -138,6 +139,12 @@ module Solargraph
138
139
  }
139
140
  end
140
141
 
142
+ def static_highlights
143
+ {
144
+ documentHighlightProvider: true
145
+ }
146
+ end
147
+
141
148
  # @param section [String]
142
149
  # @param capability [String]
143
150
  # @return [Boolean]
@@ -17,6 +17,7 @@ module Solargraph
17
17
  textDocument/rename
18
18
  textDocument/prepareRename
19
19
  textDocument/foldingRange
20
+ textDocument/documentHighlight
20
21
  workspace/symbol
21
22
  ]
22
23
  end
@@ -6,6 +6,8 @@ module Solargraph
6
6
  module TextDocument
7
7
  class Completion < Base
8
8
  def process
9
+ return set_error(ErrorCodes::REQUEST_CANCELLED, "cancelled by so many request") if host.has_pending_completions?
10
+
9
11
  line = params['position']['line']
10
12
  col = params['position']['character']
11
13
  begin
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph::LanguageServer::Message::TextDocument
4
+ class DocumentHighlight < Base
5
+ def process
6
+ locs = host.references_from(params['textDocument']['uri'], params['position']['line'], params['position']['character'], strip: true, only: true)
7
+ result = locs.map do |loc|
8
+ {
9
+ range: loc.range.to_hash,
10
+ kind: 1
11
+ }
12
+ end
13
+ set_result result
14
+ end
15
+ end
16
+ end
@@ -4,24 +4,24 @@ module Solargraph
4
4
  module LanguageServer
5
5
  module Message
6
6
  module TextDocument
7
- autoload :Base, 'solargraph/language_server/message/text_document/base'
8
- autoload :Completion, 'solargraph/language_server/message/text_document/completion'
9
- autoload :DidOpen, 'solargraph/language_server/message/text_document/did_open'
10
- autoload :DidChange, 'solargraph/language_server/message/text_document/did_change'
11
- autoload :DidClose, 'solargraph/language_server/message/text_document/did_close'
12
- autoload :DidSave, 'solargraph/language_server/message/text_document/did_save'
13
- autoload :Hover, 'solargraph/language_server/message/text_document/hover'
14
- autoload :SignatureHelp, 'solargraph/language_server/message/text_document/signature_help'
15
- autoload :DiagnosticsQueue, 'solargraph/language_server/message/text_document/diagnostics_queue'
16
- autoload :OnTypeFormatting, 'solargraph/language_server/message/text_document/on_type_formatting'
17
- autoload :Definition, 'solargraph/language_server/message/text_document/definition'
18
- autoload :DocumentSymbol, 'solargraph/language_server/message/text_document/document_symbol'
19
- autoload :Formatting, 'solargraph/language_server/message/text_document/formatting'
20
- autoload :References, 'solargraph/language_server/message/text_document/references'
21
- autoload :Rename, 'solargraph/language_server/message/text_document/rename'
22
- autoload :PrepareRename, 'solargraph/language_server/message/text_document/prepare_rename'
23
- autoload :FoldingRange, 'solargraph/language_server/message/text_document/folding_range'
24
- autoload :CodeAction, 'solargraph/language_server/message/text_document/code_action'
7
+ autoload :Base, 'solargraph/language_server/message/text_document/base'
8
+ autoload :Completion, 'solargraph/language_server/message/text_document/completion'
9
+ autoload :DidOpen, 'solargraph/language_server/message/text_document/did_open'
10
+ autoload :DidChange, 'solargraph/language_server/message/text_document/did_change'
11
+ autoload :DidClose, 'solargraph/language_server/message/text_document/did_close'
12
+ autoload :DidSave, 'solargraph/language_server/message/text_document/did_save'
13
+ autoload :Hover, 'solargraph/language_server/message/text_document/hover'
14
+ autoload :SignatureHelp, 'solargraph/language_server/message/text_document/signature_help'
15
+ autoload :DiagnosticsQueue, 'solargraph/language_server/message/text_document/diagnostics_queue'
16
+ autoload :OnTypeFormatting, 'solargraph/language_server/message/text_document/on_type_formatting'
17
+ autoload :Definition, 'solargraph/language_server/message/text_document/definition'
18
+ autoload :DocumentSymbol, 'solargraph/language_server/message/text_document/document_symbol'
19
+ autoload :Formatting, 'solargraph/language_server/message/text_document/formatting'
20
+ autoload :References, 'solargraph/language_server/message/text_document/references'
21
+ autoload :Rename, 'solargraph/language_server/message/text_document/rename'
22
+ autoload :PrepareRename, 'solargraph/language_server/message/text_document/prepare_rename'
23
+ autoload :FoldingRange, 'solargraph/language_server/message/text_document/folding_range'
24
+ autoload :DocumentHighlight, 'solargraph/language_server/message/text_document/document_highlight'
25
25
  end
26
26
  end
27
27
  end
@@ -22,6 +22,7 @@ module Solargraph::LanguageServer::Message::Workspace
22
22
  (host.options['definitions'] ? y : n).push('textDocument/definition')
23
23
  (host.options['references'] ? y : n).push('textDocument/references')
24
24
  (host.options['folding'] ? y : n).push('textDocument/folding')
25
+ (host.options['highlights'] ? y : n).push('textDocument/documentHighlight')
25
26
  host.register_capabilities y
26
27
  host.unregister_capabilities n
27
28
  end
@@ -73,7 +73,8 @@ module Solargraph
73
73
  register 'textDocument/rename', TextDocument::Rename
74
74
  register 'textDocument/prepareRename', TextDocument::PrepareRename
75
75
  register 'textDocument/foldingRange', TextDocument::FoldingRange
76
- register 'textDocument/codeAction', TextDocument::CodeAction
76
+ # register 'textDocument/codeAction', TextDocument::CodeAction
77
+ register 'textDocument/documentHighlight', TextDocument::DocumentHighlight
77
78
  register 'workspace/didChangeWatchedFiles', Workspace::DidChangeWatchedFiles
78
79
  register 'workspace/didChangeConfiguration', Workspace::DidChangeConfiguration
79
80
  register 'workspace/didChangeWorkspaceFolders', Workspace::DidChangeWorkspaceFolders
@@ -41,8 +41,7 @@ module Solargraph
41
41
  # @param request [String]
42
42
  # @return [void]
43
43
  def process request
44
- message = @host.receive(request)
45
- message && message.send_response
44
+ @host.process(request)
46
45
  end
47
46
 
48
47
  def shutdown
@@ -216,33 +216,35 @@ module Solargraph
216
216
  # @param line [Integer]
217
217
  # @param column [Integer]
218
218
  # @param strip [Boolean] Strip special characters from variable names
219
+ # @param only [Boolean] Search for references in the current file only
219
220
  # @return [Array<Solargraph::Range>]
220
221
  # @todo Take a Location instead of filename/line/column
221
- def references_from filename, line, column, strip: false
222
+ def references_from filename, line, column, strip: false, only: false
222
223
  cursor = api_map.cursor_at(filename, Position.new(line, column))
223
224
  clip = api_map.clip(cursor)
224
- pins = clip.define
225
- return [] if pins.empty?
225
+ pin = clip.define.first
226
+ return [] unless pin
226
227
  result = []
227
- pins.uniq.each do |pin|
228
- (workspace.sources + (@current ? [@current] : [])).uniq(&:filename).each do |source|
229
- found = source.references(pin.name)
230
- found.select! do |loc|
231
- referenced = definitions_at(loc.filename, loc.range.ending.line, loc.range.ending.character)
232
- # HACK: The additional location comparison is necessary because
233
- # Clip#define can return proxies for parameter pins
234
- referenced.any? { |r| r == pin || r.location == pin.location }
235
- end
236
- # HACK: for language clients that exclude special characters from the start of variable names
237
- if strip && match = cursor.word.match(/^[^a-z0-9_]+/i)
238
- found.map! do |loc|
239
- Solargraph::Location.new(loc.filename, Solargraph::Range.from_to(loc.range.start.line, loc.range.start.column + match[0].length, loc.range.ending.line, loc.range.ending.column))
240
- end
228
+ files = if only
229
+ [api_map.source_map(filename)]
230
+ else
231
+ (workspace.sources + (@current ? [@current] : []))
232
+ end
233
+ files.uniq(&:filename).each do |source|
234
+ found = source.references(pin.name)
235
+ found.select! do |loc|
236
+ referenced = definitions_at(loc.filename, loc.range.ending.line, loc.range.ending.character).first
237
+ referenced && referenced.path == pin.path
238
+ end
239
+ # HACK: for language clients that exclude special characters from the start of variable names
240
+ if strip && match = cursor.word.match(/^[^a-z0-9_]+/i)
241
+ found.map! do |loc|
242
+ Solargraph::Location.new(loc.filename, Solargraph::Range.from_to(loc.range.start.line, loc.range.start.column + match[0].length, loc.range.ending.line, loc.range.ending.column))
241
243
  end
242
- result.concat(found.sort do |a, b|
243
- a.range.start.line <=> b.range.start.line
244
- end)
245
244
  end
245
+ result.concat(found.sort do |a, b|
246
+ a.range.start.line <=> b.range.start.line
247
+ end)
246
248
  end
247
249
  result.uniq
248
250
  end
@@ -518,6 +520,7 @@ module Solargraph
518
520
 
519
521
  def maybe_map source
520
522
  return unless source
523
+ return unless @current == source || workspace.has_file?(source.filename)
521
524
  if source_map_hash.key?(source.filename)
522
525
  return if source_map_hash[source.filename].code == source.code &&
523
526
  source_map_hash[source.filename].source.synchronized? &&
@@ -2,7 +2,7 @@ require 'ripper'
2
2
 
3
3
  module Solargraph
4
4
  module Parser
5
- class CommentRipper < Ripper::SexpBuilder
5
+ class CommentRipper < Ripper::SexpBuilderPP
6
6
  def initialize src, filename = '(ripper)', lineno = 0
7
7
  super
8
8
  @buffer = src
@@ -103,6 +103,31 @@ module Solargraph
103
103
  en = Position.new(node.loc.last_line, node.loc.last_column)
104
104
  Range.new(st, en)
105
105
  end
106
+
107
+ def string_ranges node
108
+ return [] unless is_ast_node?(node)
109
+ result = []
110
+ if node.type == :str
111
+ result.push Range.from_node(node)
112
+ elsif node.type == :dstr
113
+ here = Range.from_node(node)
114
+ there = Range.from_node(node.children[1])
115
+ result.push Range.new(here.start, there.start)
116
+ end
117
+ node.children.each do |child|
118
+ result.concat string_ranges(child)
119
+ end
120
+ if node.type == :dstr && node.children.last.nil?
121
+ # result.push Range.new(result.last.ending, result.last.ending)
122
+ last = node.children[-2]
123
+ unless last.nil?
124
+ rng = Range.from_node(last)
125
+ pos = Position.new(rng.ending.line, rng.ending.column - 1)
126
+ result.push Range.new(pos, pos)
127
+ end
128
+ end
129
+ result
130
+ end
106
131
  end
107
132
  end
108
133
  end
@@ -178,6 +178,7 @@ module Solargraph
178
178
 
179
179
  # @param cursor [Solargraph::Source::Cursor]
180
180
  def find_recipient_node cursor
181
+ return repaired_find_recipient_node(cursor) if cursor.source.repaired? && cursor.source.code[cursor.offset - 1] == '('
181
182
  source = cursor.source
182
183
  position = cursor.position
183
184
  offset = cursor.offset
@@ -210,6 +211,12 @@ module Solargraph
210
211
  nil
211
212
  end
212
213
 
214
+ def repaired_find_recipient_node cursor
215
+ cursor = cursor.source.cursor_at([cursor.position.line, cursor.position.column - 1])
216
+ node = cursor.source.tree_at(cursor.position.line, cursor.position.column).first
217
+ return node if node && node.type == :send
218
+ end
219
+
213
220
  module DeepInference
214
221
  class << self
215
222
  CONDITIONAL = [:if, :unless]
@@ -141,36 +141,45 @@ module Solargraph
141
141
  class << self
142
142
  protected
143
143
 
144
+ # @param cursor [Source::Cursor]
145
+ # @return [RubyVM::AbstractSyntaxTree::Node, nil]
144
146
  def synchronized_find_recipient_node cursor
147
+ cursor = maybe_adjust_cursor(cursor)
145
148
  source = cursor.source
146
149
  position = cursor.position
147
150
  offset = cursor.offset
148
151
  tree = source.tree_at(position.line, position.column)
149
- tree.shift while tree.first && [:FCALL, :VCALL, :CALL].include?(tree.first.type) && !source.code_for(tree.first).strip.end_with?(')')
152
+ .select { |n| [:FCALL, :VCALL, :CALL].include?(n.type) }
153
+ unless source.repaired?
154
+ tree.shift while tree.first && !source.code_for(tree.first).strip.end_with?(')')
155
+ end
156
+ return tree.first if source.repaired? || source.code[0..offset-1] =~ /\(\s*$/
150
157
  tree.each do |node|
151
- if [:FCALL, :VCALL, :CALL].include?(node.type)
152
- args = node.children.find { |c| Parser.is_ast_node?(c) && [:ARRAY, :ZARRAY, :LIST].include?(c.type) }
153
- if args
154
- match = source.code[0..offset-1].match(/,[^\)]*\z/)
155
- rng = Solargraph::Range.from_node(args)
156
- if match
157
- rng = Solargraph::Range.new(rng.start, position)
158
- end
159
- return node if rng.contain?(position)
160
- elsif source.code[0..offset-1] =~ /\(\s*$/
161
- break unless source.code_for(node).strip.end_with?(')')
162
- return node
158
+ args = node.children.find { |c| Parser.is_ast_node?(c) && [:ARRAY, :ZARRAY, :LIST].include?(c.type) }
159
+ if args
160
+ match = source.code[0..offset-1].match(/,[^\)]*\z/)
161
+ rng = Solargraph::Range.from_node(args)
162
+ if match
163
+ rng = Solargraph::Range.new(rng.start, position)
163
164
  end
165
+ return node if rng.contain?(position)
164
166
  end
165
167
  end
166
168
  nil
167
169
  end
168
170
 
171
+ # @param cursor [Source::Cursor]
172
+ # @return [Source::Cursor]
173
+ def maybe_adjust_cursor cursor
174
+ return cursor unless (cursor.source.repaired? && cursor.source.code[cursor.offset - 1] == '(') || [',', ' '].include?(cursor.source.code[cursor.offset - 1])
175
+ cursor.source.cursor_at([cursor.position.line, cursor.position.column - 1])
176
+ end
177
+
169
178
  def unsynchronized_find_recipient_node cursor
170
179
  source = cursor.source
171
180
  position = cursor.position
172
181
  offset = cursor.offset
173
- if source.code[0..offset-1] =~ /\([A-Zaz0-9_\s]*\z$/ #&& source.code[offset] == ')'
182
+ if source.code[0..offset-1] =~ /\([A-Zaz0-9_\s]*\z$/
174
183
  tree = source.tree_at(position.line, position.column - 1)
175
184
  if tree.first && [:FCALL, :VCALL, :CALL].include?(tree.first.type)
176
185
  return tree.first
@@ -22,12 +22,18 @@ module Solargraph
22
22
  @closure = Solargraph::Pin::ROOT_PIN
23
23
  end
24
24
  @open_gates = gates
25
- if @open_gates.one? && @open_gates.first.empty? && @name.include?('::')
25
+ if @name.include?('::')
26
26
  # In this case, a chained namespace was opened (e.g., Foo::Bar)
27
27
  # but Foo does not exist.
28
28
  parts = @name.split('::')
29
29
  @name = parts.pop
30
- @closure = Pin::Namespace.new(name: parts.join('::'), gates: [parts.join('::')])
30
+ closure_name = if [Solargraph::Pin::ROOT_PIN, nil].include?(closure)
31
+ ''
32
+ else
33
+ closure.full_context.namespace + '::'
34
+ end
35
+ closure_name += parts.join('::')
36
+ @closure = Pin::Namespace.new(name: closure_name, gates: [parts.join('::')])
31
37
  @context = nil
32
38
  end
33
39
  end
@@ -204,6 +204,8 @@ module Solargraph
204
204
  com_pos = Position.new(line + 1 - comments.lines.length, 0)
205
205
  process_comment(src_pos, com_pos, comments)
206
206
  end
207
+ rescue StandardError => e
208
+ raise e.class, "Error processing comment directives in #{@filename}: #{e.message}"
207
209
  end
208
210
  end
209
211
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = '0.43.0'
4
+ VERSION = '0.44.0'
5
5
  end
@@ -2,7 +2,7 @@
2
2
  Namespace:
3
3
  </h2>
4
4
  <p>
5
- <a href="command:solargraph._openDocument?<%= CGI.escape "\"solargraph:/document?query=#{object.namespace}\"" %>"><%= object.namespace %></a>
5
+ <a href="solargraph:/document?query=<%= CGI.escape object.namespace.path %>"><%= object.namespace %></a>
6
6
  </p>
7
7
  <h2>
8
8
  Overview:
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'open3'
4
+ require 'rubygems'
5
+
3
6
  module Solargraph
4
7
  # A workspace consists of the files in a project's directory and the
5
8
  # project's configuration. It provides a Source for each file to be used
@@ -168,19 +171,20 @@ module Solargraph
168
171
  # HACK: Evaluating gemspec files violates the goal of not running
169
172
  # workspace code, but this is how Gem::Specification.load does it
170
173
  # anyway.
171
- Dir.chdir base do
174
+ cmd = ['ruby', '-e', "require 'rubygems'; require 'json'; spec = eval(File.read('#{file}'), TOPLEVEL_BINDING, '#{file}'); return unless Gem::Specification === spec; puts({name: spec.name, paths: spec.require_paths}.to_json)"]
175
+ o, e, s = Open3.capture3(*cmd)
176
+ if s.success?
172
177
  begin
173
- # @type [Gem::Specification]
174
- spec = eval(File.read(file), TOPLEVEL_BINDING, file)
175
- next unless Gem::Specification === spec
176
- @gemnames.push spec.name
177
- result.concat(spec.require_paths.map { |path| File.join(base, path) })
178
- rescue RuntimeError, ScriptError, Errno::ENOENT => e
179
- # Don't die if we have an error during eval-ing a gem spec.
180
- # Concat the default lib directory instead.
178
+ hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {}
179
+ next if hash.empty?
180
+ @gemnames.push hash['name']
181
+ result.concat(hash['paths'].map { |path| File.join(base, path) })
182
+ rescue StandardError => e
181
183
  Solargraph.logger.warn "Error reading #{file}: [#{e.class}] #{e.message}"
182
- result.push File.join(base, 'lib')
183
184
  end
185
+ else
186
+ Solargraph.logger.warn "Error reading #{file}"
187
+ Solargraph.logger.warn e
184
188
  end
185
189
  end
186
190
  result.concat(config.require_paths.map { |p| File.join(directory, p) })
@@ -10,6 +10,8 @@ module Solargraph
10
10
  # stdlib, and gems.
11
11
  #
12
12
  class YardMap
13
+ class NoYardocError < StandardError; end
14
+
13
15
  autoload :Cache, 'solargraph/yard_map/cache'
14
16
  autoload :CoreDocs, 'solargraph/yard_map/core_docs'
15
17
  autoload :CoreGen, 'solargraph/yard_map/core_gen'
@@ -224,7 +226,7 @@ module Solargraph
224
226
  result.concat process_yardoc yd, spec
225
227
  result.concat add_gem_dependencies(spec) if with_dependencies?
226
228
  end
227
- rescue Gem::LoadError => e
229
+ rescue Gem::LoadError, NoYardocError => e
228
230
  base = r.split('/').first
229
231
  next if from_std.include?(base)
230
232
  from_std.push base
@@ -299,6 +301,7 @@ module Solargraph
299
301
  result = Marshal.load(dump)
300
302
  return result unless result.nil? || result.empty?
301
303
  Solargraph.logger.warn "Empty cache for #{spec.name} #{spec.version}. Reloading"
304
+ File.unlink ser
302
305
  rescue StandardError => e
303
306
  Solargraph.logger.warn "Error loading pin cache: [#{e.class}] #{e.message}"
304
307
  File.unlink ser
@@ -315,6 +318,7 @@ module Solargraph
315
318
  Solargraph.logger.info "Loading #{spec.name} #{spec.version} from #{y}"
316
319
  load_yardoc y
317
320
  result = Mapper.new(YARD::Registry.all, spec).map
321
+ raise NoYardocError, "Yardoc at #{y} is empty" if result.empty?
318
322
  if spec
319
323
  ser = File.join(CoreDocs.cache_dir, 'gems', "#{spec.name}-#{spec.version}.ser")
320
324
  file = File.open(ser, 'wb')
data/lib/solargraph.rb CHANGED
@@ -57,6 +57,7 @@ module Solargraph
57
57
  # A helper method that runs Bundler.with_unbundled_env or falls back to
58
58
  # Bundler.with_clean_env for earlier versions of Bundler.
59
59
  #
60
+ # @return [void]
60
61
  def self.with_clean_env &block
61
62
  meth = if Bundler.respond_to?(:with_unbundled_env)
62
63
  :with_unbundled_env
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.43.0
4
+ version: 0.44.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-25 00:00:00.000000000 Z
11
+ date: 2021-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backport
@@ -350,6 +350,7 @@ files:
350
350
  - lib/solargraph/language_server/host/cataloger.rb
351
351
  - lib/solargraph/language_server/host/diagnoser.rb
352
352
  - lib/solargraph/language_server/host/dispatch.rb
353
+ - lib/solargraph/language_server/host/message_worker.rb
353
354
  - lib/solargraph/language_server/host/sources.rb
354
355
  - lib/solargraph/language_server/message.rb
355
356
  - lib/solargraph/language_server/message/base.rb
@@ -380,6 +381,7 @@ files:
380
381
  - lib/solargraph/language_server/message/text_document/did_close.rb
381
382
  - lib/solargraph/language_server/message/text_document/did_open.rb
382
383
  - lib/solargraph/language_server/message/text_document/did_save.rb
384
+ - lib/solargraph/language_server/message/text_document/document_highlight.rb
383
385
  - lib/solargraph/language_server/message/text_document/document_symbol.rb
384
386
  - lib/solargraph/language_server/message/text_document/folding_range.rb
385
387
  - lib/solargraph/language_server/message/text_document/formatting.rb