solargraph 0.42.1 → 0.43.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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/SPONSORS.md +0 -1
  4. data/lib/solargraph/api_map.rb +1 -1
  5. data/lib/solargraph/api_map/bundler_methods.rb +1 -6
  6. data/lib/solargraph/api_map/store.rb +17 -17
  7. data/lib/solargraph/bench.rb +1 -1
  8. data/lib/solargraph/complex_type.rb +4 -0
  9. data/lib/solargraph/complex_type/type_methods.rb +6 -3
  10. data/lib/solargraph/language_server/host.rb +2 -1
  11. data/lib/solargraph/language_server/host/dispatch.rb +2 -3
  12. data/lib/solargraph/language_server/transport/adapter.rb +1 -3
  13. data/lib/solargraph/library.rb +12 -5
  14. data/lib/solargraph/parser/legacy/node_chainer.rb +14 -1
  15. data/lib/solargraph/parser/legacy/node_methods.rb +9 -2
  16. data/lib/solargraph/parser/node_processor/base.rb +0 -3
  17. data/lib/solargraph/parser/rubyvm/node_chainer.rb +22 -1
  18. data/lib/solargraph/parser/rubyvm/node_methods.rb +5 -0
  19. data/lib/solargraph/pin/base.rb +1 -1
  20. data/lib/solargraph/pin/local_variable.rb +47 -1
  21. data/lib/solargraph/shell.rb +2 -2
  22. data/lib/solargraph/source.rb +0 -12
  23. data/lib/solargraph/source/chain.rb +1 -4
  24. data/lib/solargraph/source/chain/call.rb +3 -2
  25. data/lib/solargraph/source/chain/hash.rb +28 -0
  26. data/lib/solargraph/source_map.rb +2 -1
  27. data/lib/solargraph/source_map/clip.rb +1 -1
  28. data/lib/solargraph/type_checker.rb +11 -4
  29. data/lib/solargraph/version.rb +1 -1
  30. data/lib/solargraph/workspace.rb +1 -1
  31. data/lib/solargraph/yard_map.rb +16 -14
  32. data/lib/solargraph/yard_map/core_fills.rb +18 -1
  33. data/lib/yard-solargraph.rb +3 -0
  34. data/solargraph.gemspec +1 -1
  35. metadata +9 -9
  36. data/lib/solargraph/pin/localized.rb +0 -43
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1099281670bd5cacd480624f66b36d7ce8fb6b69b4486f080d0c0d6436c0f36c
4
- data.tar.gz: 4b1e028bc357204b953c31916845a7f12caefc07d733dc90cfb5bc142401f2d4
3
+ metadata.gz: 89a1a9d76094688ff7ebe1af3dec4452c379819408ffc458eddcd75f5b66d2eb
4
+ data.tar.gz: 9b03143ba896ccd2d3bd0a44c9a1e90f06e3ed757a47a08e6b8aaf2a9f56e62e
5
5
  SHA512:
6
- metadata.gz: 771c40ddff0a11757d1e9c4487802afc9f5b992840383f525643957d92516e735dc7328a8d23ed1ff4f0e55ceb09e3792eb24818758e4b217927bf4ba9708b5a
7
- data.tar.gz: 948101863fc7f3f167dee00139f07ccdf5711afd12125aa2a18b708ed4ba6edaffbc7ba0c1a7a7dd09ad51460a0827546841cf2dbcd223198247fd86d8b0841b
6
+ metadata.gz: a6d4e99416af96a8a11ca569d096f4ba1491155f3fde1efc734e59c73d1794007d0b7b1e936c1dfb248472df1fa679c7701866176de99119e8f6a0b8eef721d4
7
+ data.tar.gz: edd612d341e6c9bb421e4bbb8db04badc855e924eca469c47943c374813025810151aca1abdd0da13100597803799a50c7355a362b8a0ae65187c0760464e840
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## 0.43.0 - July 25, 2021
2
+ - Correct arity checks when restarg precedes arg (#418)
3
+ - Improve the performance of catalog by 4 times (#457)
4
+ - Type checker validates duck type variables and params (#453)
5
+ - Kernel#raise exception type checker
6
+ - Pin::Base#inspect includes path
7
+ - Fix arity with combined restargs and kwrestargs (#396)
8
+
9
+ ## 0.42.4 - July 11, 2021
10
+ - Yardoc cache handling
11
+ - Fix required_paths when gemspec is used (#451)
12
+ - fix: yard stdout may break language client (#454)
13
+
14
+ ## 0.42.3 - June 14, 2021
15
+ - Require 'pathname' for Library
16
+
17
+ ## 0.42.2 - June 14, 2021
18
+ - Improve download-core command output
19
+ - Ignore missing requests to client responses
20
+ - Add automatically required gems to YardMap
21
+ - Use closures to identify local variables
22
+
1
23
  ## 0.42.1 - June 11, 2021
2
24
  - YardMap#change sets new directory (#445)
3
25
 
data/SPONSORS.md CHANGED
@@ -13,4 +13,3 @@ The following people and organizations provide funding or other resources. [Beco
13
13
  - Emily Strickland
14
14
  - Tom de Grunt
15
15
  - Akira Yamada
16
- - Jared White
@@ -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 [Array<Solargraph::Pin::Keyword>]
141
+ # @return [Enumerable<Solargraph::Pin::Keyword>]
142
142
  def keyword_pins
143
143
  store.pins_by_class(Pin::Keyword)
144
144
  end
@@ -9,7 +9,7 @@ module Solargraph
9
9
  # @param directory [String]
10
10
  # @return [Hash]
11
11
  def require_from_bundle directory
12
- @require_from_bundle ||= begin
12
+ begin
13
13
  Solargraph.logger.info "Loading gems for bundler/require"
14
14
  Documentor.specs_from_bundle(directory)
15
15
  rescue BundleNotFoundError => e
@@ -17,11 +17,6 @@ module Solargraph
17
17
  {}
18
18
  end
19
19
  end
20
-
21
- # @return [void]
22
- def reset_require_from_bundle
23
- @require_from_bundle = nil
24
- end
25
20
  end
26
21
  end
27
22
  end
@@ -5,10 +5,10 @@ require 'set'
5
5
  module Solargraph
6
6
  class ApiMap
7
7
  class Store
8
- # @return [Array<Solargraph::Pin::Base>]
8
+ # @return [Enumerable<Solargraph::Pin::Base>]
9
9
  attr_reader :pins
10
10
 
11
- # @param pins [Array<Solargraph::Pin::Base>]
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 [Array<Solargraph::Pin::Base>]
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 [Array<Solargraph::Pin::Base>]
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 [Array<Solargraph::Pin::Base>]
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 [Array<Solargraph::Pin::Base>]
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 [Array<Solargraph::Pin::Base>]
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 [Array<Solargraph::Pin::Base>]
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 [Array<Solargraph::Pin::Base>]
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 [Array<Solargraph::Pin::Method>]
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 [Array<Pin::Block>]
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 [Array<Solargraph::Pin::Base>]
145
+ # @return [Enumerable<Solargraph::Pin::Base>]
146
146
  def pins_by_class klass
147
- @pin_select_cache[klass] ||= @pin_class_hash.select { |key, _| key <= klass }.values.flatten
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 [Array<Solargraph::Pin::Symbol>]
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 [Array<Solargraph::Pin::Base>]
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).transform_values(&:to_a)
220
- @path_pin_hash = set.classify(&:path).transform_values(&:to_a)
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] ||= []
@@ -3,7 +3,7 @@
3
3
  require 'set'
4
4
 
5
5
  module Solargraph
6
- # A container of source maps and gem specs to be cataloged in an ApiMap.
6
+ # A container of source maps and workspace data to be cataloged in an ApiMap.
7
7
  #
8
8
  class Bench
9
9
  # @return [Set<SourceMap>]
@@ -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?
@@ -72,9 +72,12 @@ module Solargraph
72
72
 
73
73
  # @return [String]
74
74
  def namespace
75
- @namespace ||= 'Object' if duck_type?
76
- @namespace ||= 'NilClass' if nil_type?
77
- @namespace ||= (name == 'Class' || name == 'Module') && !subtypes.empty? ? subtypes.first.name : name
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
@@ -31,7 +31,7 @@ module Solargraph
31
31
  @cancel = []
32
32
  @buffer = String.new
33
33
  @stopped = true
34
- @next_request_id = 0
34
+ @next_request_id = 1
35
35
  @dynamic_capabilities = Set.new
36
36
  @registered_capabilities = Set.new
37
37
  end
@@ -113,6 +113,7 @@ module Solargraph
113
113
  requests.delete request['id']
114
114
  else
115
115
  logger.warn "Discarding client response to unrecognized message #{request['id']}"
116
+ nil
116
117
  end
117
118
  else
118
119
  logger.warn "Invalid message received."
@@ -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
- result.attach sources.find(uri) if sources.include?(uri)
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
 
@@ -42,9 +42,7 @@ module Solargraph
42
42
  # @return [void]
43
43
  def process request
44
44
  message = @host.receive(request)
45
- message.send_response
46
- # tmp = @host.flush
47
- # write tmp unless tmp.empty?
45
+ message && message.send_response
48
46
  end
49
47
 
50
48
  def shutdown
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'pathname'
4
+
3
5
  module Solargraph
4
6
  # A Library handles coordination between a Workspace and an ApiMap.
5
7
  #
@@ -46,7 +48,7 @@ module Solargraph
46
48
  # @return [void]
47
49
  def attach source
48
50
  mutex.synchronize do
49
- if @current && @current.filename != source.filename && source_map_hash.key?(@current.filename) && !workspace.has_file?(@current.filename)
51
+ if @current && (!source || @current.filename != source.filename) && source_map_hash.key?(@current.filename) && !workspace.has_file?(@current.filename)
50
52
  source_map_hash.delete @current.filename
51
53
  source_map_external_require_hash.delete @current.filename
52
54
  @external_requires = nil
@@ -54,7 +56,7 @@ module Solargraph
54
56
  end
55
57
  @current = source
56
58
  maybe_map @current
57
- api_map.catalog bench unless synchronized?
59
+ catalog_inlock
58
60
  end
59
61
  end
60
62
 
@@ -349,7 +351,7 @@ module Solargraph
349
351
  else
350
352
  args = line.split(':').map(&:strip)
351
353
  name = args.shift
352
- reporter = Diagnostics.reporter(name)
354
+ reporter = Diagnostics.reporter(name)
353
355
  raise DiagnosticsError, "Diagnostics reporter #{name} does not exist" if reporter.nil?
354
356
  repargs[reporter] ||= []
355
357
  repargs[reporter].concat args
@@ -366,12 +368,16 @@ module Solargraph
366
368
  # @return [void]
367
369
  def catalog
368
370
  mutex.synchronize do
369
- break if synchronized?
371
+ catalog_inlock
372
+ end
373
+ end
374
+
375
+ private def catalog_inlock
376
+ return if synchronized?
370
377
  logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
371
378
  api_map.catalog bench
372
379
  @synchronized = true
373
380
  logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)" if logger.info?
374
- end
375
381
  end
376
382
 
377
383
  def bench
@@ -511,6 +517,7 @@ module Solargraph
511
517
  end
512
518
 
513
519
  def maybe_map source
520
+ return unless source
514
521
  if source_map_hash.key?(source.filename)
515
522
  return if source_map_hash[source.filename].code == source.code &&
516
523
  source_map_hash[source.filename].source.synchronized? &&
@@ -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
- result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
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) && node.type == :hash
101
- return convert_hash(node.children[0].children[0]) if splatted_hash?(node)
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)
@@ -4,9 +4,6 @@ module Solargraph
4
4
  module Parser
5
5
  module NodeProcessor
6
6
  class Base
7
- # @todo The base node processor should not include legacy node methods
8
- # include Legacy::NodeMethods
9
-
10
7
  # @return [Parser::AST::Node]
11
8
  attr_reader :node
12
9
 
@@ -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
- result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
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
@@ -218,7 +218,7 @@ module Solargraph
218
218
  end
219
219
 
220
220
  def inspect
221
- "#<#{self.class} at #{self.location.inspect}>"
221
+ "#<#{self.class} `#{self.path}` at #{self.location.inspect}>"
222
222
  end
223
223
 
224
224
  protected
@@ -3,7 +3,8 @@
3
3
  module Solargraph
4
4
  module Pin
5
5
  class LocalVariable < BaseVariable
6
- include Localized
6
+ # @return [Range]
7
+ attr_reader :presence
7
8
 
8
9
  def initialize assignment: nil, presence: nil, **splat
9
10
  super(**splat)
@@ -16,6 +17,51 @@ module Solargraph
16
17
  @presence = pin.presence
17
18
  true
18
19
  end
20
+
21
+ # @param other [Pin::Base] The caller's block
22
+ # @param position [Position, Array(Integer, Integer)] The caller's position
23
+ # @return [Boolean]
24
+ def visible_from?(other, position)
25
+ position = Position.normalize(position)
26
+ other.filename == filename &&
27
+ match_tags(other.full_context.tag, full_context.tag) &&
28
+ (other == closure ||
29
+ (closure.location.range.contain?(other.location.range.start) && closure.location.range.contain?(other.location.range.ending))
30
+ ) &&
31
+ presence.contain?(position)
32
+ end
33
+
34
+ # @param other_loc [Location]
35
+ def visible_at?(other_closure, other_loc)
36
+ return true if location.filename == other_loc.filename &&
37
+ presence.include?(other_loc.range.start) &&
38
+ match_named_closure(other_closure, closure)
39
+ end
40
+
41
+ private
42
+
43
+ # @param tag1 [String]
44
+ # @param tag2 [String]
45
+ # @return [Boolean]
46
+ def match_tags tag1, tag2
47
+ # @todo This is an unfortunate hack made necessary by a discrepancy in
48
+ # how tags indicate the root namespace. The long-term solution is to
49
+ # standardize it, whether it's `Class<>`, an empty string, or
50
+ # something else.
51
+ tag1 == tag2 ||
52
+ (['', 'Class<>'].include?(tag1) && ['', 'Class<>'].include?(tag2))
53
+ end
54
+
55
+ def match_named_closure needle, haystack
56
+ return true if needle == haystack
57
+ cursor = haystack
58
+ until cursor.nil?
59
+ return true if needle.path == cursor.path
60
+ return false if cursor.path && !cursor.path.empty?
61
+ cursor = cursor.closure
62
+ end
63
+ false
64
+ end
19
65
  end
20
66
  end
21
67
  end
@@ -76,12 +76,12 @@ module Solargraph
76
76
  ver = version || Solargraph::YardMap::CoreDocs.best_download
77
77
  if RUBY_VERSION != ver
78
78
  puts "Documentation for #{RUBY_VERSION} is not available. Reverting to closest match..."
79
- else
80
- puts "Downloading docs for #{ver}..."
81
79
  end
80
+ puts "Downloading docs for #{ver}..."
82
81
  Solargraph::YardMap::CoreDocs.download ver
83
82
  # Clear cached documentation if it exists
84
83
  FileUtils.rm_rf Dir.glob(File.join(Solargraph::YardMap::CoreDocs.cache_dir, ver, '*.ser'))
84
+ puts "Download complete."
85
85
  rescue ArgumentError => e
86
86
  STDERR.puts "ERROR: #{e.message}"
87
87
  STDERR.puts "Run `solargraph available-cores` for a list."
@@ -6,19 +6,15 @@ module Solargraph
6
6
  # A Ruby file that has been parsed into an AST.
7
7
  #
8
8
  class Source
9
- # autoload :FlawedBuilder, 'solargraph/source/flawed_builder'
10
9
  autoload :Updater, 'solargraph/source/updater'
11
10
  autoload :Change, 'solargraph/source/change'
12
11
  autoload :Mapper, 'solargraph/source/mapper'
13
- # autoload :NodeMethods, 'solargraph/source/node_methods'
14
12
  autoload :EncodingFixes, 'solargraph/source/encoding_fixes'
15
13
  autoload :Cursor, 'solargraph/source/cursor'
16
14
  autoload :Chain, 'solargraph/source/chain'
17
15
  autoload :SourceChainer, 'solargraph/source/source_chainer'
18
- # autoload :NodeChainer, 'solargraph/source/node_chainer'
19
16
 
20
17
  include EncodingFixes
21
- # include NodeMethods
22
18
 
23
19
  # @return [String]
24
20
  attr_reader :filename
@@ -50,17 +46,9 @@ module Solargraph
50
46
  @node, @comments = Solargraph::Parser.parse_with_comments(@code, filename)
51
47
  @parsed = true
52
48
  rescue Parser::SyntaxError, EncodingError => e
53
- # @todo 100% whitespace results in a nil node, so there's no reason to parse it.
54
- # We still need to determine whether the resulting node should be nil or a dummy
55
- # node with a location that encompasses the range.
56
- # @node, @comments = Source.parse_with_comments(@code.gsub(/[^\s]/, ' '), filename)
57
49
  @node = nil
58
50
  @comments = {}
59
51
  @parsed = false
60
- # rescue Exception => e
61
- # Solargraph.logger.warn "[#{e.class}] #{e.message}"
62
- # Solargraph.logger.warn e.backtrace.join("\n")
63
- # raise "Error parsing #{filename || '(source)'}: [#{e.class}] #{e.message}"
64
52
  ensure
65
53
  @code.freeze
66
54
  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
@@ -61,10 +62,6 @@ module Solargraph
61
62
  working_pin = name_pin
62
63
  links[0..-2].each do |link|
63
64
  pins = link.resolve(api_map, working_pin, locals)
64
- # Locals are only used when resolving the first link
65
- # @todo There's a problem here. Call links need to resolve arguments
66
- # that might refer to local variables.
67
- # locals = []
68
65
  type = infer_first_defined(pins, working_pin, api_map)
69
66
  return [] if type.undefined?
70
67
  working_pin = Pin::ProxyType.anonymous(type)
@@ -77,6 +77,7 @@ module Solargraph
77
77
  end
78
78
  if match
79
79
  type = extra_return_type(ol, context)
80
+ break if type
80
81
  type = ComplexType.try_parse(*ol.tag(:return).types).self_to(context.to_s).qualify(api_map, context.namespace) if ol.has_tag?(:return) && ol.tag(:return).types && !ol.tag(:return).types.empty? && (type.nil? || type.undefined?)
81
82
  type ||= ComplexType::UNDEFINED
82
83
  end
@@ -170,8 +171,8 @@ module Solargraph
170
171
  # @param context [ComplexType]
171
172
  # @return [ComplexType]
172
173
  def extra_return_type docstring, context
173
- if docstring.has_tag?(:return_single_parameter) && context.subtypes.one?
174
- return context.subtypes.first
174
+ if docstring.has_tag?(:return_single_parameter) #&& context.subtypes.one?
175
+ return context.subtypes.first || ComplexType::UNDEFINED
175
176
  elsif docstring.has_tag?(:return_value_parameter) && context.value_types.one?
176
177
  return context.value_types.first
177
178
  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
@@ -131,7 +131,8 @@ module Solargraph
131
131
  # @return [Array<Pin::LocalVariable>]
132
132
  def locals_at(location)
133
133
  return [] if location.filename != filename
134
- locals.select { |pin| pin.visible_at?(location) }
134
+ closure = locate_named_path_pin(location.range.start.line, location.range.start.character)
135
+ locals.select { |pin| pin.visible_at?(closure, location) }
135
136
  end
136
137
 
137
138
  class << self
@@ -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
@@ -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 Parser.is_ast_node?(unchecked.last.node) && splatted_call?(unchecked.last.node)
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 && splatted_call?(unchecked.last.node) && (pin.parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
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
- return [Problem.new(location, "Not enough arguments to #{pin.path}")]
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = '0.42.1'
4
+ VERSION = '0.43.0'
5
5
  end
@@ -183,7 +183,7 @@ module Solargraph
183
183
  end
184
184
  end
185
185
  end
186
- result.concat config.require_paths
186
+ result.concat(config.require_paths.map { |p| File.join(directory, p) })
187
187
  result.push File.join(directory, 'lib') if result.empty?
188
188
  result
189
189
  end
@@ -156,12 +156,12 @@ module Solargraph
156
156
  @base_required ||= Set.new
157
157
  end
158
158
 
159
- private
160
-
161
159
  def directory
162
160
  @directory ||= ''
163
161
  end
164
162
 
163
+ private
164
+
165
165
  # @return [YardMap::Cache]
166
166
  def cache
167
167
  @cache ||= YardMap::Cache.new
@@ -190,11 +190,8 @@ module Solargraph
190
190
 
191
191
  # @return [void]
192
192
  def process_requires
193
- @gemset = if required.include?('bundler/require')
194
- require_from_bundle(directory)
195
- else
196
- {}
197
- end
193
+ @gemset = process_gemsets
194
+ required.merge @gemset.keys if required.include?('bundler/require')
198
195
  pins.replace core_pins
199
196
  unresolved_requires.clear
200
197
  stdlib_pins.clear
@@ -254,6 +251,11 @@ module Solargraph
254
251
  pins.concat environ.pins
255
252
  end
256
253
 
254
+ def process_gemsets
255
+ return {} if directory.empty? || !File.file?(File.join(directory, 'Gemfile'))
256
+ require_from_bundle(directory)
257
+ end
258
+
257
259
  # @param spec [Gem::Specification]
258
260
  # @return [void]
259
261
  def add_gem_dependencies spec
@@ -286,9 +288,6 @@ module Solargraph
286
288
  # @return [Array<Pin::Base>]
287
289
  def process_yardoc y, spec = nil
288
290
  return [] if y.nil?
289
- size = Dir.glob(File.join(y, '**', '*'))
290
- .map{ |f| File.size(f) }
291
- .inject(:+)
292
291
  if spec
293
292
  ser = File.join(CoreDocs.cache_dir, 'gems', "#{spec.name}-#{spec.version}.ser")
294
293
  if File.file?(ser)
@@ -297,19 +296,24 @@ module Solargraph
297
296
  dump = file.read
298
297
  file.close
299
298
  begin
300
- return Marshal.load(dump)
299
+ result = Marshal.load(dump)
300
+ return result unless result.nil? || result.empty?
301
+ Solargraph.logger.warn "Empty cache for #{spec.name} #{spec.version}. Reloading"
301
302
  rescue StandardError => e
302
303
  Solargraph.logger.warn "Error loading pin cache: [#{e.class}] #{e.message}"
303
304
  File.unlink ser
304
305
  end
305
306
  end
306
307
  end
308
+ size = Dir.glob(File.join(y, '**', '*'))
309
+ .map{ |f| File.size(f) }
310
+ .inject(:+)
307
311
  if !size.nil? && size > 20_000_000
308
312
  Solargraph::Logging.logger.warn "Yardoc at #{y} is too large to process (#{size} bytes)"
309
313
  return []
310
314
  end
315
+ Solargraph.logger.info "Loading #{spec.name} #{spec.version} from #{y}"
311
316
  load_yardoc y
312
- Solargraph.logger.info "Loading #{spec.name} #{spec.version} from yardoc"
313
317
  result = Mapper.new(YARD::Registry.all, spec).map
314
318
  if spec
315
319
  ser = File.join(CoreDocs.cache_dir, 'gems', "#{spec.name}-#{spec.version}.ser")
@@ -433,5 +437,3 @@ module Solargraph
433
437
  end
434
438
 
435
439
  Solargraph::YardMap::CoreDocs.require_minimum
436
- # Change YARD log IO to avoid sending unexpected messages to STDOUT
437
- YARD::Logger.instance.io = File.new(File::NULL, 'w')
@@ -82,6 +82,18 @@ module Solargraph
82
82
  Override.method_return('Class#allocate', 'self'),
83
83
  Override.method_return('Class.allocate', 'Class<Object>'),
84
84
 
85
+ Override.from_comment('Enumerable#detect', %(
86
+ @overload detect(&block)
87
+ @return_single_parameter
88
+ @overload detect()
89
+ @return [Enumerator]
90
+ )),
91
+ Override.from_comment('Enumerable#find', %(
92
+ @overload find(&block)
93
+ @return_single_parameter
94
+ @overload find()
95
+ @return [Enumerator]
96
+ )),
85
97
  Override.method_return('Enumerable#select', 'self'),
86
98
 
87
99
  Override.method_return('File.absolute_path', 'String'),
@@ -111,7 +123,12 @@ module Solargraph
111
123
  @param y [Numeric]
112
124
  @return [Numeric]
113
125
  )),
114
- Override.method_return('Integer#times', 'Enumerator', delete: [:overload]),
126
+ Override.from_comment('Integer#times', %(
127
+ @overload times(&block)
128
+ @return [Integer]
129
+ @overload times()
130
+ @return [Enumerator]
131
+ )),
115
132
 
116
133
  Override.method_return('Kernel#puts', 'nil'),
117
134
 
@@ -2,6 +2,9 @@
2
2
 
3
3
  require 'yard'
4
4
 
5
+ # Change YARD log IO to avoid sending unexpected messages to STDOUT
6
+ YARD::Logger.instance.io = File.new(File::NULL, 'w')
7
+
5
8
  module Solargraph
6
9
  # A placeholder for the @!domain directive. It doesn't need to do anything
7
10
  # for yardocs. It's only used for Solargraph API maps.
data/solargraph.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
 
20
20
  s.required_ruby_version = '>= 2.4'
21
21
 
22
- s.add_runtime_dependency 'backport', '~> 1.1'
22
+ s.add_runtime_dependency 'backport', '~> 1.2'
23
23
  s.add_runtime_dependency 'benchmark'
24
24
  s.add_runtime_dependency 'bundler', '>= 1.17.2'
25
25
  s.add_runtime_dependency 'diff-lcs', '~> 1.4'
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.42.1
4
+ version: 0.43.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-11 00:00:00.000000000 Z
11
+ date: 2021-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.1'
19
+ version: '1.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.1'
26
+ version: '1.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: benchmark
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -477,7 +477,6 @@ files:
477
477
  - lib/solargraph/pin/keyword.rb
478
478
  - lib/solargraph/pin/keyword_param.rb
479
479
  - lib/solargraph/pin/local_variable.rb
480
- - lib/solargraph/pin/localized.rb
481
480
  - lib/solargraph/pin/method.rb
482
481
  - lib/solargraph/pin/method_alias.rb
483
482
  - lib/solargraph/pin/namespace.rb
@@ -503,6 +502,7 @@ files:
503
502
  - lib/solargraph/source/chain/class_variable.rb
504
503
  - lib/solargraph/source/chain/constant.rb
505
504
  - lib/solargraph/source/chain/global_variable.rb
505
+ - lib/solargraph/source/chain/hash.rb
506
506
  - lib/solargraph/source/chain/head.rb
507
507
  - lib/solargraph/source/chain/instance_variable.rb
508
508
  - lib/solargraph/source/chain/link.rb
@@ -554,7 +554,7 @@ homepage: http://solargraph.org
554
554
  licenses:
555
555
  - MIT
556
556
  metadata: {}
557
- post_install_message:
557
+ post_install_message:
558
558
  rdoc_options: []
559
559
  require_paths:
560
560
  - lib
@@ -569,8 +569,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
569
569
  - !ruby/object:Gem::Version
570
570
  version: '0'
571
571
  requirements: []
572
- rubygems_version: 3.1.2
573
- signing_key:
572
+ rubygems_version: 3.1.6
573
+ signing_key:
574
574
  specification_version: 4
575
575
  summary: A Ruby language server
576
576
  test_files: []
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- module Pin
5
- module Localized
6
- # @return [Range]
7
- attr_reader :presence
8
-
9
- # @param other [Pin::Base] The caller's block
10
- # @param position [Position, Array(Integer, Integer)] The caller's position
11
- # @return [Boolean]
12
- def visible_from?(other, position)
13
- position = Position.normalize(position)
14
- other.filename == filename &&
15
- match_tags(other.full_context.tag, full_context.tag) &&
16
- (other == closure ||
17
- (closure.location.range.contain?(other.location.range.start) && closure.location.range.contain?(other.location.range.ending))
18
- ) &&
19
- presence.contain?(position)
20
- end
21
-
22
- # @param other_loc [Location]
23
- def visible_at?(other_loc)
24
- return false if location.filename != other_loc.filename
25
- presence.include?(other_loc.range.start)
26
- end
27
-
28
- private
29
-
30
- # @param tag1 [String]
31
- # @param tag2 [String]
32
- # @return [Boolean]
33
- def match_tags tag1, tag2
34
- # @todo This is an unfortunate hack made necessary by a discrepancy in
35
- # how tags indicate the root namespace. The long-term solution is to
36
- # standardize it, whether it's `Class<>`, an empty string, or
37
- # something else.
38
- tag1 == tag2 ||
39
- (['', 'Class<>'].include?(tag1) && ['', 'Class<>'].include?(tag2))
40
- end
41
- end
42
- end
43
- end