solargraph 0.54.1 → 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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/lib/solargraph/api_map/cache.rb +10 -1
  4. data/lib/solargraph/api_map/index.rb +167 -0
  5. data/lib/solargraph/api_map/store.rb +71 -122
  6. data/lib/solargraph/api_map.rb +97 -36
  7. data/lib/solargraph/bench.rb +17 -1
  8. data/lib/solargraph/complex_type/type_methods.rb +7 -0
  9. data/lib/solargraph/complex_type/unique_type.rb +109 -8
  10. data/lib/solargraph/complex_type.rb +48 -18
  11. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +51 -0
  12. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
  13. data/lib/solargraph/convention/struct_definition.rb +101 -0
  14. data/lib/solargraph/convention.rb +2 -0
  15. data/lib/solargraph/doc_map.rb +43 -18
  16. data/lib/solargraph/equality.rb +33 -0
  17. data/lib/solargraph/language_server/host/message_worker.rb +31 -11
  18. data/lib/solargraph/language_server/host.rb +13 -11
  19. data/lib/solargraph/language_server/message/base.rb +19 -12
  20. data/lib/solargraph/language_server/message/initialize.rb +3 -1
  21. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  22. data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -0
  23. data/lib/solargraph/library.rb +7 -8
  24. data/lib/solargraph/location.rb +15 -0
  25. data/lib/solargraph/parser/comment_ripper.rb +11 -6
  26. data/lib/solargraph/parser/flow_sensitive_typing.rb +226 -0
  27. data/lib/solargraph/parser/node_methods.rb +15 -1
  28. data/lib/solargraph/parser/parser_gem/class_methods.rb +11 -6
  29. data/lib/solargraph/parser/parser_gem/node_chainer.rb +17 -23
  30. data/lib/solargraph/parser/parser_gem/node_methods.rb +5 -3
  31. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  32. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +21 -1
  33. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
  34. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -2
  35. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +26 -5
  36. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
  37. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +28 -0
  38. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +28 -0
  39. data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
  40. data/lib/solargraph/parser.rb +1 -0
  41. data/lib/solargraph/pin/base.rb +35 -19
  42. data/lib/solargraph/pin/base_variable.rb +10 -3
  43. data/lib/solargraph/pin/block.rb +3 -3
  44. data/lib/solargraph/pin/breakable.rb +9 -0
  45. data/lib/solargraph/pin/callable.rb +3 -3
  46. data/lib/solargraph/pin/local_variable.rb +7 -1
  47. data/lib/solargraph/pin/method.rb +26 -18
  48. data/lib/solargraph/pin/namespace.rb +10 -7
  49. data/lib/solargraph/pin/parameter.rb +15 -6
  50. data/lib/solargraph/pin/proxy_type.rb +12 -6
  51. data/lib/solargraph/pin/until.rb +18 -0
  52. data/lib/solargraph/pin/while.rb +18 -0
  53. data/lib/solargraph/pin.rb +3 -0
  54. data/lib/solargraph/position.rb +7 -0
  55. data/lib/solargraph/range.rb +7 -0
  56. data/lib/solargraph/rbs_map/conversions.rb +16 -10
  57. data/lib/solargraph/rbs_map/core_fills.rb +10 -3
  58. data/lib/solargraph/rbs_map.rb +1 -0
  59. data/lib/solargraph/shell.rb +2 -0
  60. data/lib/solargraph/source/chain/array.rb +5 -4
  61. data/lib/solargraph/source/chain/call.rb +55 -19
  62. data/lib/solargraph/source/chain/constant.rb +1 -1
  63. data/lib/solargraph/source/chain/hash.rb +8 -2
  64. data/lib/solargraph/source/chain/if.rb +5 -0
  65. data/lib/solargraph/source/chain/link.rb +19 -5
  66. data/lib/solargraph/source/chain/literal.rb +27 -2
  67. data/lib/solargraph/source/chain/z_super.rb +1 -1
  68. data/lib/solargraph/source/chain.rb +107 -63
  69. data/lib/solargraph/source/cursor.rb +1 -11
  70. data/lib/solargraph/source/source_chainer.rb +2 -2
  71. data/lib/solargraph/source.rb +2 -1
  72. data/lib/solargraph/source_map/clip.rb +4 -2
  73. data/lib/solargraph/type_checker/checks.rb +4 -0
  74. data/lib/solargraph/type_checker.rb +41 -14
  75. data/lib/solargraph/version.rb +1 -1
  76. data/lib/solargraph/workspace/config.rb +7 -3
  77. data/lib/solargraph/workspace.rb +1 -1
  78. data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -0
  79. data/lib/solargraph/yard_map/mapper/to_method.rb +42 -15
  80. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
  81. data/lib/solargraph/yard_map/mapper.rb +1 -0
  82. data/lib/solargraph/yardoc.rb +1 -1
  83. data/lib/solargraph.rb +1 -0
  84. data/solargraph.gemspec +5 -5
  85. metadata +39 -25
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Convention
5
+ module StructDefinition
6
+ # A node wrapper for a Struct definition via inheritance.
7
+ # @example
8
+ # class MyStruct < Struct.new(:bar, :baz)
9
+ # def foo
10
+ # end
11
+ # end
12
+ class StructDefintionNode
13
+ class << self
14
+ # @example
15
+ # s(:class,
16
+ # s(:const, nil, :Foo),
17
+ # s(:send,
18
+ # s(:const, nil, :Struct), :new,
19
+ # s(:sym, :bar),
20
+ # s(:sym, :baz)),
21
+ # s(:hash,
22
+ # s(:pair,
23
+ # s(:sym, :keyword_init),
24
+ # s(:true)))),
25
+ # s(:def, :foo,
26
+ # s(:args),
27
+ # s(:send, nil, :bar)))
28
+ def valid?(node)
29
+ return false unless node&.type == :class
30
+
31
+ struct_definition_node?(node.children[1])
32
+ end
33
+
34
+ private
35
+
36
+ # @param struct_node [Parser::AST::Node]
37
+ # @return [Boolean]
38
+ def struct_definition_node?(struct_node)
39
+ return false unless struct_node.is_a?(::Parser::AST::Node)
40
+ return false unless struct_node&.type == :send
41
+ return false unless struct_node.children[0]&.type == :const
42
+ return false unless struct_node.children[0].children[1] == :Struct
43
+ return false unless struct_node.children[1] == :new
44
+
45
+ true
46
+ end
47
+ end
48
+
49
+ # @return [Parser::AST::Node]
50
+ def initialize(node)
51
+ @node = node
52
+ end
53
+
54
+ # @return [String]
55
+ def class_name
56
+ Parser::NodeMethods.unpack_name(node)
57
+ end
58
+
59
+ # @return [Array<Array(Parser::AST::Node, String)>]
60
+ def attributes
61
+ struct_attribute_nodes.map do |struct_def_param|
62
+ next unless struct_def_param.type == :sym
63
+ [struct_def_param, struct_def_param.children[0].to_s]
64
+ end.compact
65
+ end
66
+
67
+ def keyword_init?
68
+ keyword_init_param = struct_attribute_nodes.find do |struct_def_param|
69
+ struct_def_param.type == :hash && struct_def_param.children[0].type == :pair &&
70
+ struct_def_param.children[0].children[0].children[0] == :keyword_init
71
+ end
72
+
73
+ return false if keyword_init_param.nil?
74
+
75
+ keyword_init_param.children[0].children[1].type == :true
76
+ end
77
+
78
+ # @return [Parser::AST::Node]
79
+ def body_node
80
+ node.children[2]
81
+ end
82
+
83
+ private
84
+
85
+ # @return [Parser::AST::Node]
86
+ attr_reader :node
87
+
88
+ # @return [Parser::AST::Node]
89
+ def struct_node
90
+ node.children[1]
91
+ end
92
+
93
+ # @return [Array<Parser::AST::Node>]
94
+ def struct_attribute_nodes
95
+ struct_node.children[2..-1]
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -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>]
@@ -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 ||= required_gem_map.values.compact
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 ||= required_gem_map.select { |_, gemspec| gemspec.nil? }.keys
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
- required_gem_map.each do |path, gemspec|
56
- if gemspec
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, nil}]
67
- def required_gem_map
68
- @required_gem_map ||= requires.to_h { |path| [path, resolve_path_to_gemspec(path)] }
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, nil]
128
- def resolve_path_to_gemspec path
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
- nil
165
+ []
145
166
  end
146
167
  end
147
- gemspec_or_preference gemspec
168
+ return nil if gemspec.nil?
169
+ [gemspec_or_preference(gemspec)]
148
170
  end
149
171
 
150
- # @param gemspec [Gem::Specification, nil]
151
- # @return [Gem::Specification, nil]
172
+ # @param gemspec [Gem::Specification]
173
+ # @return [Gem::Specification]
152
174
  def gemspec_or_preference gemspec
153
- return gemspec unless gemspec && preference_map.key?(gemspec.name)
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::Specification.find_by_name(spec.name, spec.requirement)
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.requirements} for #{gemspec.name} not found."
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,12 +3,21 @@
3
3
  module Solargraph
4
4
  module LanguageServer
5
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
6
+ # A serial worker Thread to handle incoming messages.
9
7
  #
10
8
  class MessageWorker
11
- UPDATE_METHODS = ['textDocument/didOpen', 'textDocument/didChange', 'workspace/didChangeWatchedFiles'].freeze
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
12
21
 
13
22
  # @param host [Host]
14
23
  def initialize(host)
@@ -64,22 +73,33 @@ module Solargraph
64
73
  private
65
74
 
66
75
  def next_message
67
- # Prioritize updates and version-dependent messages for performance
68
- idx = messages.find_index do |msg|
69
- UPDATE_METHODS.include?(msg['method']) || version_dependent?(msg)
70
- end
71
- # @todo We might want to clear duplicate instances of this message
72
- # that occur before the next update
73
- return messages.shift unless idx
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
74
83
 
75
84
  msg = messages[idx]
76
85
  messages.delete_at idx
77
86
  msg
78
87
  end
79
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
+
80
97
  # True if the message requires a previous update to have executed in
81
98
  # order to work correctly.
82
99
  #
100
+ # @param msg [Hash{String => Object}]
101
+ # @todo need boolish type from RBS
102
+ # @return [Object]
83
103
  def version_dependent? msg
84
104
  msg['textDocument'] && msg['position']
85
105
  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
- @cancel_semaphore.synchronize { @cancel.push id }
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
- result = false
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
- @cancel_semaphore.synchronize { @cancel.delete id }
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 "Server received #{request['method']}"
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::Range>]
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, Hash, String, Integer}]
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
- if host.cancel?(id)
65
- # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#cancelRequest
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: "2.0",
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
- # @return [Boolean]
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
@@ -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)