solargraph 0.26.1 → 0.27.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph.rb +5 -2
  3. data/lib/solargraph/api_map.rb +236 -234
  4. data/lib/solargraph/api_map/store.rb +18 -53
  5. data/lib/solargraph/bundle.rb +22 -0
  6. data/lib/solargraph/complex_type.rb +9 -5
  7. data/lib/solargraph/complex_type/type_methods.rb +113 -0
  8. data/lib/solargraph/complex_type/unique_type.rb +35 -0
  9. data/lib/solargraph/core_fills.rb +1 -0
  10. data/lib/solargraph/diagnostics.rb +6 -4
  11. data/lib/solargraph/diagnostics/base.rb +3 -0
  12. data/lib/solargraph/diagnostics/require_not_found.rb +2 -1
  13. data/lib/solargraph/diagnostics/rubocop.rb +21 -6
  14. data/lib/solargraph/diagnostics/type_not_defined.rb +4 -3
  15. data/lib/solargraph/diagnostics/update_errors.rb +18 -0
  16. data/lib/solargraph/language_server/host.rb +90 -222
  17. data/lib/solargraph/language_server/host/cataloger.rb +68 -0
  18. data/lib/solargraph/language_server/host/diagnoser.rb +85 -0
  19. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +35 -24
  20. data/lib/solargraph/language_server/message/text_document/completion.rb +6 -8
  21. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +1 -1
  22. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +0 -1
  23. data/lib/solargraph/language_server/transport/socket.rb +4 -6
  24. data/lib/solargraph/language_server/transport/stdio.rb +4 -6
  25. data/lib/solargraph/library.rb +152 -99
  26. data/lib/solargraph/live_map.rb +1 -1
  27. data/lib/solargraph/location.rb +28 -0
  28. data/lib/solargraph/pin.rb +2 -0
  29. data/lib/solargraph/pin/attribute.rb +26 -12
  30. data/lib/solargraph/pin/base.rb +15 -35
  31. data/lib/solargraph/pin/base_variable.rb +7 -15
  32. data/lib/solargraph/pin/block.rb +5 -9
  33. data/lib/solargraph/pin/block_parameter.rb +9 -7
  34. data/lib/solargraph/pin/conversions.rb +5 -5
  35. data/lib/solargraph/pin/duck_method.rb +1 -1
  36. data/lib/solargraph/pin/instance_variable.rb +0 -4
  37. data/lib/solargraph/pin/keyword.rb +4 -0
  38. data/lib/solargraph/pin/localized.rb +5 -3
  39. data/lib/solargraph/pin/method.rb +11 -0
  40. data/lib/solargraph/pin/namespace.rb +7 -3
  41. data/lib/solargraph/pin/proxy_type.rb +3 -7
  42. data/lib/solargraph/pin/reference.rb +2 -2
  43. data/lib/solargraph/pin/symbol.rb +1 -1
  44. data/lib/solargraph/pin/yard_pin/method.rb +2 -2
  45. data/lib/solargraph/pin/yard_pin/namespace.rb +16 -7
  46. data/lib/solargraph/position.rb +103 -0
  47. data/lib/solargraph/range.rb +70 -0
  48. data/lib/solargraph/source.rb +159 -328
  49. data/lib/solargraph/source/chain.rb +38 -55
  50. data/lib/solargraph/source/chain/call.rb +47 -29
  51. data/lib/solargraph/source/chain/class_variable.rb +2 -2
  52. data/lib/solargraph/source/chain/constant.rb +3 -3
  53. data/lib/solargraph/source/chain/definition.rb +7 -3
  54. data/lib/solargraph/source/chain/global_variable.rb +1 -1
  55. data/lib/solargraph/source/chain/head.rb +22 -9
  56. data/lib/solargraph/source/chain/instance_variable.rb +2 -2
  57. data/lib/solargraph/source/chain/link.rb +4 -4
  58. data/lib/solargraph/source/chain/literal.rb +1 -1
  59. data/lib/solargraph/source/chain/variable.rb +2 -2
  60. data/lib/solargraph/source/change.rb +0 -6
  61. data/lib/solargraph/source/cursor.rb +161 -0
  62. data/lib/solargraph/source/encoding_fixes.rb +1 -1
  63. data/lib/solargraph/source/node_chainer.rb +28 -21
  64. data/lib/solargraph/source/node_methods.rb +1 -1
  65. data/lib/solargraph/source/source_chainer.rb +217 -0
  66. data/lib/solargraph/source_map.rb +138 -0
  67. data/lib/solargraph/source_map/clip.rb +123 -0
  68. data/lib/solargraph/{source → source_map}/completion.rb +3 -3
  69. data/lib/solargraph/{source → source_map}/mapper.rb +143 -41
  70. data/lib/solargraph/version.rb +1 -1
  71. data/lib/solargraph/workspace.rb +13 -20
  72. data/lib/solargraph/yard_map.rb +77 -48
  73. metadata +17 -11
  74. data/lib/solargraph/basic_type.rb +0 -33
  75. data/lib/solargraph/basic_type_methods.rb +0 -111
  76. data/lib/solargraph/source/call_chainer.rb +0 -273
  77. data/lib/solargraph/source/fragment.rb +0 -342
  78. data/lib/solargraph/source/location.rb +0 -23
  79. data/lib/solargraph/source/position.rb +0 -95
  80. data/lib/solargraph/source/range.rb +0 -64
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54749425491eea537bc8a0ef4878e21fd17aee1c88e3575a44fecbdbfe323498
4
- data.tar.gz: 2c61f75cb843a534dc8779245a5395f3d38f411efd3cb0350cfc473b53d7e290
3
+ metadata.gz: 46fca036521e6830f95550138d35e1c504cec3f75c04b45ff9ce3cf06cbafc30
4
+ data.tar.gz: 32fd1c0b308a3c5b89f00bbd4b8f804aba057d8277ccafb5505f7e787dca3fb3
5
5
  SHA512:
6
- metadata.gz: 76bb57c0cc0843d3a71c8ee3afa65d4f14282455d43f3fcd0fa471387ff547873476cb39c5164f8ea0499d1586d7e34f1dbb14c701c7aab02abfc9de06fad676
7
- data.tar.gz: 37cc582ea792270dbe447e356b575ef2d81e76b4af9805428048589b59ef0a6f20a3c70b169631a83a1fdfb328b0ce39550b14d497fb5aad6b8f89b67a2c5a96
6
+ metadata.gz: '07263532728a958734568023619dedbd8b2fcc1c5b84aa9a4432c4af8c247f5cf47343861ee15ef8b377371c35ed96e05367d34781fbb49b1dcf120cc5f1893b'
7
+ data.tar.gz: 1bc3fddcfc98cee8f0287b46e1792f4ff87c51c45bc6ed1281e88ea8473470ef062eebe2e3ac112d63d639c647015c702072840732464b271f68574cf329cf66
@@ -24,8 +24,12 @@ module Solargraph
24
24
  end
25
25
  end
26
26
 
27
+ autoload :Position, 'solargraph/position'
28
+ autoload :Range, 'solargraph/range'
29
+ autoload :Location, 'solargraph/location'
27
30
  autoload :Shell, 'solargraph/shell'
28
31
  autoload :Source, 'solargraph/source'
32
+ autoload :SourceMap, 'solargraph/source_map'
29
33
  autoload :ApiMap, 'solargraph/api_map'
30
34
  autoload :YardMap, 'solargraph/yard_map'
31
35
  autoload :Pin, 'solargraph/pin'
@@ -38,9 +42,8 @@ module Solargraph
38
42
  autoload :Page, 'solargraph/page'
39
43
  autoload :Library, 'solargraph/library'
40
44
  autoload :Diagnostics, 'solargraph/diagnostics'
41
- autoload :BasicTypeMethods, 'solargraph/basic_type_methods'
42
- autoload :BasicType, 'solargraph/basic_type'
43
45
  autoload :ComplexType, 'solargraph/complex_type'
46
+ autoload :Bundle, 'solargraph/bundle'
44
47
 
45
48
  YARDOC_PATH = File.join(File.realpath(File.dirname(__FILE__)), '..', 'yardoc')
46
49
  YARD_EXTENSION_FILE = File.join(File.realpath(File.dirname(__FILE__)), 'yard-solargraph.rb')
@@ -1,6 +1,5 @@
1
1
  require 'rubygems'
2
2
  require 'set'
3
- require 'time'
4
3
 
5
4
  module Solargraph
6
5
  # An aggregate provider for information about workspaces, sources, gems, and
@@ -12,132 +11,147 @@ module Solargraph
12
11
  autoload :Store, 'solargraph/api_map/store'
13
12
 
14
13
  include Solargraph::ApiMap::SourceToYard
15
- include CoreFills
16
-
17
- # The workspace to analyze and process.
18
- #
19
- # @return [Solargraph::Workspace]
20
- attr_reader :workspace
21
14
 
22
15
  # Get a LiveMap associated with the current workspace.
23
16
  #
24
17
  # @return [Solargraph::LiveMap]
25
18
  attr_reader :live_map
26
19
 
27
- # @param workspace [Solargraph::Workspace]
28
- def initialize workspace = Solargraph::Workspace.new(nil)
29
- @workspace = workspace
30
- require_extensions
31
- @virtual_source = nil
32
- @sources = workspace.sources
33
- refresh_store_and_maps
34
- end
35
-
36
- # Create an ApiMap with a workspace in the specified directory.
37
- #
38
- # @param directory [String]
39
- # @return [ApiMap]
40
- def self.load directory
41
- self.new(Solargraph::Workspace.new(directory))
42
- end
43
-
44
- # @return [Array<Solargraph::Pin::Base>]
45
- def pins
46
- store.pins
47
- end
48
-
49
20
  # @return [Array<String>]
50
- def domains
51
- @domains ||= []
52
- end
21
+ attr_reader :unresolved_requires
53
22
 
54
- # An array of required paths in the workspace.
23
+ # @param pins [Array<Solargraph::Pin::Base>]
24
+ # @param yard_map [YardMap]
25
+ # def initialize workspace = Solargraph::Workspace.new(nil)
26
+ def initialize pins: []
27
+ # @todo Extensions don't work yet
28
+ # require_extensions
29
+ @source_map_hash = {}
30
+ @cache = Cache.new
31
+ @mutex = Mutex.new
32
+ index pins
33
+ end
34
+
35
+ # @param pins [Array<Pin::Base>]
36
+ # @return [self]
37
+ def index pins
38
+ @mutex.synchronize {
39
+ @source_map_hash.clear
40
+ @cache.clear
41
+ @store = Store.new(pins + YardMap.new.pins)
42
+ @unresolved_requires = []
43
+ }
44
+ self
45
+ end
46
+
47
+ # @param source [Source]
48
+ # @return [self]
49
+ def map source
50
+ catalog Bundle.new(sources: [source])
51
+ self
52
+ end
53
+
54
+ # Catalog a workspace. Additional sources that need to be mapped can be
55
+ # included in an optional array.
55
56
  #
56
- # @return [Array<String>]
57
- def required
58
- result = []
59
- @sources.each do |s|
60
- result.concat s.required.map(&:name)
57
+ # @param bundle [Bundle]
58
+ # @return [self]
59
+ def catalog bundle
60
+ # @todo This can be more efficient. We don't need to remap sources that
61
+ # are already here.
62
+ # all_sources = (workspace.sources + others).uniq
63
+ new_map_hash = {}
64
+ unmerged = false
65
+ bundle.sources.each do |source|
66
+ if source_map_hash.has_key?(source.filename)
67
+ if source_map_hash[source.filename].code == source.code
68
+ new_map_hash[source.filename] = source_map_hash[source.filename]
69
+ else
70
+ map = Solargraph::SourceMap.map(source)
71
+ if source_map_hash[source.filename].try_merge!(map)
72
+ new_map_hash[source.filename] = source_map_hash[source.filename]
73
+ else
74
+ new_map_hash[source.filename] = map
75
+ unmerged = true
76
+ end
77
+ end
78
+ else
79
+ map = Solargraph::SourceMap.map(source)
80
+ new_map_hash[source.filename] = map
81
+ unmerged = true
82
+ end
61
83
  end
62
- result.concat workspace.config.required
63
- result.uniq
64
- end
65
-
66
- # Declare a virtual source that will be included in the map regardless of
67
- # whether it's in the workspace.
68
- #
69
- # If the source is in the workspace, virtualizing it has no effect. Only
70
- # one source can be virtualized at a time.
71
- #
72
- # @param source [Solargraph::Source]
73
- # @return [Solargraph::Source]
74
- def virtualize source
75
- # @todo Confirm the correct way to handle caches
76
- cache.clear if (source.nil? and !@virtual_source.nil?) or (!source.nil? and !@virtual_source.nil? and source.pins != @virtual_source.pins)
77
- store.remove @virtual_source unless @virtual_source.nil?
78
- domains.clear
79
- domains.concat workspace.config.domains
80
- domains.concat source.domains unless source.nil?
81
- domains.uniq!
82
- if workspace.has_source?(source)
83
- @sources = workspace.sources
84
- @virtual_source = nil
85
- else
86
- @virtual_source = source
87
- @sources = workspace.sources
88
- unless @virtual_source.nil?
89
- @sources.push @virtual_source
90
- process_virtual
84
+ return self unless unmerged
85
+ pins = []
86
+ reqs = []
87
+ # @param map [SourceMap]
88
+ new_map_hash.values.each do |map|
89
+ pins.concat map.pins
90
+ reqs.concat map.requires.map(&:name)
91
+ end
92
+ reqs.concat bundle.required
93
+ unless bundle.load_paths.empty?
94
+ reqs.delete_if do |r|
95
+ result = false
96
+ bundle.load_paths.each do |l|
97
+ if new_map_hash.keys.include?(File.join(l, "#{r}.rb"))
98
+ result = true
99
+ break
100
+ end
101
+ end
102
+ result
91
103
  end
92
104
  end
93
- source
105
+ bundle.yard_map.change(reqs)
106
+ new_store = Store.new(pins + bundle.yard_map.pins)
107
+ @mutex.synchronize {
108
+ @cache.clear
109
+ @source_map_hash = new_map_hash
110
+ @store = new_store
111
+ @unresolved_requires = bundle.yard_map.unresolved_requires
112
+ }
113
+ self
94
114
  end
95
115
 
96
- # Create a Source from the code and filename, and virtualize the result.
97
- # This method can be useful for directly testing the ApiMap. In practice,
98
- # applications should use a Library to synchronize the ApiMap to a
99
- # workspace.
100
- #
101
- # @param code [String]
102
116
  # @param filename [String]
103
- # @return [Solargraph::Source]
104
- def virtualize_string code, filename = nil
105
- source = Source.load_string(code, filename)
106
- virtualize source
117
+ # @param position [Position]
118
+ # @return [Source::Cursor]
119
+ def cursor_at filename, position
120
+ raise "File not found: #{filename}" unless source_map_hash.has_key?(filename)
121
+ source_map_hash[filename].cursor_at(position)
107
122
  end
108
123
 
109
- # Refresh the ApiMap. This method checks for pending changes before
110
- # performing the refresh unless the `force` parameter is true.
124
+ # Get a clip by filename and position.
111
125
  #
112
- # @param force [Boolean] Perform a refresh even if the map is not "stale."
113
- # @return [Boolean] True if a refresh was performed.
114
- def refresh force = false
115
- return false unless force or changed?
116
- if force
117
- refresh_store_and_maps
118
- else
119
- update_store_and_maps
120
- end
121
- true
126
+ # @param filename [String]
127
+ # @param position [Position]
128
+ # @return [SourceMap::Clip]
129
+ def clip_at filename, position
130
+ SourceMap::Clip.new(self, cursor_at(filename, position))
122
131
  end
123
132
 
124
- # True if a workspace file has been created, modified, or deleted since
125
- # the last time the map was processed.
133
+ # Create an ApiMap with a workspace in the specified directory.
126
134
  #
127
- # @return [Boolean]
128
- def changed?
129
- return true if current_workspace_sources.length != workspace.sources.length
130
- return true if @stime.nil?
131
- return true if workspace.stime > @stime
132
- return true if !@virtual_source.nil? and @virtual_source.stime > @stime
133
- false
135
+ # @param directory [String]
136
+ # @return [ApiMap]
137
+ def self.load directory
138
+ # @todo How should this work?
139
+ api_map = self.new #(Solargraph::Workspace.new(directory))
140
+ workspace = Solargraph::Workspace.new(directory)
141
+ api_map.catalog Bundle.new(sources: workspace.sources)
142
+ api_map
143
+ end
144
+
145
+ # @return [Array<Solargraph::Pin::Base>]
146
+ def pins
147
+ store.pins
134
148
  end
135
149
 
136
150
  # An array of suggestions based on Ruby keywords (`if`, `end`, etc.).
137
151
  #
138
152
  # @return [Array<Solargraph::Pin::Keyword>]
139
153
  def self.keywords
140
- @keywords ||= KEYWORDS.map{ |s|
154
+ @keywords ||= CoreFills::KEYWORDS.map{ |s|
141
155
  Pin::Keyword.new(s)
142
156
  }.freeze
143
157
  end
@@ -190,34 +204,26 @@ module Solargraph
190
204
  # Get a fully qualified namespace name. This method will start the search
191
205
  # in the specified context until it finds a match for the name.
192
206
  #
193
- # @param namespace [String] The namespace to match
207
+ # @param namespace [String, nil] The namespace to match
194
208
  # @param context [String] The context to search
195
209
  # @return [String]
196
210
  def qualify namespace, context = ''
197
211
  # @todo The return for self might work better elsewhere
212
+ return nil if namespace.nil?
198
213
  return qualify(context) if namespace == 'self'
199
214
  cached = cache.get_qualified_namespace(namespace, context)
200
215
  return cached.clone unless cached.nil?
201
- result = inner_qualify(namespace, context, [])
216
+ # result = inner_qualify(namespace, context, [])
217
+ # result = result[2..-1] if !result.nil? && result.start_with?('::')
218
+ if namespace.start_with?('::')
219
+ result = inner_qualify(namespace[2..-1], '', [])
220
+ else
221
+ result = inner_qualify(namespace, context, [])
222
+ end
202
223
  cache.set_qualified_namespace(namespace, context, result)
203
224
  result
204
225
  end
205
226
 
206
- def fragment_at(location)
207
- @sources.each do |source|
208
- return source.fragment_at(location.range.start.line, location.range.start.column) if source.filename == location.filename
209
- end
210
- nil
211
- end
212
-
213
- # @deprecated Use #qualify instead
214
- # @param namespace [String]
215
- # @param context [String]
216
- # @return [String]
217
- def find_fully_qualified_namespace namespace, context = ''
218
- qualify namespace, context
219
- end
220
-
221
227
  # Get an array of instance variable pins defined in specified namespace
222
228
  # and scope.
223
229
  #
@@ -225,7 +231,14 @@ module Solargraph
225
231
  # @param scope [Symbol] :instance or :class
226
232
  # @return [Array<Solargraph::Pin::InstanceVariable>]
227
233
  def get_instance_variable_pins(namespace, scope = :instance)
228
- store.get_instance_variables(namespace, scope)
234
+ result = []
235
+ result.concat store.get_instance_variables(namespace, scope)
236
+ sc = qualify(store.get_superclass(namespace), namespace)
237
+ until sc.nil?
238
+ result.concat store.get_instance_variables(sc, scope)
239
+ sc = qualify(store.get_superclass(sc), sc)
240
+ end
241
+ result
229
242
  end
230
243
 
231
244
  # Get an array of class variable pins for a namespace.
@@ -241,21 +254,10 @@ module Solargraph
241
254
  store.get_symbols
242
255
  end
243
256
 
244
- # @todo Make this better
245
- def get_source filename
246
- @sources.each do |s|
247
- return s if s.filename == filename
248
- end
249
- nil
250
- end
251
-
252
257
  # @return [Array<Solargraph::Pin::GlobalVariable>]
253
258
  def get_global_variable_pins
254
- globals = []
255
- @sources.each do |s|
256
- globals.concat s.global_variable_pins
257
- end
258
- globals
259
+ # @todo Slow version
260
+ pins.select{|p| p.kind == Pin::GLOBAL_VARIABLE}
259
261
  end
260
262
 
261
263
  # Get an array of methods available in a particular context.
@@ -271,21 +273,22 @@ module Solargraph
271
273
  result = []
272
274
  skip = []
273
275
  if fqns == ''
274
- domains.each do |domain|
275
- type = ComplexType.parse(domain).first
276
- result.concat inner_get_methods(type.name, type.scope, [:public], deep, skip)
277
- end
276
+ # @todo Implement domains
277
+ # domains.each do |domain|
278
+ # type = ComplexType.parse(domain).first
279
+ # result.concat inner_get_methods(type.name, type.scope, [:public], deep, skip)
280
+ # end
278
281
  result.concat inner_get_methods(fqns, :class, visibility, deep, skip)
279
282
  result.concat inner_get_methods(fqns, :instance, visibility, deep, skip)
280
283
  result.concat inner_get_methods('Kernel', :instance, visibility, deep, skip)
281
284
  else
282
285
  result.concat inner_get_methods(fqns, scope, visibility, deep, skip)
283
286
  end
284
- live = live_map.get_methods(fqns, '', scope.to_s, visibility.include?(:private))
285
- unless live.empty?
286
- exist = result.map(&:name)
287
- result.concat live.reject{|p| exist.include?(p.name)}
288
- end
287
+ # live = live_map.get_methods(fqns, '', scope.to_s, visibility.include?(:private))
288
+ # unless live.empty?
289
+ # exist = result.map(&:name)
290
+ # result.concat live.reject{|p| exist.include?(p.name)}
291
+ # end
289
292
  cache.set_methods(fqns, scope, visibility, deep, result)
290
293
  result
291
294
  end
@@ -296,21 +299,17 @@ module Solargraph
296
299
  # @param context [String]
297
300
  # @return [Array<Solargraph::Pin::Base>]
298
301
  def get_complex_type_methods type, context = '', internal = false
299
- return [] if type.undefined? or type.void?
302
+ return [] if type.undefined? || type.void?
300
303
  result = []
301
304
  if type.duck_type?
302
305
  type.select(&:duck_type?).each do |t|
303
306
  result.push Pin::DuckMethod.new(nil, t.tag[1..-1])
304
307
  end
305
308
  else
306
- unless type.nil? or type.name == 'void'
309
+ unless type.nil? || type.name == 'void'
307
310
  namespace = qualify(type.namespace, context)
308
- if ['Class', 'Module'].include?(namespace) and !type.subtypes.empty?
309
- subtype = qualify(type.subtypes.first.name, context)
310
- result.concat get_methods(subtype, scope: :class)
311
- end
312
311
  visibility = [:public]
313
- if namespace == context or super_and_sub?(namespace, context)
312
+ if namespace == context || super_and_sub?(namespace, context)
314
313
  visibility.push :protected
315
314
  visibility.push :private if internal
316
315
  end
@@ -333,9 +332,9 @@ module Solargraph
333
332
  # @param scope [Symbol] :instance or :class
334
333
  # @return [Array<Solargraph::Pin::Base>]
335
334
  def get_method_stack fqns, name, scope: :instance
336
- # @todo Caches don't work on this query
335
+ # @todo This cache is still causing problems.
337
336
  # cached = cache.get_method_stack(fqns, name, scope)
338
- # return cached.clone unless cached.nil?
337
+ # return cached unless cached.nil?
339
338
  result = get_methods(fqns, scope: scope, visibility: [:private, :protected, :public]).select{|p| p.name == name}
340
339
  # cache.set_method_stack(fqns, name, scope, result)
341
340
  result
@@ -343,19 +342,29 @@ module Solargraph
343
342
 
344
343
  # Get an array of all suggestions that match the specified path.
345
344
  #
345
+ # @deprecated Use #get_path_pins instead.
346
+ #
346
347
  # @param path [String] The path to find
347
348
  # @return [Array<Solargraph::Pin::Base>]
348
349
  def get_path_suggestions path
349
350
  return [] if path.nil?
350
351
  result = []
351
352
  result.concat store.get_path_pins(path)
352
- if result.empty?
353
- lp = live_map.get_path_pin(path)
354
- result.push lp unless lp.nil?
355
- end
353
+ # if result.empty?
354
+ # lp = live_map.get_path_pin(path)
355
+ # result.push lp unless lp.nil?
356
+ # end
356
357
  result
357
358
  end
358
359
 
360
+ # Get an array of pins that match the specified path.
361
+ #
362
+ # @param path [String]
363
+ # @return [Array<Pin::Base>]
364
+ def get_path_pins path
365
+ get_path_suggestions(path)
366
+ end
367
+
359
368
  # Get a list of documented paths that match the query.
360
369
  #
361
370
  # @example
@@ -364,11 +373,10 @@ module Solargraph
364
373
  # @param query [String] The text to match
365
374
  # @return [Array<String>]
366
375
  def search query
367
- rake_yard(store) if @yard_stale
368
- @yard_stale = false
376
+ rake_yard(store)
369
377
  found = []
370
378
  code_object_paths.each do |k|
371
- if found.empty? or (query.include?('.') or query.include?('#')) or !(k.include?('.') or k.include?('#'))
379
+ if found.empty? || (query.include?('.') || query.include?('#')) || !(k.include?('.') || k.include?('#'))
372
380
  found.push k if k.downcase.include?(query.downcase)
373
381
  end
374
382
  end
@@ -383,8 +391,7 @@ module Solargraph
383
391
  # @param path [String] The path to find
384
392
  # @return [Array<YARD::CodeObject::Base>]
385
393
  def document path
386
- rake_yard(store) if @yard_stale
387
- @yard_stale = false
394
+ rake_yard(store)
388
395
  docs = []
389
396
  docs.push code_object_at(path) unless code_object_at(path).nil?
390
397
  docs
@@ -396,74 +403,68 @@ module Solargraph
396
403
  # @return [Array<Pin::Base>]
397
404
  def query_symbols query
398
405
  result = []
399
- @sources.each do |s|
406
+ source_map_hash.values.each do |s|
400
407
  result.concat s.query_symbols(query)
401
408
  end
402
409
  result
403
410
  end
404
411
 
405
- # @param location [Solargraph::Source::Location]
412
+ # @param location [Solargraph::Location]
406
413
  # @return [Solargraph::Pin::Base]
407
414
  def locate_pin location
408
- @sources.each do |source|
409
- pin = source.locate_pin(location)
410
- return pin unless pin.nil?
411
- end
412
- nil
415
+ return nil if location.nil? || !source_map_hash.has_key?(location.filename)
416
+ source_map_hash[location.filename].locate_pin(location)
413
417
  end
414
418
 
415
- # @return [Array<String>]
416
- def unresolved_requires
417
- yard_map.unresolved_requires
419
+ # @raise [FileNotFoundError] if the cursor's file is not in the ApiMap
420
+ # @param cursor [Source::Cursor]
421
+ # @return [SourceMap::Clip]
422
+ def clip cursor
423
+ raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename}" unless source_map_hash.has_key?(cursor.filename)
424
+ SourceMap::Clip.new(self, cursor)
418
425
  end
419
426
 
420
- private
421
-
422
- # @return [ApiMap::Store]
423
- def store
424
- @store ||= ApiMap::Store.new(@sources, yard_map.pins)
427
+ # Get an array of document symbols from a file.
428
+ #
429
+ # @param filename [String]
430
+ # @return [Array<Pin::Symbol>]
431
+ def document_symbols filename
432
+ return [] unless source_map_hash.has_key?(filename) # @todo Raise error?
433
+ source_map_hash[filename].document_symbols
425
434
  end
426
435
 
427
- # @return [void]
428
- def refresh_store_and_maps
429
- @yard_stale = true
430
- @live_map = Solargraph::LiveMap.new(self)
431
- store.update_yard(yard_map.pins) if yard_map.change(required)
432
- cache.clear
433
- @stime = Time.now
436
+ # Get a source map by filename.
437
+ #
438
+ # @param filename [String]
439
+ # @return [SourceMap]
440
+ def source_map filename
441
+ raise FileNotFoundError, "Source map for `#{filename}` not found" unless source_map_hash.has_key?(filename)
442
+ source_map_hash[filename]
434
443
  end
435
444
 
436
- # @return [void]
437
- def update_store_and_maps
438
- @yard_stale = true
439
- store.remove *(current_workspace_sources.reject{ |s| workspace.sources.include?(s) })
440
- @sources = workspace.sources
441
- @sources.push @virtual_source unless @virtual_source.nil?
442
- store.update *(@sources.select{ |s| @stime.nil? or s.stime > @stime })
443
- store.update_yard(yard_map.pins) if yard_map.change(required)
444
- cache.clear
445
- @stime = Time.now
446
- end
445
+ private
447
446
 
448
- # @return [void]
449
- def process_virtual
450
- map_source @virtual_source unless @virtual_source.nil?
451
- if yard_map.change(required)
452
- store.update_yard(yard_map.pins)
453
- end
454
- cache.clear
447
+ # A hash of source maps with filename keys.
448
+ #
449
+ # @return [Hash{String => SourceMap}]
450
+ def source_map_hash
451
+ @mutex.synchronize {
452
+ @source_map_hash
453
+ }
455
454
  end
456
455
 
457
- # @param source [Solargraph::Source]
458
- # @return [void]
459
- def map_source source
460
- store.update source
461
- path_macros.merge! source.path_macros
456
+ # @return [ApiMap::Store]
457
+ def store
458
+ @mutex.synchronize {
459
+ @store
460
+ }
462
461
  end
463
462
 
464
463
  # @return [Solargraph::ApiMap::Cache]
465
464
  def cache
466
- @cache ||= Cache.new
465
+ @mutex.synchronize {
466
+ @cache
467
+ }
467
468
  end
468
469
 
469
470
  # @param fqns [String] A fully qualified namespace
@@ -471,34 +472,41 @@ module Solargraph
471
472
  # @param visibility [Array<Symbol>] :public, :protected, and/or :private
472
473
  # @param deep [Boolean]
473
474
  # @param skip [Array<String>]
475
+ # @param no_core [Boolean] Skip core classes if true
474
476
  # @return [Array<Pin::Base>]
475
- def inner_get_methods fqns, scope, visibility, deep, skip
477
+ def inner_get_methods fqns, scope, visibility, deep, skip, no_core = false
478
+ return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module|Kernel)$/
476
479
  reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
477
480
  return [] if skip.include?(reqstr)
478
481
  skip.push reqstr
479
482
  result = []
480
- result.concat store.get_attrs(fqns, scope)
481
483
  result.concat store.get_methods(fqns, scope: scope, visibility: visibility)
482
484
  if deep
483
485
  sc = store.get_superclass(fqns)
484
486
  unless sc.nil?
485
- fqsc = qualify(sc, fqns)
486
- result.concat inner_get_methods(fqsc, scope, visibility, true, skip) unless fqsc.nil?
487
+ fqsc = qualify(sc, fqns.split('::')[0..-2].join('::'))
488
+ result.concat inner_get_methods(fqsc, scope, visibility, true, skip, true) unless fqsc.nil?
487
489
  end
488
490
  if scope == :instance
489
- store.get_includes(fqns).each do |im|
491
+ store.get_includes(fqns).reverse.each do |im|
490
492
  fqim = qualify(im, fqns)
491
- result.concat inner_get_methods(fqim, scope, visibility, deep, skip) unless fqim.nil?
493
+ result.concat inner_get_methods(fqim, scope, visibility, deep, skip, true) unless fqim.nil?
492
494
  end
493
- result.concat inner_get_methods('Object', :instance, [:public], deep, skip) unless fqns == 'Object'
495
+ result.concat inner_get_methods('Object', :instance, [:public], deep, skip, no_core)
494
496
  else
495
- store.get_extends(fqns).each do |em|
497
+ store.get_extends(fqns).reverse.each do |em|
496
498
  fqem = qualify(em, fqns)
497
- result.concat inner_get_methods(fqem, :instance, visibility, deep, skip) unless fqem.nil?
499
+ result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
498
500
  end
499
- type = get_namespace_type(fqns)
500
- result.concat inner_get_methods('Class', :instance, fqns == '' ? [:public] : visibility, deep, skip) if type == :class
501
- result.concat inner_get_methods('Module', :instance, fqns == '' ? [:public] : visibility, deep, skip) #if type == :module
501
+ unless no_core || fqns.empty?
502
+ type = get_namespace_type(fqns)
503
+ result.concat inner_get_methods('Class', :instance, visibility, deep, skip, no_core) if type == :class
504
+ result.concat inner_get_methods('Module', :instance,visibility, deep, skip, no_core)
505
+ end
506
+ end
507
+ store.domains(fqns).each do |d|
508
+ dt = ComplexType.parse(d)
509
+ result.concat inner_get_methods(dt.namespace, dt.scope, [:public], deep, skip)
502
510
  end
503
511
  end
504
512
  result
@@ -517,7 +525,7 @@ module Solargraph
517
525
  fqis = qualify(is, fqns)
518
526
  result.concat inner_get_constants(fqis, [:public], skip) unless fqis.nil?
519
527
  end
520
- result.concat live_map.get_constants(fqns)
528
+ # result.concat live_map.get_constants(fqns)
521
529
  result
522
530
  end
523
531
 
@@ -538,11 +546,6 @@ module Solargraph
538
546
  @path_macros ||= {}
539
547
  end
540
548
 
541
- # @return [Array<Solargraph::Source>]
542
- def current_workspace_sources
543
- @sources - [@virtual_source]
544
- end
545
-
546
549
  # @param name [String]
547
550
  # @param root [String]
548
551
  # @param skip [Array<String>]
@@ -571,7 +574,7 @@ module Solargraph
571
574
  return name if store.namespace_exists?(name)
572
575
  end
573
576
  end
574
- live_map.get_fqns(name, root)
577
+ # live_map.get_fqns(name, root)
575
578
  end
576
579
 
577
580
  # Get the namespace's type (Class or Module).
@@ -585,13 +588,6 @@ module Solargraph
585
588
  pin.type
586
589
  end
587
590
 
588
- # Get a YardMap associated with the current workspace.
589
- #
590
- # @return [Solargraph::YardMap]
591
- def yard_map
592
- @yard_map ||= Solargraph::YardMap.new(required: required, workspace: workspace)
593
- end
594
-
595
591
  # Sort an array of pins to put nil or undefined variables last.
596
592
  #
597
593
  # @param pins [Array<Solargraph::Pin::Base>]
@@ -600,7 +596,7 @@ module Solargraph
600
596
  result = []
601
597
  nil_pins = []
602
598
  pins.each do |pin|
603
- if pin.variable? and pin.nil_assignment?
599
+ if pin.variable? && pin.nil_assignment?
604
600
  nil_pins.push pin
605
601
  else
606
602
  result.push pin
@@ -609,11 +605,17 @@ module Solargraph
609
605
  result + nil_pins
610
606
  end
611
607
 
608
+ # Check if a class is a superclass of another class.
609
+ #
610
+ # @param sup [String] The superclass
611
+ # @param sub [String] The subclass
612
+ # @return [Boolean]
612
613
  def super_and_sub?(sup, sub)
613
- cls = store.get_superclass(sub)
614
+ fqsup = qualify(sup)
615
+ cls = qualify(store.get_superclass(sub), sub)
614
616
  until cls.nil?
615
- return true if cls == sup
616
- cls = store.get_superclass(cls)
617
+ return true if cls == fqsup
618
+ cls = qualify(store.get_superclass(cls), cls)
617
619
  end
618
620
  false
619
621
  end