solargraph 0.42.4 → 0.43.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -1
- data/README.md +2 -1
- data/lib/solargraph/api_map/store.rb +17 -17
- data/lib/solargraph/api_map.rb +1 -1
- data/lib/solargraph/complex_type/type_methods.rb +6 -3
- data/lib/solargraph/complex_type.rb +4 -0
- data/lib/solargraph/documentor.rb +10 -12
- data/lib/solargraph/language_server/host/dispatch.rb +2 -3
- data/lib/solargraph/language_server/host/message_worker.rb +59 -0
- data/lib/solargraph/language_server/host.rb +47 -34
- data/lib/solargraph/language_server/message/base.rb +6 -2
- data/lib/solargraph/language_server/message/text_document/completion.rb +2 -0
- data/lib/solargraph/language_server/transport/adapter.rb +1 -2
- data/lib/solargraph/library.rb +8 -3
- data/lib/solargraph/parser/comment_ripper.rb +1 -1
- data/lib/solargraph/parser/legacy/class_methods.rb +25 -0
- data/lib/solargraph/parser/legacy/node_chainer.rb +14 -1
- data/lib/solargraph/parser/legacy/node_methods.rb +9 -2
- data/lib/solargraph/parser/rubyvm/node_chainer.rb +22 -1
- data/lib/solargraph/parser/rubyvm/node_methods.rb +5 -0
- data/lib/solargraph/pin/base.rb +1 -1
- data/lib/solargraph/pin/namespace.rb +8 -2
- data/lib/solargraph/source/chain/hash.rb +28 -0
- data/lib/solargraph/source/chain.rb +1 -0
- data/lib/solargraph/source_map/clip.rb +1 -1
- data/lib/solargraph/source_map/mapper.rb +2 -0
- data/lib/solargraph/type_checker.rb +11 -4
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/views/_method.erb +1 -1
- data/lib/solargraph/workspace.rb +14 -10
- data/lib/solargraph/yard_map.rb +5 -1
- data/lib/solargraph.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed5d2e054ff8eca78f0b4f2294eecd1018cb32f53bd43c241e803edefeb4fce1
|
4
|
+
data.tar.gz: c8161d2b732b03024d592a6db89a07c4cf27588d5a8b8d41c6cfcd63871c6796
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b310e476d1f7434a5da52adeda1212994b5cfa9907a60475a1dbd230e2bdbf9619a5a16420524fb0542e13dd527010f437e032f39ccac3596543bc96c4b1c24
|
7
|
+
data.tar.gz: 65bd2f40560050fc92f79bb4450a122f61e9b5a80bf58703f8317bd86d50058d6834b07b02d699b9804ab803c228f77441a32e37049d95ea4364704b916cf908
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,27 @@
|
|
1
|
-
## 0.
|
1
|
+
## 0.43.3 - September 25, 2021
|
2
|
+
- Avoid Dir.chdir in Bundler processes (#481)
|
3
|
+
- Check stdlib for gems with empty yardocs
|
4
|
+
- Library#maybe_map checks for source in workspace
|
5
|
+
|
6
|
+
## 0.43.2 - September 23, 2021
|
7
|
+
- Synchronize server requests (#461)
|
8
|
+
|
9
|
+
## 0.43.1 - September 20, 2021
|
10
|
+
- Complete nested namespaces in open gates
|
11
|
+
- SourceMap::Mapper reports filename for encoding errors (#474)
|
12
|
+
- Handle request on a specific thread, and cancel completion when there has newer completion request (#459)
|
13
|
+
- Fix namespace links generated by views/_method.erb (#472)
|
14
|
+
- Source handles long squiggly heredocs (#460)
|
15
|
+
|
16
|
+
## 0.43.0 - July 25, 2021
|
17
|
+
- Correct arity checks when restarg precedes arg (#418)
|
18
|
+
- Improve the performance of catalog by 4 times (#457)
|
19
|
+
- Type checker validates duck type variables and params (#453)
|
20
|
+
- Kernel#raise exception type checker
|
21
|
+
- Pin::Base#inspect includes path
|
22
|
+
- Fix arity with combined restargs and kwrestargs (#396)
|
23
|
+
|
24
|
+
## 0.42.4 - July 11, 2021
|
2
25
|
- Yardoc cache handling
|
3
26
|
- Fix required_paths when gemspec is used (#451)
|
4
27
|
- fix: yard stdout may break language client (#454)
|
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/
|
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
|
@@ -5,10 +5,10 @@ require 'set'
|
|
5
5
|
module Solargraph
|
6
6
|
class ApiMap
|
7
7
|
class Store
|
8
|
-
# @return [
|
8
|
+
# @return [Enumerable<Solargraph::Pin::Base>]
|
9
9
|
attr_reader :pins
|
10
10
|
|
11
|
-
# @param pins [
|
11
|
+
# @param pins [Enumerable<Solargraph::Pin::Base>]
|
12
12
|
def initialize pins = []
|
13
13
|
@pins = pins
|
14
14
|
index
|
@@ -16,7 +16,7 @@ module Solargraph
|
|
16
16
|
|
17
17
|
# @param fqns [String]
|
18
18
|
# @param visibility [Array<Symbol>]
|
19
|
-
# @return [
|
19
|
+
# @return [Enumerable<Solargraph::Pin::Base>]
|
20
20
|
def get_constants fqns, visibility = [:public]
|
21
21
|
namespace_children(fqns).select { |pin|
|
22
22
|
!pin.name.empty? && (pin.is_a?(Pin::Namespace) || pin.is_a?(Pin::Constant)) && visibility.include?(pin.visibility)
|
@@ -26,7 +26,7 @@ module Solargraph
|
|
26
26
|
# @param fqns [String]
|
27
27
|
# @param scope [Symbol]
|
28
28
|
# @param visibility [Array<Symbol>]
|
29
|
-
# @return [
|
29
|
+
# @return [Enumerable<Solargraph::Pin::Base>]
|
30
30
|
def get_methods fqns, scope: :instance, visibility: [:public]
|
31
31
|
namespace_children(fqns).select do |pin|
|
32
32
|
pin.is_a?(Pin::Method) && pin.scope == scope && visibility.include?(pin.visibility)
|
@@ -61,14 +61,14 @@ module Solargraph
|
|
61
61
|
end
|
62
62
|
|
63
63
|
# @param path [String]
|
64
|
-
# @return [
|
64
|
+
# @return [Enumerable<Solargraph::Pin::Base>]
|
65
65
|
def get_path_pins path
|
66
66
|
path_pin_hash[path] || []
|
67
67
|
end
|
68
68
|
|
69
69
|
# @param fqns [String]
|
70
70
|
# @param scope [Symbol] :class or :instance
|
71
|
-
# @return [
|
71
|
+
# @return [Enumerable<Solargraph::Pin::Base>]
|
72
72
|
def get_instance_variables(fqns, scope = :instance)
|
73
73
|
all_instance_variables.select { |pin|
|
74
74
|
pin.binder.namespace == fqns && pin.binder.scope == scope
|
@@ -76,12 +76,12 @@ module Solargraph
|
|
76
76
|
end
|
77
77
|
|
78
78
|
# @param fqns [String]
|
79
|
-
# @return [
|
79
|
+
# @return [Enumerable<Solargraph::Pin::Base>]
|
80
80
|
def get_class_variables(fqns)
|
81
81
|
namespace_children(fqns).select{|pin| pin.is_a?(Pin::ClassVariable)}
|
82
82
|
end
|
83
83
|
|
84
|
-
# @return [
|
84
|
+
# @return [Enumerable<Solargraph::Pin::Base>]
|
85
85
|
def get_symbols
|
86
86
|
symbols.uniq(&:name)
|
87
87
|
end
|
@@ -97,12 +97,12 @@ module Solargraph
|
|
97
97
|
@namespaces ||= Set.new
|
98
98
|
end
|
99
99
|
|
100
|
-
# @return [
|
100
|
+
# @return [Enumerable<Solargraph::Pin::Base>]
|
101
101
|
def namespace_pins
|
102
102
|
pins_by_class(Solargraph::Pin::Namespace)
|
103
103
|
end
|
104
104
|
|
105
|
-
# @return [
|
105
|
+
# @return [Enumerable<Solargraph::Pin::Method>]
|
106
106
|
def method_pins
|
107
107
|
pins_by_class(Solargraph::Pin::Method)
|
108
108
|
end
|
@@ -131,7 +131,7 @@ module Solargraph
|
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
|
-
# @return [
|
134
|
+
# @return [Enumerable<Pin::Block>]
|
135
135
|
def block_pins
|
136
136
|
pins_by_class(Pin::Block)
|
137
137
|
end
|
@@ -142,9 +142,9 @@ module Solargraph
|
|
142
142
|
end
|
143
143
|
|
144
144
|
# @param klass [Class]
|
145
|
-
# @return [
|
145
|
+
# @return [Enumerable<Solargraph::Pin::Base>]
|
146
146
|
def pins_by_class klass
|
147
|
-
@pin_select_cache[klass] ||= @pin_class_hash.
|
147
|
+
@pin_select_cache[klass] ||= @pin_class_hash.each_with_object(Set.new) { |(key, o), n| n.merge(o) if key <= klass }
|
148
148
|
end
|
149
149
|
|
150
150
|
private
|
@@ -171,7 +171,7 @@ module Solargraph
|
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|
174
|
-
# @return [
|
174
|
+
# @return [Enumerable<Solargraph::Pin::Symbol>]
|
175
175
|
def symbols
|
176
176
|
pins_by_class(Pin::Symbol)
|
177
177
|
end
|
@@ -193,7 +193,7 @@ module Solargraph
|
|
193
193
|
end
|
194
194
|
|
195
195
|
# @param name [String]
|
196
|
-
# @return [
|
196
|
+
# @return [Enumerable<Solargraph::Pin::Base>]
|
197
197
|
def namespace_children name
|
198
198
|
namespace_map[name] || []
|
199
199
|
end
|
@@ -216,8 +216,8 @@ module Solargraph
|
|
216
216
|
set = pins.to_set
|
217
217
|
@pin_class_hash = set.classify(&:class).transform_values(&:to_a)
|
218
218
|
@pin_select_cache = {}
|
219
|
-
@namespace_map = set.classify(&:namespace)
|
220
|
-
@path_pin_hash = set.classify(&:path)
|
219
|
+
@namespace_map = set.classify(&:namespace)
|
220
|
+
@path_pin_hash = set.classify(&:path)
|
221
221
|
@namespaces = @path_pin_hash.keys.compact.to_set
|
222
222
|
pins_by_class(Pin::Reference::Include).each do |pin|
|
223
223
|
include_references[pin.namespace] ||= []
|
data/lib/solargraph/api_map.rb
CHANGED
@@ -138,7 +138,7 @@ module Solargraph
|
|
138
138
|
|
139
139
|
# An array of pins based on Ruby keywords (`if`, `end`, etc.).
|
140
140
|
#
|
141
|
-
# @return [
|
141
|
+
# @return [Enumerable<Solargraph::Pin::Keyword>]
|
142
142
|
def keyword_pins
|
143
143
|
store.pins_by_class(Pin::Keyword)
|
144
144
|
end
|
@@ -72,9 +72,12 @@ module Solargraph
|
|
72
72
|
|
73
73
|
# @return [String]
|
74
74
|
def namespace
|
75
|
-
|
76
|
-
@namespace ||=
|
77
|
-
|
75
|
+
# if priority higher than ||=, old implements cause unnecessary check
|
76
|
+
@namespace ||= lambda do
|
77
|
+
return 'Object' if duck_type?
|
78
|
+
return 'NilClass' if nil_type?
|
79
|
+
return (name == 'Class' || name == 'Module') && !subtypes.empty? ? subtypes.first.name : name
|
80
|
+
end.call
|
78
81
|
end
|
79
82
|
|
80
83
|
# @return [Symbol] :class or :instance
|
@@ -51,6 +51,10 @@ module Solargraph
|
|
51
51
|
def select &block
|
52
52
|
@items.select &block
|
53
53
|
end
|
54
|
+
def namespace
|
55
|
+
# cache this attr for high frequency call
|
56
|
+
@namespace ||= method_missing(:namespace).to_s
|
57
|
+
end
|
54
58
|
|
55
59
|
def method_missing name, *args, &block
|
56
60
|
return if @items.first.nil?
|
@@ -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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
@@ -41,7 +41,8 @@ module Solargraph
|
|
41
41
|
result = explicit_library_for(uri) ||
|
42
42
|
implicit_library_for(uri) ||
|
43
43
|
generic_library_for(uri)
|
44
|
-
|
44
|
+
# previous library for already call attach. avoid call twice
|
45
|
+
# result.attach sources.find(uri) if sources.include?(uri)
|
45
46
|
result
|
46
47
|
end
|
47
48
|
|
@@ -82,7 +83,6 @@ module Solargraph
|
|
82
83
|
libraries.each do |lib|
|
83
84
|
if filename.start_with?(lib.workspace.directory)
|
84
85
|
lib.attach sources.find(uri)
|
85
|
-
lib.catalog
|
86
86
|
return lib
|
87
87
|
end
|
88
88
|
end
|
@@ -98,7 +98,6 @@ module Solargraph
|
|
98
98
|
# @return [Library]
|
99
99
|
def generic_library_for uri
|
100
100
|
generic_library.attach sources.find(uri)
|
101
|
-
generic_library.catalog
|
102
101
|
generic_library
|
103
102
|
end
|
104
103
|
|
@@ -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,12 @@ module Solargraph
|
|
12
12
|
# safety for multi-threaded transports.
|
13
13
|
#
|
14
14
|
class Host
|
15
|
-
autoload :Diagnoser,
|
16
|
-
autoload :Cataloger,
|
17
|
-
autoload :Sources,
|
18
|
-
autoload :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'
|
20
|
+
|
19
21
|
|
20
22
|
include UriHelpers
|
21
23
|
include Logging
|
@@ -27,7 +29,7 @@ module Solargraph
|
|
27
29
|
def initialize
|
28
30
|
@cancel_semaphore = Mutex.new
|
29
31
|
@buffer_semaphore = Mutex.new
|
30
|
-
@
|
32
|
+
@request_mutex = Mutex.new
|
31
33
|
@cancel = []
|
32
34
|
@buffer = String.new
|
33
35
|
@stopped = true
|
@@ -45,6 +47,7 @@ module Solargraph
|
|
45
47
|
diagnoser.start
|
46
48
|
cataloger.start
|
47
49
|
sources.start
|
50
|
+
message_worker.start
|
48
51
|
end
|
49
52
|
|
50
53
|
# Update the configuration options with the provided hash.
|
@@ -89,8 +92,15 @@ module Solargraph
|
|
89
92
|
@cancel_semaphore.synchronize { @cancel.delete id }
|
90
93
|
end
|
91
94
|
|
95
|
+
# Called by adapter, to handle the request
|
96
|
+
# @param request [Hash]
|
97
|
+
# @return [void]
|
98
|
+
def process request
|
99
|
+
message_worker.queue(request)
|
100
|
+
end
|
101
|
+
|
92
102
|
# Start processing a request from the client. After the message is
|
93
|
-
# processed,
|
103
|
+
# processed, caller is responsible for sending the response.
|
94
104
|
#
|
95
105
|
# @param request [Hash] The contents of the message.
|
96
106
|
# @return [Solargraph::LanguageServer::Message::Base] The message handler.
|
@@ -355,19 +365,21 @@ module Solargraph
|
|
355
365
|
# @yieldparam [Hash] The result sent by the client
|
356
366
|
# @return [void]
|
357
367
|
def send_request method, params, &block
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
368
|
+
@request_mutex.synchronize do
|
369
|
+
message = {
|
370
|
+
jsonrpc: "2.0",
|
371
|
+
method: method,
|
372
|
+
params: params,
|
373
|
+
id: @next_request_id
|
374
|
+
}
|
375
|
+
json = message.to_json
|
376
|
+
requests[@next_request_id] = Request.new(@next_request_id, &block)
|
377
|
+
envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
|
378
|
+
queue envelope
|
379
|
+
@next_request_id += 1
|
380
|
+
logger.info "Server sent #{method}"
|
381
|
+
logger.debug params
|
382
|
+
end
|
371
383
|
end
|
372
384
|
|
373
385
|
# Register the methods as capabilities with the client.
|
@@ -378,20 +390,16 @@ module Solargraph
|
|
378
390
|
# @return [void]
|
379
391
|
def register_capabilities methods
|
380
392
|
logger.debug "Registering capabilities: #{methods}"
|
381
|
-
registrations = methods.select{|m| can_register?(m) and !registered?(m)}.map
|
393
|
+
registrations = methods.select { |m| can_register?(m) and !registered?(m) }.map do |m|
|
382
394
|
@registered_capabilities.add m
|
383
395
|
{
|
384
396
|
id: m,
|
385
397
|
method: m,
|
386
398
|
registerOptions: dynamic_capability_options[m]
|
387
399
|
}
|
388
|
-
}
|
389
|
-
return if registrations.empty?
|
390
|
-
@register_semaphore.synchronize do
|
391
|
-
send_request 'client/registerCapability', {
|
392
|
-
registrations: registrations
|
393
|
-
}
|
394
400
|
end
|
401
|
+
return if registrations.empty?
|
402
|
+
send_request 'client/registerCapability', { registrations: registrations }
|
395
403
|
end
|
396
404
|
|
397
405
|
# Unregister the methods with the client.
|
@@ -410,11 +418,7 @@ module Solargraph
|
|
410
418
|
}
|
411
419
|
}
|
412
420
|
return if unregisterations.empty?
|
413
|
-
|
414
|
-
send_request 'client/unregisterCapability', {
|
415
|
-
unregisterations: unregisterations
|
416
|
-
}
|
417
|
-
end
|
421
|
+
send_request 'client/unregisterCapability', { unregisterations: unregisterations }
|
418
422
|
end
|
419
423
|
|
420
424
|
# Flag a method as available for dynamic registration.
|
@@ -422,9 +426,7 @@ module Solargraph
|
|
422
426
|
# @param method [String] The method name, e.g., 'textDocument/completion'
|
423
427
|
# @return [void]
|
424
428
|
def allow_registration method
|
425
|
-
@
|
426
|
-
@dynamic_capabilities.add method
|
427
|
-
end
|
429
|
+
@dynamic_capabilities.add method
|
428
430
|
end
|
429
431
|
|
430
432
|
# True if the specified LSP method can be dynamically registered.
|
@@ -451,6 +453,7 @@ module Solargraph
|
|
451
453
|
def stop
|
452
454
|
return if @stopped
|
453
455
|
@stopped = true
|
456
|
+
message_worker.stop
|
454
457
|
cataloger.stop
|
455
458
|
diagnoser.stop
|
456
459
|
sources.stop
|
@@ -513,6 +516,11 @@ module Solargraph
|
|
513
516
|
library.completions_at uri_to_file(uri), line, column
|
514
517
|
end
|
515
518
|
|
519
|
+
# @return [Bool] if has pending completion request
|
520
|
+
def has_pending_completions?
|
521
|
+
message_worker.messages.reverse_each.any? { |req| req['method'] == 'textDocument/completion' }
|
522
|
+
end
|
523
|
+
|
516
524
|
# @param uri [String]
|
517
525
|
# @param line [Integer]
|
518
526
|
# @param column [Integer]
|
@@ -646,6 +654,11 @@ module Solargraph
|
|
646
654
|
|
647
655
|
private
|
648
656
|
|
657
|
+
# @return [MessageWorker]
|
658
|
+
def message_worker
|
659
|
+
@message_worker ||= MessageWorker.new(self)
|
660
|
+
end
|
661
|
+
|
649
662
|
# @return [Diagnoser]
|
650
663
|
def diagnoser
|
651
664
|
@diagnoser ||= Diagnoser.new(self)
|
@@ -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
|
-
|
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,
|
@@ -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
|
data/lib/solargraph/library.rb
CHANGED
@@ -56,7 +56,7 @@ module Solargraph
|
|
56
56
|
end
|
57
57
|
@current = source
|
58
58
|
maybe_map @current
|
59
|
-
|
59
|
+
catalog_inlock
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -368,12 +368,16 @@ module Solargraph
|
|
368
368
|
# @return [void]
|
369
369
|
def catalog
|
370
370
|
mutex.synchronize do
|
371
|
-
|
371
|
+
catalog_inlock
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
private def catalog_inlock
|
376
|
+
return if synchronized?
|
372
377
|
logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
|
373
378
|
api_map.catalog bench
|
374
379
|
@synchronized = true
|
375
380
|
logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)" if logger.info?
|
376
|
-
end
|
377
381
|
end
|
378
382
|
|
379
383
|
def bench
|
@@ -514,6 +518,7 @@ module Solargraph
|
|
514
518
|
|
515
519
|
def maybe_map source
|
516
520
|
return unless source
|
521
|
+
return unless @current == source || workspace.has_file?(source.filename)
|
517
522
|
if source_map_hash.key?(source.filename)
|
518
523
|
return if source_map_hash[source.filename].code == source.code &&
|
519
524
|
source_map_hash[source.filename].source.synchronized? &&
|
@@ -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
|
@@ -102,13 +102,26 @@ module Solargraph
|
|
102
102
|
result.concat generate_links(n.children[0])
|
103
103
|
elsif n.type == :block_pass
|
104
104
|
result.push Chain::BlockVariable.new("&#{n.children[0].children[0].to_s}")
|
105
|
+
elsif n.type == :hash
|
106
|
+
result.push Chain::Hash.new('::Hash', hash_is_splatted?(n))
|
105
107
|
else
|
106
108
|
lit = infer_literal_node_type(n)
|
107
|
-
|
109
|
+
# if lit == '::Hash'
|
110
|
+
# result.push Chain::Hash.new(lit, hash_is_splatted?(n))
|
111
|
+
# else
|
112
|
+
result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
|
113
|
+
# end
|
108
114
|
end
|
109
115
|
result
|
110
116
|
end
|
111
117
|
|
118
|
+
def hash_is_splatted? node
|
119
|
+
return false unless Parser.is_ast_node?(node) && node.type == :hash
|
120
|
+
return false unless Parser.is_ast_node?(node.children.last) && node.children.last.type == :kwsplat
|
121
|
+
return false if Parser.is_ast_node?(node.children.last.children[0]) && node.children.last.children[0].type == :hash
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
112
125
|
def block_passed? node
|
113
126
|
node.children.last.is_a?(::Parser::AST::Node) && node.children.last.type == :block_pass
|
114
127
|
end
|
@@ -97,8 +97,10 @@ module Solargraph
|
|
97
97
|
end
|
98
98
|
|
99
99
|
def convert_hash node
|
100
|
-
return {} unless Parser.is_ast_node?(node)
|
101
|
-
return convert_hash(node.children[0]
|
100
|
+
return {} unless Parser.is_ast_node?(node)
|
101
|
+
return convert_hash(node.children[0]) if node.type == :kwsplat
|
102
|
+
return convert_hash(node.children[0]) if Parser.is_ast_node?(node.children[0]) && node.children[0].type == :kwsplat
|
103
|
+
return {} unless node.type == :hash
|
102
104
|
result = {}
|
103
105
|
node.children.each do |pair|
|
104
106
|
result[pair.children[0].children[0]] = Solargraph::Parser.chain(pair.children[1])
|
@@ -124,9 +126,14 @@ module Solargraph
|
|
124
126
|
end
|
125
127
|
|
126
128
|
def splatted_call? node
|
129
|
+
return false unless Parser.is_ast_node?(node)
|
127
130
|
Parser.is_ast_node?(node.children[0]) && node.children[0].type == :kwsplat && node.children[0].children[0].type != :hash
|
128
131
|
end
|
129
132
|
|
133
|
+
def any_splatted_call?(nodes)
|
134
|
+
nodes.any? { |n| splatted_call?(n) }
|
135
|
+
end
|
136
|
+
|
130
137
|
# @todo Temporarily here for testing. Move to Solargraph::Parser.
|
131
138
|
def call_nodes_from node
|
132
139
|
return [] unless node.is_a?(::Parser::AST::Node)
|
@@ -97,13 +97,31 @@ module Solargraph
|
|
97
97
|
result.concat generate_links(n.children[0])
|
98
98
|
elsif n.type == :BLOCK_PASS
|
99
99
|
result.push Chain::BlockVariable.new("&#{n.children[1].children[0].to_s}")
|
100
|
+
elsif n.type == :HASH
|
101
|
+
result.push Chain::Hash.new('::Hash', hash_is_splatted?(n))
|
100
102
|
else
|
101
103
|
lit = infer_literal_node_type(n)
|
102
|
-
|
104
|
+
if lit
|
105
|
+
if lit == '::Hash'
|
106
|
+
result.push Chain::Hash.new(lit, hash_is_splatted?(n))
|
107
|
+
else
|
108
|
+
result.push Chain::Literal.new(lit)
|
109
|
+
end
|
110
|
+
else
|
111
|
+
result.push Chain::Link.new
|
112
|
+
end
|
113
|
+
# result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
|
103
114
|
end
|
104
115
|
result
|
105
116
|
end
|
106
117
|
|
118
|
+
def hash_is_splatted? node
|
119
|
+
return false unless Parser.is_ast_node?(node.children[0]) && node.children[0].type == :LIST
|
120
|
+
list = node.children[0].children
|
121
|
+
eol = list.rindex(&:nil?)
|
122
|
+
eol && Parser.is_ast_node?(list[eol + 1])
|
123
|
+
end
|
124
|
+
|
107
125
|
def block_passed? node
|
108
126
|
node.children.last.is_a?(RubyVM::AbstractSyntaxTree::Node) && node.children.last.type == :BLOCK_PASS
|
109
127
|
end
|
@@ -114,6 +132,9 @@ module Solargraph
|
|
114
132
|
node.children[0..-2].map { |c| NodeChainer.chain(c) }
|
115
133
|
elsif node.type == :SPLAT
|
116
134
|
[NodeChainer.chain(node)]
|
135
|
+
elsif node.type == :ARGSPUSH
|
136
|
+
result = node_to_argchains(node.children[0])
|
137
|
+
result.push NodeChainer.chain(node.children[1]) if Parser.is_ast_node?(node.children[1])
|
117
138
|
elsif node.type == :ARGSCAT
|
118
139
|
result = node.children[0].children[0..-2].map { |c| NodeChainer.chain(c) }
|
119
140
|
result.push NodeChainer.chain(node.children[1])
|
@@ -117,9 +117,14 @@ module Solargraph
|
|
117
117
|
end
|
118
118
|
|
119
119
|
def splatted_call? node
|
120
|
+
return false unless Parser.is_ast_node?(node)
|
120
121
|
splatted_node?(node) && node.children[0].children[1].type != :HASH
|
121
122
|
end
|
122
123
|
|
124
|
+
def any_splatted_call?(nodes)
|
125
|
+
nodes.any? { |n| splatted_call?(n) }
|
126
|
+
end
|
127
|
+
|
123
128
|
def node? node
|
124
129
|
node.is_a?(RubyVM::AbstractSyntaxTree::Node)
|
125
130
|
end
|
data/lib/solargraph/pin/base.rb
CHANGED
@@ -22,12 +22,18 @@ module Solargraph
|
|
22
22
|
@closure = Solargraph::Pin::ROOT_PIN
|
23
23
|
end
|
24
24
|
@open_gates = gates
|
25
|
-
if @
|
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
|
-
|
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
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
class Source
|
5
|
+
class Chain
|
6
|
+
class Hash < Literal
|
7
|
+
# @param type [String]
|
8
|
+
# @param splatted [Boolean]
|
9
|
+
def initialize type, splatted = false
|
10
|
+
super(type)
|
11
|
+
@splatted = splatted
|
12
|
+
end
|
13
|
+
|
14
|
+
def word
|
15
|
+
@word ||= "<#{@type}>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def resolve api_map, name_pin, locals
|
19
|
+
[Pin::ProxyType.anonymous(@complex_type)]
|
20
|
+
end
|
21
|
+
|
22
|
+
def splatted?
|
23
|
+
@splatted
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -21,6 +21,7 @@ module Solargraph
|
|
21
21
|
autoload :Or, 'solargraph/source/chain/or'
|
22
22
|
autoload :BlockVariable, 'solargraph/source/chain/block_variable'
|
23
23
|
autoload :ZSuper, 'solargraph/source/chain/z_super'
|
24
|
+
autoload :Hash, 'solargraph/source/chain/hash'
|
24
25
|
|
25
26
|
@@inference_stack = []
|
26
27
|
@@inference_depth = 0
|
@@ -213,7 +213,7 @@ module Solargraph
|
|
213
213
|
result.concat api_map.get_methods(block.binder.namespace, scope: block.binder.scope, visibility: [:public, :private, :protected])
|
214
214
|
result.concat api_map.get_methods('Kernel')
|
215
215
|
# result.concat ApiMap.keywords
|
216
|
-
result.concat api_map.keyword_pins
|
216
|
+
result.concat api_map.keyword_pins.to_a
|
217
217
|
result.concat yielded_self_pins
|
218
218
|
end
|
219
219
|
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
|
@@ -148,6 +148,7 @@ module Solargraph
|
|
148
148
|
all_variables.each do |pin|
|
149
149
|
if pin.return_type.defined?
|
150
150
|
declared = pin.typify(api_map)
|
151
|
+
next if declared.duck_type?
|
151
152
|
if declared.defined?
|
152
153
|
if rules.validate_tags?
|
153
154
|
inferred = pin.probe(api_map)
|
@@ -416,7 +417,7 @@ module Solargraph
|
|
416
417
|
end
|
417
418
|
settled_kwargs = 0
|
418
419
|
unless unchecked.empty?
|
419
|
-
if
|
420
|
+
if any_splatted_call?(unchecked.map(&:node))
|
420
421
|
settled_kwargs = pin.parameters.count(&:keyword?)
|
421
422
|
else
|
422
423
|
kwargs = convert_hash(unchecked.last.node)
|
@@ -431,6 +432,7 @@ module Solargraph
|
|
431
432
|
kwargs.delete param.name.to_sym
|
432
433
|
settled_kwargs += 1
|
433
434
|
elsif param.decl == :kwarg
|
435
|
+
return [] if arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash) && arguments.last.links.last.splatted?
|
434
436
|
return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
|
435
437
|
end
|
436
438
|
end
|
@@ -450,12 +452,17 @@ module Solargraph
|
|
450
452
|
if unchecked.length == req + opt + 1 && unchecked.last.links.last.is_a?(Source::Chain::BlockVariable)
|
451
453
|
return []
|
452
454
|
end
|
453
|
-
if req + add_params + 1 == unchecked.length &&
|
455
|
+
if req + add_params + 1 == unchecked.length && any_splatted_call?(unchecked.map(&:node)) && (pin.parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
|
454
456
|
return []
|
455
457
|
end
|
458
|
+
return [] if arguments.length - req == pin.parameters.select { |p| [:optarg, :kwoptarg].include?(p.decl) }.length
|
456
459
|
return [Problem.new(location, "Too many arguments to #{pin.path}")]
|
457
|
-
elsif unchecked.length < req - settled_kwargs && (arguments.empty? || !arguments.last.splat?)
|
458
|
-
|
460
|
+
elsif unchecked.length < req - settled_kwargs && (arguments.empty? || (!arguments.last.splat? && !arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash)))
|
461
|
+
# HACK: Kernel#raise signature is incorrect in Ruby 2.7 core docs.
|
462
|
+
# See https://github.com/castwide/solargraph/issues/418
|
463
|
+
unless arguments.empty? && pin.path == 'Kernel#raise'
|
464
|
+
return [Problem.new(location, "Not enough arguments to #{pin.path}")]
|
465
|
+
end
|
459
466
|
end
|
460
467
|
[]
|
461
468
|
end
|
data/lib/solargraph/version.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
Namespace:
|
3
3
|
</h2>
|
4
4
|
<p>
|
5
|
-
<a href="
|
5
|
+
<a href="solargraph:/document?query=<%= CGI.escape object.namespace.path %>"><%= object.namespace %></a>
|
6
6
|
</p>
|
7
7
|
<h2>
|
8
8
|
Overview:
|
data/lib/solargraph/workspace.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
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) })
|
data/lib/solargraph/yard_map.rb
CHANGED
@@ -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.
|
4
|
+
version: 0.43.3
|
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-
|
11
|
+
date: 2021-09-25 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
|
@@ -502,6 +503,7 @@ files:
|
|
502
503
|
- lib/solargraph/source/chain/class_variable.rb
|
503
504
|
- lib/solargraph/source/chain/constant.rb
|
504
505
|
- lib/solargraph/source/chain/global_variable.rb
|
506
|
+
- lib/solargraph/source/chain/hash.rb
|
505
507
|
- lib/solargraph/source/chain/head.rb
|
506
508
|
- lib/solargraph/source/chain/instance_variable.rb
|
507
509
|
- lib/solargraph/source/chain/link.rb
|