Class: Solargraph::ApiMap

Inherits:
Object
  • Object
show all
Includes:
SourceToYard
Defined in:
lib/solargraph/api_map.rb,
lib/solargraph/api_map/cache.rb,
lib/solargraph/api_map/store.rb,
lib/solargraph/api_map/source_to_yard.rb,
lib/solargraph/api_map/bundler_methods.rb

Overview

An aggregate provider for information about workspaces, sources, gems, and the Ruby core.

Defined Under Namespace

Modules: BundlerMethods, SourceToYard Classes: Cache, Store

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SourceToYard

#code_object_at, #code_object_paths, #rake_yard

Constructor Details

#initialize(pins: []) ⇒ ApiMap

Returns a new instance of ApiMap.

Parameters:



25
26
27
28
29
30
# File 'lib/solargraph/api_map.rb', line 25

def initialize pins: []
  @source_map_hash = {}
  @cache = Cache.new
  @method_alias_stack = []
  index pins
end

Instance Attribute Details

#unresolved_requiresArray<String> (readonly)

Returns:



22
23
24
# File 'lib/solargraph/api_map.rb', line 22

def unresolved_requires
  @unresolved_requires
end

Class Method Details

.load(directory) ⇒ ApiMap

Create an ApiMap with a workspace in the specified directory.

Parameters:

  • directory (String)

Returns:



114
115
116
117
118
119
120
121
122
# File 'lib/solargraph/api_map.rb', line 114

def self.load directory
  api_map = new
  workspace = Solargraph::Workspace.new(directory)
  # api_map.catalog Bench.new(workspace: workspace)
  library = Library.new(workspace)
  library.map!
  api_map.catalog library.bench
  api_map
end

Instance Method Details

#bundled?(filename) ⇒ Boolean

True if the specified file was included in a bundle, i.e., it’s either included in a workspace or open in a library.

Parameters:

  • filename (String)

Returns:

  • (Boolean)


433
434
435
# File 'lib/solargraph/api_map.rb', line 433

def bundled? filename
  source_map_hash.keys.include?(filename)
end

#catalog(bench) ⇒ Object

Catalog a bench.

Parameters:



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/solargraph/api_map.rb', line 57

def catalog bench
  implicit.clear
  @cache.clear
  @source_map_hash = bench.source_maps.map { |s| [s.filename, s] }.to_h
  pins = bench.source_maps.map(&:pins).flatten
  external_requires = bench.external_requires
  source_map_hash.each_value do |map|
    implicit.merge map.environ
  end
  external_requires.merge implicit.requires
  external_requires.merge bench.workspace.config.required
  yard_map.change(external_requires, bench.workspace.directory, bench.workspace.source_gems)
  @store = Store.new(yard_map.pins + implicit.pins + pins)
  @unresolved_requires = yard_map.unresolved_requires
  @rebindable_method_names = nil
  store.block_pins.each { |blk| blk.rebind(self) }
  self
end

#clip(cursor) ⇒ SourceMap::Clip

Parameters:

Returns:

Raises:



401
402
403
404
# File 'lib/solargraph/api_map.rb', line 401

def clip cursor
  raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename}" unless source_map_hash.key?(cursor.filename)
  SourceMap::Clip.new(self, cursor)
end

#clip_at(filename, position) ⇒ SourceMap::Clip

Get a clip by filename and position.

Parameters:

Returns:



105
106
107
108
# File 'lib/solargraph/api_map.rb', line 105

def clip_at filename, position
  position = Position.normalize(position)
  SourceMap::Clip.new(self, cursor_at(filename, position))
end

#cursor_at(filename, position) ⇒ Source::Cursor

Parameters:

Returns:

Raises:



94
95
96
97
98
# File 'lib/solargraph/api_map.rb', line 94

def cursor_at filename, position
  position = Position.normalize(position)
  raise FileNotFoundError, "File not found: #{filename}" unless source_map_hash.key?(filename)
  source_map_hash[filename].cursor_at(position)
end

#document(path) ⇒ Array<YARD::CodeObjects::Base>

Get YARD documentation for the specified path.

Examples:

api_map.document('String#split')

Parameters:

  • path (String)

    The path to find

Returns:

  • (Array<YARD::CodeObjects::Base>)


374
375
376
377
378
379
# File 'lib/solargraph/api_map.rb', line 374

def document path
  rake_yard(store)
  docs = []
  docs.push code_object_at(path) unless code_object_at(path).nil?
  docs
end

#document_symbols(filename) ⇒ Array<Pin::Symbol>

Get an array of document symbols from a file.

Parameters:

  • filename (String)

Returns:



410
411
412
413
# File 'lib/solargraph/api_map.rb', line 410

def document_symbols filename
  return [] unless source_map_hash.key?(filename) # @todo Raise error?
  resolve_method_aliases source_map_hash[filename].document_symbols
end

#get_class_variable_pins(namespace) ⇒ Array<Solargraph::Pin::ClassVariable>

Get an array of class variable pins for a namespace.

Parameters:

  • namespace (String)

    A fully qualified namespace

Returns:



227
228
229
# File 'lib/solargraph/api_map.rb', line 227

def get_class_variable_pins(namespace)
  prefer_non_nil_variables(store.get_class_variables(namespace))
end

#get_complex_type_methods(type, context = '', internal = false) ⇒ Array<Solargraph::Pin::Base>

Get an array of method pins for a complex type.

The type’s namespace and the context should be fully qualified. If the context matches the namespace type or is a subclass of the type, protected methods are included in the results. If protected methods are included and internal is true, private methods are also included.

Examples:

api_map = Solargraph::ApiMap.new
type = Solargraph::ComplexType.parse('String')
api_map.get_complex_type_methods(type)

Parameters:

  • type (Solargraph::ComplexType)

    The complex type of the namespace

  • context (String) (defaults to: '')

    The context from which the type is referenced

  • internal (Boolean) (defaults to: false)

    True to include private methods

Returns:



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/solargraph/api_map.rb', line 288

def get_complex_type_methods type, context = '', internal = false
  # This method does not qualify the complex type's namespace because
  # it can cause conflicts between similar names, e.g., `Foo` vs.
  # `Other::Foo`. It still takes a context argument to determine whether
  # protected and private methods are visible.
  return [] if type.undefined? || type.void?
  result = []
  if type.duck_type?
    type.select(&:duck_type?).each do |t|
      result.push Pin::DuckMethod.new(name: t.tag[1..-1])
    end
    result.concat get_methods('Object')
  else
    unless type.nil? || type.name == 'void'
      visibility = [:public]
      if type.namespace == context || super_and_sub?(type.namespace, context)
        visibility.push :protected
        visibility.push :private if internal
      end
      result.concat get_methods(type.namespace, scope: type.scope, visibility: visibility)
    end
  end
  result
end

#get_constants(namespace, *contexts) ⇒ Array<Solargraph::Pin::Base>

Get suggestions for constants in the specified namespace. The result may contain both constant and namespace pins.

Parameters:

  • namespace (String)

    The namespace

  • contexts (Array<String>)

    The contexts

Returns:



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/solargraph/api_map.rb', line 168

def get_constants namespace, *contexts
  namespace ||= ''
  contexts.push '' if contexts.empty?
  cached = cache.get_constants(namespace, contexts)
  return cached.clone unless cached.nil?
  skip = Set.new
  result = []
  contexts.each do |context|
    fqns = qualify(namespace, context)
    visibility = [:public]
    visibility.push :private if fqns == context
    result.concat inner_get_constants(fqns, visibility, skip)
  end
  cache.set_constants(namespace, contexts, result)
  result
end

#get_global_variable_pinsArray<Solargraph::Pin::GlobalVariable>



237
238
239
# File 'lib/solargraph/api_map.rb', line 237

def get_global_variable_pins
  store.pins_by_class(Pin::GlobalVariable)
end

#get_instance_variable_pins(namespace, scope = :instance) ⇒ Array<Solargraph::Pin::InstanceVariable>

Get an array of instance variable pins defined in specified namespace and scope.

Parameters:

  • namespace (String)

    A fully qualified namespace

  • scope (Symbol) (defaults to: :instance)

    :instance or :class

Returns:



210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/solargraph/api_map.rb', line 210

def get_instance_variable_pins(namespace, scope = :instance)
  result = []
  used = [namespace]
  result.concat store.get_instance_variables(namespace, scope)
  sc = qualify_lower(store.get_superclass(namespace), namespace)
  until sc.nil? || used.include?(sc)
    used.push sc
    result.concat store.get_instance_variables(sc, scope)
    sc = qualify_lower(store.get_superclass(sc), sc)
  end
  result
end

#get_method_stack(fqns, name, scope: :instance) ⇒ Array<Solargraph::Pin::Method>

Get a stack of method pins for a method name in a namespace. The order of the pins corresponds to the ancestry chain, with highest precedence first.

Examples:

api_map.get_method_stack('Subclass', 'method_name')
  #=> [ <Subclass#method_name pin>, <Superclass#method_name pin> ]

Parameters:

  • fqns (String)
  • name (String)
  • scope (Symbol) (defaults to: :instance)

    :instance or :class

Returns:



325
326
327
# File 'lib/solargraph/api_map.rb', line 325

def get_method_stack fqns, name, scope: :instance
  get_methods(fqns, scope: scope, visibility: [:private, :protected, :public]).select { |p| p.name == name }
end

#get_methods(fqns, scope: :instance, visibility: [:public], deep: true) ⇒ Array<Solargraph::Pin::Method>

Get an array of methods available in a particular context.

Parameters:

  • fqns (String)

    The fully qualified namespace to search for methods

  • scope (Symbol) (defaults to: :instance)

    :class or :instance

  • visibility (Array<Symbol>) (defaults to: [:public])

    :public, :protected, and/or :private

  • deep (Boolean) (defaults to: true)

    True to include superclasses, mixins, etc.

Returns:



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/solargraph/api_map.rb', line 248

def get_methods fqns, scope: :instance, visibility: [:public], deep: true
  cached = cache.get_methods(fqns, scope, visibility, deep)
  return cached.clone unless cached.nil?
  result = []
  skip = Set.new
  if fqns == ''
    # @todo Implement domains
    implicit.domains.each do |domain|
      type = ComplexType.try_parse(domain)
      next if type.undefined?
      result.concat inner_get_methods(type.name, type.scope, visibility, deep, skip)
    end
    result.concat inner_get_methods(fqns, :class, visibility, deep, skip)
    result.concat inner_get_methods(fqns, :instance, visibility, deep, skip)
    result.concat inner_get_methods('Kernel', :instance, visibility, deep, skip)
  else
    result.concat inner_get_methods(fqns, scope, visibility, deep, skip)
    result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
  end
  resolved = resolve_method_aliases(result, visibility)
  cache.set_methods(fqns, scope, visibility, deep, resolved)
  resolved
end

#get_path_pins(path) ⇒ Array<Pin::Base>

Get an array of pins that match the specified path.

Parameters:

  • path (String)

Returns:



344
345
346
# File 'lib/solargraph/api_map.rb', line 344

def get_path_pins path
  get_path_suggestions(path)
end

#get_path_suggestions(path) ⇒ Array<Solargraph::Pin::Base>

Deprecated.

Use #get_path_pins instead.

Get an array of all suggestions that match the specified path.

Parameters:

  • path (String)

    The path to find

Returns:



335
336
337
338
# File 'lib/solargraph/api_map.rb', line 335

def get_path_suggestions path
  return [] if path.nil?
  resolve_method_aliases store.get_path_pins(path)
end

#get_symbolsArray<Solargraph::Pin::Base>



232
233
234
# File 'lib/solargraph/api_map.rb', line 232

def get_symbols
  store.get_symbols
end

#implicitEnviron

Returns:



87
88
89
# File 'lib/solargraph/api_map.rb', line 87

def implicit
  @implicit ||= Environ.new
end

#index(pins) ⇒ self

Parameters:

Returns:

  • (self)


34
35
36
37
38
39
40
41
42
# File 'lib/solargraph/api_map.rb', line 34

def index pins
  # @todo This implementation is incomplete. It should probably create a
  #   Bench.
  @source_map_hash = {}
  implicit.clear
  cache.clear
  @store = Store.new(yard_map.pins + pins)
  self
end

#keyword_pinsEnumerable<Solargraph::Pin::Keyword>

An array of pins based on Ruby keywords (‘if`, `end`, etc.).

Returns:



142
143
144
# File 'lib/solargraph/api_map.rb', line 142

def keyword_pins
  store.pins_by_class(Pin::Keyword)
end

#locate_pins(location) ⇒ Array<Solargraph::Pin::Base>

Parameters:

Returns:



393
394
395
396
# File 'lib/solargraph/api_map.rb', line 393

def locate_pins location
  return [] if location.nil? || !source_map_hash.key?(location.filename)
  resolve_method_aliases source_map_hash[location.filename].locate_pins(location)
end

#map(source) ⇒ self

Map a single source.

Parameters:

Returns:

  • (self)


48
49
50
51
52
# File 'lib/solargraph/api_map.rb', line 48

def map source
  map = Solargraph::SourceMap.map(source)
  catalog Bench.new(source_maps: [map])
  self
end

#named_macro(name) ⇒ YARD::Tags::MacroDirective?

Parameters:

  • name (String)

Returns:

  • (YARD::Tags::MacroDirective, nil)


78
79
80
# File 'lib/solargraph/api_map.rb', line 78

def named_macro name
  store.named_macros[name]
end

#namespace_exists?(name, context = '') ⇒ Boolean

True if the namespace exists.

Parameters:

  • name (String)

    The namespace to match

  • context (String) (defaults to: '')

    The context to search

Returns:

  • (Boolean)


158
159
160
# File 'lib/solargraph/api_map.rb', line 158

def namespace_exists? name, context = ''
  !qualify(name, context).nil?
end

#namespacesSet<String>

An array of namespace names defined in the ApiMap.

Returns:

  • (Set<String>)


149
150
151
# File 'lib/solargraph/api_map.rb', line 149

def namespaces
  store.namespaces
end

#pinsArray<Solargraph::Pin::Base>



125
126
127
# File 'lib/solargraph/api_map.rb', line 125

def pins
  store.pins
end

#qualify(namespace, context = '') ⇒ String

Get a fully qualified namespace name. This method will start the search in the specified context until it finds a match for the name.

Parameters:

  • namespace (String, nil)

    The namespace to match

  • context (String) (defaults to: '')

    The context to search

Returns:

  • (String)


191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/solargraph/api_map.rb', line 191

def qualify namespace, context = ''
  return namespace if ['self', nil].include?(namespace)
  cached = cache.get_qualified_namespace(namespace, context)
  return cached.clone unless cached.nil?
  result = if namespace.start_with?('::')
             inner_qualify(namespace[2..-1], '', Set.new)
           else
             inner_qualify(namespace, context, Set.new)
           end
  cache.set_qualified_namespace(namespace, context, result)
  result
end

#query_symbols(query) ⇒ Array<Pin::Base>

Get an array of all symbols in the workspace that match the query.

Parameters:

  • query (String)

Returns:



385
386
387
388
389
# File 'lib/solargraph/api_map.rb', line 385

def query_symbols query
  result = []
  source_map_hash.each_value { |s| result.concat s.query_symbols(query) }
  result
end

#rebindable_method_namesObject



129
130
131
132
133
134
135
136
137
# File 'lib/solargraph/api_map.rb', line 129

def rebindable_method_names
  @rebindable_method_names ||= begin
    result = yard_map.rebindable_method_names
    source_maps.each do |map|
      result.merge map.rebindable_method_names
    end
    result
  end
end

#requiredObject



82
83
84
# File 'lib/solargraph/api_map.rb', line 82

def required
  @required ||= Set.new
end

#search(query) ⇒ Array<String>

Get a list of documented paths that match the query.

Examples:

api_map.query('str') # Results will include `String` and `Struct`

Parameters:

  • query (String)

    The text to match

Returns:



355
356
357
358
359
360
361
362
363
364
365
# File 'lib/solargraph/api_map.rb', line 355

def search query
  rake_yard(store)
  found = []
  code_object_paths.each do |k|
    if (found.empty? || (query.include?('.') || query.include?('#')) || !(k.include?('.') || k.include?('#'))) &&
       k.downcase.include?(query.downcase)
      found.push k
    end
  end
  found
end

#source_map(filename) ⇒ SourceMap

Get a source map by filename.

Parameters:

  • filename (String)

Returns:

Raises:



424
425
426
427
# File 'lib/solargraph/api_map.rb', line 424

def source_map filename
  raise FileNotFoundError, "Source map for `#{filename}` not found" unless source_map_hash.key?(filename)
  source_map_hash[filename]
end

#source_mapsArray<SourceMap>

Returns:



416
417
418
# File 'lib/solargraph/api_map.rb', line 416

def source_maps
  source_map_hash.values
end

#super_and_sub?(sup, sub) ⇒ Boolean

Check if a class is a superclass of another class.

Parameters:

  • sup (String)

    The superclass

  • sub (String)

    The subclass

Returns:

  • (Boolean)


442
443
444
445
446
447
448
449
450
# File 'lib/solargraph/api_map.rb', line 442

def super_and_sub?(sup, sub)
  fqsup = qualify(sup)
  cls = qualify(sub)
  until fqsup.nil? || cls.nil?
    return true if cls == fqsup
    cls = qualify_superclass(cls)
  end
  false
end

#type_include?(host, mod) ⇒ Boolean

Check if the host class includes the specified module.

Parameters:

  • host (String)

    The class

  • mod (String)

    The module

Returns:

  • (Boolean)


462
463
464
# File 'lib/solargraph/api_map.rb', line 462

def type_include?(host, mod)
  store.get_includes(host).include?(mod)
end

#yard_mapYardMap

Returns:



453
454
455
# File 'lib/solargraph/api_map.rb', line 453

def yard_map
  @yard_map ||= YardMap.new
end