solargraph 0.54.0 → 0.55.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 +4 -4
- data/CHANGELOG.md +58 -0
- data/lib/solargraph/api_map/cache.rb +10 -1
- data/lib/solargraph/api_map/index.rb +167 -0
- data/lib/solargraph/api_map/store.rb +75 -122
- data/lib/solargraph/api_map.rb +109 -41
- data/lib/solargraph/bench.rb +17 -1
- data/lib/solargraph/complex_type/type_methods.rb +17 -11
- data/lib/solargraph/complex_type/unique_type.rb +181 -17
- data/lib/solargraph/complex_type.rb +103 -24
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +51 -0
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
- data/lib/solargraph/convention/struct_definition.rb +101 -0
- data/lib/solargraph/convention.rb +2 -0
- data/lib/solargraph/doc_map.rb +43 -18
- data/lib/solargraph/equality.rb +33 -0
- data/lib/solargraph/language_server/host/message_worker.rb +51 -5
- data/lib/solargraph/language_server/host.rb +13 -11
- data/lib/solargraph/language_server/message/base.rb +19 -12
- data/lib/solargraph/language_server/message/initialize.rb +3 -1
- data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
- data/lib/solargraph/language_server/message/text_document/definition.rb +3 -3
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
- data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -0
- data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
- data/lib/solargraph/language_server/message/text_document/type_definition.rb +3 -3
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
- data/lib/solargraph/language_server/progress.rb +19 -2
- data/lib/solargraph/library.rb +31 -41
- data/lib/solargraph/location.rb +29 -1
- data/lib/solargraph/parser/comment_ripper.rb +11 -6
- data/lib/solargraph/parser/flow_sensitive_typing.rb +226 -0
- data/lib/solargraph/parser/node_methods.rb +15 -1
- data/lib/solargraph/parser/parser_gem/class_methods.rb +11 -6
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +10 -10
- data/lib/solargraph/parser/parser_gem/node_methods.rb +6 -4
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +23 -19
- data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +21 -1
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +8 -2
- data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +26 -5
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +1 -1
- data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +28 -0
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +28 -0
- data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
- data/lib/solargraph/parser.rb +3 -5
- data/lib/solargraph/pin/base.rb +47 -17
- data/lib/solargraph/pin/base_variable.rb +11 -4
- data/lib/solargraph/pin/block.rb +8 -26
- data/lib/solargraph/pin/breakable.rb +9 -0
- data/lib/solargraph/pin/callable.rb +147 -0
- data/lib/solargraph/pin/closure.rb +8 -3
- data/lib/solargraph/pin/common.rb +2 -6
- data/lib/solargraph/pin/conversions.rb +3 -2
- data/lib/solargraph/pin/instance_variable.rb +2 -2
- data/lib/solargraph/pin/local_variable.rb +7 -1
- data/lib/solargraph/pin/method.rb +76 -48
- data/lib/solargraph/pin/namespace.rb +14 -11
- data/lib/solargraph/pin/parameter.rb +24 -17
- data/lib/solargraph/pin/proxy_type.rb +13 -7
- data/lib/solargraph/pin/signature.rb +3 -129
- data/lib/solargraph/pin/until.rb +18 -0
- data/lib/solargraph/pin/while.rb +18 -0
- data/lib/solargraph/pin.rb +7 -1
- data/lib/solargraph/position.rb +7 -0
- data/lib/solargraph/range.rb +9 -4
- data/lib/solargraph/rbs_map/conversions.rb +84 -45
- data/lib/solargraph/rbs_map/core_fills.rb +16 -9
- data/lib/solargraph/rbs_map.rb +1 -0
- data/lib/solargraph/shell.rb +19 -2
- data/lib/solargraph/source/chain/array.rb +10 -8
- data/lib/solargraph/source/chain/block_symbol.rb +1 -1
- data/lib/solargraph/source/chain/block_variable.rb +1 -1
- data/lib/solargraph/source/chain/call.rb +125 -61
- data/lib/solargraph/source/chain/constant.rb +1 -1
- data/lib/solargraph/source/chain/hash.rb +8 -2
- data/lib/solargraph/source/chain/if.rb +5 -0
- data/lib/solargraph/source/chain/link.rb +28 -5
- data/lib/solargraph/source/chain/literal.rb +27 -2
- data/lib/solargraph/source/chain/or.rb +1 -1
- data/lib/solargraph/source/chain/z_super.rb +1 -1
- data/lib/solargraph/source/chain.rb +131 -63
- data/lib/solargraph/source/cursor.rb +3 -2
- data/lib/solargraph/source/source_chainer.rb +2 -2
- data/lib/solargraph/source.rb +104 -86
- data/lib/solargraph/source_map/clip.rb +8 -6
- data/lib/solargraph/source_map/data.rb +30 -0
- data/lib/solargraph/source_map.rb +28 -16
- data/lib/solargraph/type_checker/checks.rb +4 -0
- data/lib/solargraph/type_checker/rules.rb +6 -1
- data/lib/solargraph/type_checker.rb +48 -21
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/views/environment.erb +3 -5
- data/lib/solargraph/workspace/config.rb +7 -3
- data/lib/solargraph/workspace.rb +1 -1
- data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -0
- data/lib/solargraph/yard_map/mapper/to_method.rb +42 -15
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
- data/lib/solargraph/yard_map/mapper.rb +1 -0
- data/lib/solargraph/yardoc.rb +1 -1
- data/lib/solargraph.rb +1 -0
- data/solargraph.gemspec +5 -5
- metadata +41 -25
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Convention
|
|
5
|
+
module StructDefinition
|
|
6
|
+
autoload :StructDefintionNode, 'solargraph/convention/struct_definition/struct_definition_node'
|
|
7
|
+
autoload :StructAssignmentNode, 'solargraph/convention/struct_definition/struct_assignment_node'
|
|
8
|
+
|
|
9
|
+
module NodeProcessors
|
|
10
|
+
class StructNode < Parser::NodeProcessor::Base
|
|
11
|
+
def process
|
|
12
|
+
return if struct_definition_node.nil?
|
|
13
|
+
|
|
14
|
+
loc = get_node_location(node)
|
|
15
|
+
nspin = Solargraph::Pin::Namespace.new(
|
|
16
|
+
type: :class,
|
|
17
|
+
location: loc,
|
|
18
|
+
closure: region.closure,
|
|
19
|
+
name: struct_definition_node.class_name,
|
|
20
|
+
comments: comments_for(node),
|
|
21
|
+
visibility: :public,
|
|
22
|
+
gates: region.closure.gates.freeze
|
|
23
|
+
)
|
|
24
|
+
pins.push nspin
|
|
25
|
+
|
|
26
|
+
# define initialize method
|
|
27
|
+
initialize_method_pin = Pin::Method.new(
|
|
28
|
+
name: 'initialize',
|
|
29
|
+
parameters: [],
|
|
30
|
+
scope: :instance,
|
|
31
|
+
location: get_node_location(node),
|
|
32
|
+
closure: nspin,
|
|
33
|
+
visibility: :private,
|
|
34
|
+
comments: comments_for(node)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
pins.push initialize_method_pin
|
|
38
|
+
|
|
39
|
+
struct_definition_node.attributes.map do |attribute_node, attribute_name|
|
|
40
|
+
initialize_method_pin.parameters.push(
|
|
41
|
+
Pin::Parameter.new(
|
|
42
|
+
name: attribute_name,
|
|
43
|
+
decl: struct_definition_node.keyword_init? ? :kwarg : :arg,
|
|
44
|
+
location: get_node_location(attribute_node),
|
|
45
|
+
closure: initialize_method_pin
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# define attribute accessors and instance variables
|
|
51
|
+
struct_definition_node.attributes.each do |attribute_node, attribute_name|
|
|
52
|
+
[attribute_name, "#{attribute_name}="].each do |name|
|
|
53
|
+
method_pin = Pin::Method.new(
|
|
54
|
+
name: name,
|
|
55
|
+
parameters: [],
|
|
56
|
+
scope: :instance,
|
|
57
|
+
location: get_node_location(attribute_node),
|
|
58
|
+
closure: nspin,
|
|
59
|
+
comments: attribute_comments(attribute_node, attribute_name),
|
|
60
|
+
visibility: :public
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
pins.push method_pin
|
|
64
|
+
|
|
65
|
+
next unless name.include?('=') # setter
|
|
66
|
+
pins.push Pin::InstanceVariable.new(name: "@#{attribute_name}",
|
|
67
|
+
closure: method_pin,
|
|
68
|
+
location: get_node_location(attribute_node),
|
|
69
|
+
comments: attribute_comments(attribute_node, attribute_name))
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
process_children region.update(closure: nspin, visibility: :public)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
# @return [StructDefintionNode, nil]
|
|
79
|
+
def struct_definition_node
|
|
80
|
+
@struct_definition_node ||= if StructDefintionNode.valid?(node)
|
|
81
|
+
StructDefintionNode.new(node)
|
|
82
|
+
elsif StructAssignmentNode.valid?(node)
|
|
83
|
+
StructAssignmentNode.new(node)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @param attribute_node [Parser::AST::Node]
|
|
88
|
+
# @return [String, nil]
|
|
89
|
+
def attribute_comments(attribute_node, attribute_name)
|
|
90
|
+
struct_comments = comments_for(attribute_node)
|
|
91
|
+
return if struct_comments.nil? || struct_comments.empty?
|
|
92
|
+
|
|
93
|
+
struct_comments.split("\n").find do |row|
|
|
94
|
+
row.include?(attribute_name)
|
|
95
|
+
end&.gsub('@param', '@return')&.gsub(attribute_name, '')
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -10,7 +10,9 @@ module Solargraph
|
|
|
10
10
|
autoload :Gemfile, 'solargraph/convention/gemfile'
|
|
11
11
|
autoload :Gemspec, 'solargraph/convention/gemspec'
|
|
12
12
|
autoload :Rakefile, 'solargraph/convention/rakefile'
|
|
13
|
+
autoload :StructDefinition, 'solargraph/convention/struct_definition'
|
|
13
14
|
|
|
15
|
+
# @type [Set<Convention::Base>]
|
|
14
16
|
@@conventions = Set.new
|
|
15
17
|
|
|
16
18
|
# @param convention [Class<Convention::Base>]
|
data/lib/solargraph/doc_map.rb
CHANGED
|
@@ -4,6 +4,8 @@ module Solargraph
|
|
|
4
4
|
# A collection of pins generated from required gems.
|
|
5
5
|
#
|
|
6
6
|
class DocMap
|
|
7
|
+
include Logging
|
|
8
|
+
|
|
7
9
|
# @return [Array<String>]
|
|
8
10
|
attr_reader :requires
|
|
9
11
|
|
|
@@ -28,12 +30,12 @@ module Solargraph
|
|
|
28
30
|
|
|
29
31
|
# @return [Array<Gem::Specification>]
|
|
30
32
|
def gemspecs
|
|
31
|
-
@gemspecs ||=
|
|
33
|
+
@gemspecs ||= required_gems_map.values.compact.flatten
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
# @return [Array<String>]
|
|
35
37
|
def unresolved_requires
|
|
36
|
-
@unresolved_requires ||=
|
|
38
|
+
@unresolved_requires ||= required_gems_map.select { |_, gemspecs| gemspecs.nil? }.keys
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
# @return [Hash{Gem::Specification => Array[Pin::Base]}]
|
|
@@ -52,20 +54,22 @@ module Solargraph
|
|
|
52
54
|
def generate
|
|
53
55
|
@pins = []
|
|
54
56
|
@uncached_gemspecs = []
|
|
55
|
-
|
|
56
|
-
if
|
|
57
|
-
try_cache gemspec
|
|
58
|
-
else
|
|
57
|
+
required_gems_map.each do |path, gemspecs|
|
|
58
|
+
if gemspecs.nil?
|
|
59
59
|
try_stdlib_map path
|
|
60
|
+
else
|
|
61
|
+
gemspecs.each do |gemspec|
|
|
62
|
+
try_cache gemspec
|
|
63
|
+
end
|
|
60
64
|
end
|
|
61
65
|
end
|
|
62
66
|
dependencies.each { |dep| try_cache dep }
|
|
63
67
|
@uncached_gemspecs.uniq!
|
|
64
68
|
end
|
|
65
69
|
|
|
66
|
-
# @return [Hash{String => Gem::Specification
|
|
67
|
-
def
|
|
68
|
-
@
|
|
70
|
+
# @return [Hash{String => Array<Gem::Specification>}]
|
|
71
|
+
def required_gems_map
|
|
72
|
+
@required_gems_map ||= requires.to_h { |path| [path, resolve_path_to_gemspecs(path)] }
|
|
69
73
|
end
|
|
70
74
|
|
|
71
75
|
# @return [Hash{String => Gem::Specification}]
|
|
@@ -112,6 +116,7 @@ module Solargraph
|
|
|
112
116
|
true
|
|
113
117
|
end
|
|
114
118
|
|
|
119
|
+
# @param gemspec [Gem::Specification]
|
|
115
120
|
def update_from_collection gemspec, gempins
|
|
116
121
|
return gempins unless @rbs_path && File.directory?(@rbs_path)
|
|
117
122
|
return gempins if RbsMap.new(gemspec.name, gemspec.version).resolved?
|
|
@@ -124,10 +129,26 @@ module Solargraph
|
|
|
124
129
|
end
|
|
125
130
|
|
|
126
131
|
# @param path [String]
|
|
127
|
-
# @return [Gem::Specification
|
|
128
|
-
def
|
|
132
|
+
# @return [::Array<Gem::Specification>, nil]
|
|
133
|
+
def resolve_path_to_gemspecs path
|
|
129
134
|
return nil if path.empty?
|
|
130
135
|
|
|
136
|
+
if path == 'bundler/require'
|
|
137
|
+
# find only the gems bundler is now using
|
|
138
|
+
gemspecs = Bundler.definition.locked_gems.specs.flat_map do |lazy_spec|
|
|
139
|
+
logger.info "Handling #{lazy_spec.name}:#{lazy_spec.version} from #{path}"
|
|
140
|
+
[Gem::Specification.find_by_name(lazy_spec.name, lazy_spec.version)]
|
|
141
|
+
rescue Gem::MissingSpecError => e
|
|
142
|
+
logger.info("Could not find #{lazy_spec.name}:#{lazy_spec.version} with find_by_name, falling back to guess")
|
|
143
|
+
# can happen in local filesystem references
|
|
144
|
+
specs = resolve_path_to_gemspecs lazy_spec.name
|
|
145
|
+
logger.info "Gem #{lazy_spec.name} #{lazy_spec.version} from bundle not found: #{e}" if specs.nil?
|
|
146
|
+
next specs
|
|
147
|
+
end.compact
|
|
148
|
+
|
|
149
|
+
return gemspecs
|
|
150
|
+
end
|
|
151
|
+
|
|
131
152
|
gemspec = Gem::Specification.find_by_path(path)
|
|
132
153
|
if gemspec.nil?
|
|
133
154
|
gem_name_guess = path.split('/').first
|
|
@@ -141,16 +162,17 @@ module Solargraph
|
|
|
141
162
|
gemspec = potential_gemspec if potential_gemspec.files.any? { |gemspec_file| file == gemspec_file }
|
|
142
163
|
rescue Gem::MissingSpecError
|
|
143
164
|
Solargraph.logger.debug "Require path #{path} could not be resolved to a gem via find_by_path or guess of #{gem_name_guess}"
|
|
144
|
-
|
|
165
|
+
[]
|
|
145
166
|
end
|
|
146
167
|
end
|
|
147
|
-
|
|
168
|
+
return nil if gemspec.nil?
|
|
169
|
+
[gemspec_or_preference(gemspec)]
|
|
148
170
|
end
|
|
149
171
|
|
|
150
|
-
# @param gemspec [Gem::Specification
|
|
151
|
-
# @return [Gem::Specification
|
|
172
|
+
# @param gemspec [Gem::Specification]
|
|
173
|
+
# @return [Gem::Specification]
|
|
152
174
|
def gemspec_or_preference gemspec
|
|
153
|
-
return gemspec unless
|
|
175
|
+
return gemspec unless preference_map.key?(gemspec.name)
|
|
154
176
|
return gemspec if gemspec.version == preference_map[gemspec.name].version
|
|
155
177
|
|
|
156
178
|
change_gemspec_version gemspec, preference_map[by_path.name].version
|
|
@@ -169,12 +191,15 @@ module Solargraph
|
|
|
169
191
|
# @param gemspec [Gem::Specification]
|
|
170
192
|
# @return [Array<Gem::Specification>]
|
|
171
193
|
def fetch_dependencies gemspec
|
|
194
|
+
# @param spec [Gem::Dependency]
|
|
172
195
|
only_runtime_dependencies(gemspec).each_with_object(Set.new) do |spec, deps|
|
|
173
196
|
Solargraph.logger.info "Adding #{spec.name} dependency for #{gemspec.name}"
|
|
174
|
-
dep = Gem
|
|
197
|
+
dep = Gem.loaded_specs[spec.name]
|
|
198
|
+
# @todo is next line necessary?
|
|
199
|
+
dep ||= Gem::Specification.find_by_name(spec.name, spec.requirement)
|
|
175
200
|
deps.merge fetch_dependencies(dep) if deps.add?(dep)
|
|
176
201
|
rescue Gem::MissingSpecError
|
|
177
|
-
Solargraph.logger.warn "Gem dependency #{spec.name} #{spec.
|
|
202
|
+
Solargraph.logger.warn "Gem dependency #{spec.name} #{spec.requirement} for #{gemspec.name} not found."
|
|
178
203
|
end.to_a
|
|
179
204
|
end
|
|
180
205
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
# @abstract This mixin relies on these -
|
|
5
|
+
# methods:
|
|
6
|
+
# equality_fields()
|
|
7
|
+
module Equality
|
|
8
|
+
# @!method equality_fields
|
|
9
|
+
# @return [Array]
|
|
10
|
+
|
|
11
|
+
# @param other [Object]
|
|
12
|
+
# @return [Boolean]
|
|
13
|
+
def eql?(other)
|
|
14
|
+
self.class.eql?(other.class) &&
|
|
15
|
+
equality_fields.eql?(other.equality_fields)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @param other [Object]
|
|
19
|
+
# @return [Boolean]
|
|
20
|
+
def ==(other)
|
|
21
|
+
self.eql?(other)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def hash
|
|
25
|
+
equality_fields.hash
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def freeze
|
|
29
|
+
equality_fields.each(&:freeze)
|
|
30
|
+
super
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -3,10 +3,22 @@
|
|
|
3
3
|
module Solargraph
|
|
4
4
|
module LanguageServer
|
|
5
5
|
class Host
|
|
6
|
-
# A serial worker Thread to handle
|
|
6
|
+
# A serial worker Thread to handle incoming messages.
|
|
7
7
|
#
|
|
8
|
-
# this make check pending message possible, and maybe cancelled to speedup process
|
|
9
8
|
class MessageWorker
|
|
9
|
+
UPDATE_METHODS = [
|
|
10
|
+
'textDocument/didChange',
|
|
11
|
+
'textDocument/didClose',
|
|
12
|
+
'textDocument/didOpen',
|
|
13
|
+
'textDocument/didSave',
|
|
14
|
+
'workspace/didChangeConfiguration',
|
|
15
|
+
'workspace/didChangeWatchedFiles',
|
|
16
|
+
'workspace/didCreateFiles',
|
|
17
|
+
'workspace/didChangeWorkspaceFolders',
|
|
18
|
+
'workspace/didDeleteFiles',
|
|
19
|
+
'workspace/didRenameFiles'
|
|
20
|
+
].freeze
|
|
21
|
+
|
|
10
22
|
# @param host [Host]
|
|
11
23
|
def initialize(host)
|
|
12
24
|
@host = host
|
|
@@ -30,7 +42,7 @@ module Solargraph
|
|
|
30
42
|
@stopped = true
|
|
31
43
|
end
|
|
32
44
|
|
|
33
|
-
# @param message [Hash] The message
|
|
45
|
+
# @param message [Hash] The message to handle. Will be forwarded to Host#receive
|
|
34
46
|
# @return [void]
|
|
35
47
|
def queue(message)
|
|
36
48
|
@mutex.synchronize do
|
|
@@ -52,10 +64,44 @@ module Solargraph
|
|
|
52
64
|
def tick
|
|
53
65
|
message = @mutex.synchronize do
|
|
54
66
|
@resource.wait(@mutex) if messages.empty?
|
|
55
|
-
|
|
67
|
+
next_message
|
|
56
68
|
end
|
|
57
69
|
handler = @host.receive(message)
|
|
58
|
-
handler
|
|
70
|
+
handler&.send_response
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def next_message
|
|
76
|
+
cancel_message || next_priority
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def cancel_message
|
|
80
|
+
# Handle cancellations first
|
|
81
|
+
idx = messages.find_index { |msg| msg['method'] == '$/cancelRequest' }
|
|
82
|
+
return unless idx
|
|
83
|
+
|
|
84
|
+
msg = messages[idx]
|
|
85
|
+
messages.delete_at idx
|
|
86
|
+
msg
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def next_priority
|
|
90
|
+
# Prioritize updates and version-dependent messages for performance
|
|
91
|
+
idx = messages.find_index do |msg|
|
|
92
|
+
UPDATE_METHODS.include?(msg['method']) || version_dependent?(msg)
|
|
93
|
+
end
|
|
94
|
+
idx ? messages.delete_at(idx) : messages.shift
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# True if the message requires a previous update to have executed in
|
|
98
|
+
# order to work correctly.
|
|
99
|
+
#
|
|
100
|
+
# @param msg [Hash{String => Object}]
|
|
101
|
+
# @todo need boolish type from RBS
|
|
102
|
+
# @return [Object]
|
|
103
|
+
def version_dependent? msg
|
|
104
|
+
msg['textDocument'] && msg['position']
|
|
59
105
|
end
|
|
60
106
|
end
|
|
61
107
|
end
|
|
@@ -24,10 +24,8 @@ module Solargraph
|
|
|
24
24
|
attr_writer :client_capabilities
|
|
25
25
|
|
|
26
26
|
def initialize
|
|
27
|
-
@cancel_semaphore = Mutex.new
|
|
28
27
|
@buffer_semaphore = Mutex.new
|
|
29
28
|
@request_mutex = Mutex.new
|
|
30
|
-
@cancel = []
|
|
31
29
|
@buffer = String.new
|
|
32
30
|
@stopped = true
|
|
33
31
|
@next_request_id = 1
|
|
@@ -65,7 +63,7 @@ module Solargraph
|
|
|
65
63
|
# @param id [Integer]
|
|
66
64
|
# @return [void]
|
|
67
65
|
def cancel id
|
|
68
|
-
|
|
66
|
+
cancelled.push id
|
|
69
67
|
end
|
|
70
68
|
|
|
71
69
|
# True if the host received a request to cancel the method with the
|
|
@@ -74,9 +72,7 @@ module Solargraph
|
|
|
74
72
|
# @param id [Integer]
|
|
75
73
|
# @return [Boolean]
|
|
76
74
|
def cancel? id
|
|
77
|
-
|
|
78
|
-
@cancel_semaphore.synchronize { result = @cancel.include? id }
|
|
79
|
-
result
|
|
75
|
+
cancelled.include? id
|
|
80
76
|
end
|
|
81
77
|
|
|
82
78
|
# Delete the specified ID from the list of cancelled IDs if it exists.
|
|
@@ -84,7 +80,7 @@ module Solargraph
|
|
|
84
80
|
# @param id [Integer]
|
|
85
81
|
# @return [void]
|
|
86
82
|
def clear id
|
|
87
|
-
|
|
83
|
+
cancelled.delete id
|
|
88
84
|
end
|
|
89
85
|
|
|
90
86
|
# Called by adapter, to handle the request
|
|
@@ -101,11 +97,11 @@ module Solargraph
|
|
|
101
97
|
# @return [Solargraph::LanguageServer::Message::Base, nil] The message handler.
|
|
102
98
|
def receive request
|
|
103
99
|
if request['method']
|
|
104
|
-
logger.info "
|
|
100
|
+
logger.info "Host received ##{request['id']} #{request['method']}"
|
|
105
101
|
logger.debug request
|
|
106
102
|
message = Message.select(request['method']).new(self, request)
|
|
107
103
|
begin
|
|
108
|
-
message.process
|
|
104
|
+
message.process unless cancel?(request['id'])
|
|
109
105
|
rescue StandardError => e
|
|
110
106
|
logger.warn "Error processing request: [#{e.class}] #{e.message}"
|
|
111
107
|
logger.warn e.backtrace.join("\n")
|
|
@@ -381,7 +377,6 @@ module Solargraph
|
|
|
381
377
|
envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
|
|
382
378
|
queue envelope
|
|
383
379
|
@next_request_id += 1
|
|
384
|
-
logger.info "Server sent #{method}"
|
|
385
380
|
logger.debug params
|
|
386
381
|
end
|
|
387
382
|
end
|
|
@@ -476,6 +471,7 @@ module Solargraph
|
|
|
476
471
|
def locate_pins params
|
|
477
472
|
return [] unless params['data'] && params['data']['uri']
|
|
478
473
|
library = library_for(params['data']['uri'])
|
|
474
|
+
# @type [Array<Pin::Base>]
|
|
479
475
|
result = []
|
|
480
476
|
if params['data']['location']
|
|
481
477
|
location = Location.new(
|
|
@@ -575,7 +571,7 @@ module Solargraph
|
|
|
575
571
|
# @param column [Integer]
|
|
576
572
|
# @param strip [Boolean] Strip special characters from variable names
|
|
577
573
|
# @param only [Boolean] If true, search current file only
|
|
578
|
-
# @return [Array<Solargraph::
|
|
574
|
+
# @return [Array<Solargraph::Location>]
|
|
579
575
|
def references_from uri, line, column, strip: true, only: false
|
|
580
576
|
library = library_for(uri)
|
|
581
577
|
library.references_from(uri_to_file(uri), line, column, strip: strip, only: only)
|
|
@@ -693,6 +689,11 @@ module Solargraph
|
|
|
693
689
|
|
|
694
690
|
private
|
|
695
691
|
|
|
692
|
+
# @return [Array<Integer>]
|
|
693
|
+
def cancelled
|
|
694
|
+
@cancelled ||= []
|
|
695
|
+
end
|
|
696
|
+
|
|
696
697
|
# @return [MessageWorker]
|
|
697
698
|
def message_worker
|
|
698
699
|
@message_worker ||= MessageWorker.new(self)
|
|
@@ -748,6 +749,7 @@ module Solargraph
|
|
|
748
749
|
return change if source.code.length + 1 != change['text'].length
|
|
749
750
|
diffs = Diff::LCS.diff(source.code, change['text'])
|
|
750
751
|
return change if diffs.length.zero? || diffs.length > 1 || diffs.first.length > 1
|
|
752
|
+
# @sg-ignore push this upstream
|
|
751
753
|
# @type [Diff::LCS::Change]
|
|
752
754
|
diff = diffs.first.first
|
|
753
755
|
return change unless diff.adding? && ['.', ':', '(', ',', ' '].include?(diff.element)
|
|
@@ -16,7 +16,7 @@ module Solargraph
|
|
|
16
16
|
# @return [String]
|
|
17
17
|
attr_reader :method
|
|
18
18
|
|
|
19
|
-
# @return [Hash{String => Array
|
|
19
|
+
# @return [Hash{String => Array<undefined>, Hash{String => undefined}, String, Integer}]
|
|
20
20
|
attr_reader :params
|
|
21
21
|
|
|
22
22
|
# @return [Hash, Array, nil]
|
|
@@ -61,18 +61,11 @@ module Solargraph
|
|
|
61
61
|
# @return [void]
|
|
62
62
|
def send_response
|
|
63
63
|
return if id.nil?
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
# cancel should send response RequestCancelled
|
|
67
|
-
Solargraph::Logging.logger.info "Cancelled response to #{method}"
|
|
68
|
-
set_result nil
|
|
69
|
-
set_error ErrorCodes::REQUEST_CANCELLED, "cancelled by client"
|
|
70
|
-
else
|
|
71
|
-
Solargraph::Logging.logger.info "Sending response to #{method}"
|
|
72
|
-
end
|
|
64
|
+
|
|
65
|
+
accept_or_cancel
|
|
73
66
|
response = {
|
|
74
|
-
jsonrpc:
|
|
75
|
-
id: id
|
|
67
|
+
jsonrpc: '2.0',
|
|
68
|
+
id: id
|
|
76
69
|
}
|
|
77
70
|
response[:result] = result unless result.nil?
|
|
78
71
|
response[:error] = error unless error.nil?
|
|
@@ -83,6 +76,20 @@ module Solargraph
|
|
|
83
76
|
host.queue envelope
|
|
84
77
|
host.clear id
|
|
85
78
|
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def accept_or_cancel
|
|
83
|
+
if host.cancel?(id)
|
|
84
|
+
# https://microsoft.github.io/language-server-protocol/specifications/specification-current/#cancelRequest
|
|
85
|
+
# cancel should send response RequestCancelled
|
|
86
|
+
Solargraph::Logging.logger.info "Cancelled response to ##{id} #{method}"
|
|
87
|
+
set_result nil
|
|
88
|
+
set_error ErrorCodes::REQUEST_CANCELLED, 'Cancelled by client'
|
|
89
|
+
else
|
|
90
|
+
Solargraph::Logging.logger.info "Sending response to ##{id} #{method}"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
86
93
|
end
|
|
87
94
|
end
|
|
88
95
|
end
|
|
@@ -174,7 +174,9 @@ module Solargraph
|
|
|
174
174
|
|
|
175
175
|
# @param section [String]
|
|
176
176
|
# @param capability [String]
|
|
177
|
-
# @
|
|
177
|
+
# @todo Need support for RBS' boolish "type", which doesn't
|
|
178
|
+
# enforce strict true/false-ness
|
|
179
|
+
# @sg-ignore
|
|
178
180
|
def dynamic_registration_for? section, capability
|
|
179
181
|
result = (params['capabilities'] &&
|
|
180
182
|
params['capabilities'][section] &&
|
|
@@ -12,9 +12,6 @@ module Solargraph
|
|
|
12
12
|
col = params['position']['character']
|
|
13
13
|
begin
|
|
14
14
|
completion = host.completions_at(params['textDocument']['uri'], line, col)
|
|
15
|
-
if host.cancel?(id)
|
|
16
|
-
return set_result(empty_result) if host.cancel?(id)
|
|
17
|
-
end
|
|
18
15
|
items = []
|
|
19
16
|
last_context = nil
|
|
20
17
|
idx = -1
|
|
@@ -13,10 +13,10 @@ module Solargraph::LanguageServer::Message::TextDocument
|
|
|
13
13
|
def code_location
|
|
14
14
|
suggestions = host.definitions_at(params['textDocument']['uri'], @line, @column)
|
|
15
15
|
return nil if suggestions.empty?
|
|
16
|
-
suggestions.reject { |pin| pin.
|
|
16
|
+
suggestions.reject { |pin| pin.best_location.nil? || pin.best_location.filename.nil? }.map do |pin|
|
|
17
17
|
{
|
|
18
|
-
uri: file_to_uri(pin.
|
|
19
|
-
range: pin.
|
|
18
|
+
uri: file_to_uri(pin.best_location.filename),
|
|
19
|
+
range: pin.best_location.range.to_hash
|
|
20
20
|
}
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -6,15 +6,15 @@ class Solargraph::LanguageServer::Message::TextDocument::DocumentSymbol < Solarg
|
|
|
6
6
|
def process
|
|
7
7
|
pins = host.document_symbols params['textDocument']['uri']
|
|
8
8
|
info = pins.map do |pin|
|
|
9
|
-
next nil unless pin.
|
|
9
|
+
next nil unless pin.best_location&.filename
|
|
10
10
|
|
|
11
11
|
result = {
|
|
12
12
|
name: pin.name,
|
|
13
13
|
containerName: pin.namespace,
|
|
14
14
|
kind: pin.symbol_kind,
|
|
15
15
|
location: {
|
|
16
|
-
uri: file_to_uri(pin.
|
|
17
|
-
range: pin.
|
|
16
|
+
uri: file_to_uri(pin.best_location.filename),
|
|
17
|
+
range: pin.best_location.range.to_hash
|
|
18
18
|
},
|
|
19
19
|
deprecated: pin.deprecated?
|
|
20
20
|
}
|
|
@@ -33,6 +33,7 @@ module Solargraph
|
|
|
33
33
|
|
|
34
34
|
private
|
|
35
35
|
|
|
36
|
+
# @param corrections [String]
|
|
36
37
|
def log_corrections(corrections)
|
|
37
38
|
corrections = corrections&.strip
|
|
38
39
|
return if corrections&.empty?
|
|
@@ -51,6 +52,7 @@ module Solargraph
|
|
|
51
52
|
conf['rubocop'] || {}
|
|
52
53
|
end
|
|
53
54
|
|
|
55
|
+
# @param config [Hash{String => String}]
|
|
54
56
|
def cli_args file_uri, config
|
|
55
57
|
file = UriHelpers.uri_to_file(file_uri)
|
|
56
58
|
args = [
|
|
@@ -68,6 +70,7 @@ module Solargraph
|
|
|
68
70
|
args + [file]
|
|
69
71
|
end
|
|
70
72
|
|
|
73
|
+
# @param config [Hash{String => String}]
|
|
71
74
|
def formatter_class(config)
|
|
72
75
|
if self.class.const_defined?('BlankRubocopFormatter')
|
|
73
76
|
# @sg-ignore
|
|
@@ -79,6 +82,7 @@ module Solargraph
|
|
|
79
82
|
end
|
|
80
83
|
end
|
|
81
84
|
|
|
85
|
+
# @param value [Array, String]
|
|
82
86
|
def cop_list(value)
|
|
83
87
|
value = value.join(',') if value.respond_to?(:join)
|
|
84
88
|
return nil if value == '' || !value.is_a?(String)
|
|
@@ -21,7 +21,7 @@ module Solargraph
|
|
|
21
21
|
parts.push pin.documentation unless pin.documentation.nil? || pin.documentation.empty?
|
|
22
22
|
unless parts.empty?
|
|
23
23
|
data = parts.join("\n\n")
|
|
24
|
-
next if contents.last
|
|
24
|
+
next if contents.last&.end_with?(data)
|
|
25
25
|
contents.push data
|
|
26
26
|
end
|
|
27
27
|
last_link = this_link unless this_link.nil?
|
|
@@ -13,10 +13,10 @@ module Solargraph::LanguageServer::Message::TextDocument
|
|
|
13
13
|
def code_location
|
|
14
14
|
suggestions = host.type_definitions_at(params['textDocument']['uri'], @line, @column)
|
|
15
15
|
return nil if suggestions.empty?
|
|
16
|
-
suggestions.reject { |pin| pin.
|
|
16
|
+
suggestions.reject { |pin| pin.best_location.nil? || pin.best_location.filename.nil? }.map do |pin|
|
|
17
17
|
{
|
|
18
|
-
uri: file_to_uri(pin.
|
|
19
|
-
range: pin.
|
|
18
|
+
uri: file_to_uri(pin.best_location.filename),
|
|
19
|
+
range: pin.best_location.range.to_hash
|
|
20
20
|
}
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -6,14 +6,14 @@ class Solargraph::LanguageServer::Message::Workspace::WorkspaceSymbol < Solargra
|
|
|
6
6
|
def process
|
|
7
7
|
pins = host.query_symbols(params['query'])
|
|
8
8
|
info = pins.map do |pin|
|
|
9
|
-
uri = file_to_uri(pin.
|
|
9
|
+
uri = file_to_uri(pin.best_location.filename)
|
|
10
10
|
{
|
|
11
11
|
name: pin.path,
|
|
12
12
|
containerName: pin.namespace,
|
|
13
13
|
kind: pin.symbol_kind,
|
|
14
14
|
location: {
|
|
15
15
|
uri: uri,
|
|
16
|
-
range: pin.
|
|
16
|
+
range: pin.best_location.range.to_hash
|
|
17
17
|
},
|
|
18
18
|
deprecated: pin.deprecated?
|
|
19
19
|
}
|
|
@@ -66,10 +66,10 @@ module Solargraph
|
|
|
66
66
|
return unless host.client_supports_progress? && !finished?
|
|
67
67
|
|
|
68
68
|
message = build
|
|
69
|
-
|
|
70
|
-
create(host) unless created?
|
|
69
|
+
create(host)
|
|
71
70
|
host.send_notification '$/progress', message
|
|
72
71
|
@status = FINISHED if kind == 'end'
|
|
72
|
+
keep_alive host
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def created?
|
|
@@ -113,6 +113,23 @@ module Solargraph
|
|
|
113
113
|
raise "Invalid progress kind #{kind}"
|
|
114
114
|
end
|
|
115
115
|
end
|
|
116
|
+
|
|
117
|
+
# @param host [Host]
|
|
118
|
+
def keep_alive host
|
|
119
|
+
mutex.synchronize { @last = Time.now }
|
|
120
|
+
@keep_alive ||= Thread.new do
|
|
121
|
+
until finished?
|
|
122
|
+
sleep 10
|
|
123
|
+
break if finished?
|
|
124
|
+
next if mutex.synchronize { Time.now - @last < 10 }
|
|
125
|
+
send host
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def mutex
|
|
131
|
+
@mutex ||= Mutex.new
|
|
132
|
+
end
|
|
116
133
|
end
|
|
117
134
|
end
|
|
118
135
|
end
|