solargraph 0.32.1 → 0.32.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +6 -0
- data/.travis.yml +25 -0
- data/EXAMPLES.md +76 -0
- data/Gemfile +3 -0
- data/LANGUAGE_SERVER.md +51 -0
- data/LICENSE +21 -0
- data/OVERVIEW.md +37 -0
- data/README.md +106 -0
- data/Rakefile +14 -0
- data/SERVER.md +95 -0
- data/bin/solargraph +0 -0
- data/bin/solargraph-runtime +5 -5
- data/lib/solargraph.rb +54 -54
- data/lib/solargraph/api_map.rb +659 -659
- data/lib/solargraph/api_map/cache.rb +49 -49
- data/lib/solargraph/api_map/source_to_yard.rb +67 -67
- data/lib/solargraph/api_map/store.rb +201 -201
- data/lib/solargraph/bundle.rb +24 -24
- data/lib/solargraph/complex_type.rb +150 -150
- data/lib/solargraph/complex_type/type_methods.rb +124 -124
- data/lib/solargraph/complex_type/unique_type.rb +44 -44
- data/lib/solargraph/core_fills.rb +37 -37
- data/lib/solargraph/diagnostics.rb +52 -52
- data/lib/solargraph/diagnostics/base.rb +20 -20
- data/lib/solargraph/diagnostics/require_not_found.rb +28 -28
- data/lib/solargraph/diagnostics/rubocop.rb +98 -98
- data/lib/solargraph/diagnostics/rubocop_helpers.rb +46 -46
- data/lib/solargraph/diagnostics/type_not_defined.rb +108 -108
- data/lib/solargraph/diagnostics/update_errors.rb +38 -38
- data/lib/solargraph/language_server/completion_item_kinds.rb +33 -33
- data/lib/solargraph/language_server/error_codes.rb +18 -18
- data/lib/solargraph/language_server/host.rb +684 -681
- data/lib/solargraph/language_server/host/cataloger.rb +54 -79
- data/lib/solargraph/language_server/host/diagnoser.rb +80 -80
- data/lib/solargraph/language_server/host/dispatch.rb +112 -113
- data/lib/solargraph/language_server/host/sources.rb +138 -138
- data/lib/solargraph/language_server/message.rb +90 -90
- data/lib/solargraph/language_server/message/base.rb +83 -83
- data/lib/solargraph/language_server/message/completion_item/resolve.rb +40 -40
- data/lib/solargraph/language_server/message/exit_notification.rb +11 -11
- data/lib/solargraph/language_server/message/extended.rb +19 -19
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +86 -86
- data/lib/solargraph/language_server/message/extended/document.rb +18 -18
- data/lib/solargraph/language_server/message/extended/document_gems.rb +30 -30
- data/lib/solargraph/language_server/message/extended/environment.rb +20 -20
- data/lib/solargraph/language_server/message/extended/search.rb +18 -18
- data/lib/solargraph/language_server/message/initialize.rb +141 -141
- data/lib/solargraph/language_server/message/initialized.rb +23 -23
- data/lib/solargraph/language_server/message/shutdown.rb +11 -11
- data/lib/solargraph/language_server/message/text_document.rb +25 -25
- data/lib/solargraph/language_server/message/text_document/completion.rb +51 -51
- data/lib/solargraph/language_server/message/text_document/definition.rb +18 -18
- data/lib/solargraph/language_server/message/text_document/did_change.rb +13 -13
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +21 -21
- data/lib/solargraph/language_server/message/text_document/folding_range.rb +24 -24
- data/lib/solargraph/language_server/message/text_document/formatting.rb +50 -50
- data/lib/solargraph/language_server/message/text_document/hover.rb +31 -31
- data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +32 -32
- data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +9 -9
- data/lib/solargraph/language_server/message/text_document/references.rb +14 -14
- data/lib/solargraph/language_server/message/text_document/rename.rb +17 -17
- data/lib/solargraph/language_server/message/text_document/signature_help.rb +19 -19
- data/lib/solargraph/language_server/message/workspace.rb +12 -12
- data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +29 -29
- data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +29 -27
- data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +24 -24
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +21 -21
- data/lib/solargraph/language_server/request.rb +22 -22
- data/lib/solargraph/language_server/symbol_kinds.rb +34 -34
- data/lib/solargraph/language_server/transport.rb +11 -11
- data/lib/solargraph/language_server/transport/adapter.rb +60 -60
- data/lib/solargraph/language_server/transport/data_reader.rb +66 -66
- data/lib/solargraph/language_server/uri_helpers.rb +25 -25
- data/lib/solargraph/library.rb +421 -419
- data/lib/solargraph/live_map.rb +126 -126
- data/lib/solargraph/live_map/cache.rb +38 -38
- data/lib/solargraph/location.rb +31 -31
- data/lib/solargraph/logging.rb +25 -25
- data/lib/solargraph/page.rb +68 -68
- data/lib/solargraph/pin.rb +50 -50
- data/lib/solargraph/pin/attribute.rb +41 -41
- data/lib/solargraph/pin/base.rb +280 -280
- data/lib/solargraph/pin/base_method.rb +76 -76
- data/lib/solargraph/pin/base_variable.rb +72 -72
- data/lib/solargraph/pin/block.rb +32 -32
- data/lib/solargraph/pin/block_parameter.rb +103 -103
- data/lib/solargraph/pin/class_variable.rb +9 -9
- data/lib/solargraph/pin/constant.rb +30 -30
- data/lib/solargraph/pin/conversions.rb +79 -79
- data/lib/solargraph/pin/documenting.rb +41 -41
- data/lib/solargraph/pin/duck_method.rb +14 -14
- data/lib/solargraph/pin/global_variable.rb +9 -9
- data/lib/solargraph/pin/instance_variable.rb +9 -9
- data/lib/solargraph/pin/keyword.rb +17 -17
- data/lib/solargraph/pin/local_variable.rb +23 -23
- data/lib/solargraph/pin/localized.rb +22 -22
- data/lib/solargraph/pin/method.rb +126 -126
- data/lib/solargraph/pin/method_alias.rb +30 -30
- data/lib/solargraph/pin/method_parameter.rb +40 -40
- data/lib/solargraph/pin/namespace.rb +54 -54
- data/lib/solargraph/pin/plugin/method.rb +25 -25
- data/lib/solargraph/pin/proxy_type.rb +35 -35
- data/lib/solargraph/pin/reference.rb +22 -22
- data/lib/solargraph/pin/reference/extend.rb +11 -11
- data/lib/solargraph/pin/reference/include.rb +11 -11
- data/lib/solargraph/pin/reference/require.rb +15 -15
- data/lib/solargraph/pin/reference/superclass.rb +11 -11
- data/lib/solargraph/pin/symbol.rb +44 -44
- data/lib/solargraph/pin/yard_pin.rb +10 -10
- data/lib/solargraph/pin/yard_pin/constant.rb +14 -14
- data/lib/solargraph/pin/yard_pin/method.rb +35 -35
- data/lib/solargraph/pin/yard_pin/namespace.rb +19 -19
- data/lib/solargraph/pin/yard_pin/yard_mixin.rb +14 -14
- data/lib/solargraph/plugin.rb +8 -8
- data/lib/solargraph/plugin/base.rb +41 -41
- data/lib/solargraph/plugin/canceler.rb +11 -11
- data/lib/solargraph/plugin/process.rb +172 -172
- data/lib/solargraph/plugin/runtime.rb +134 -134
- data/lib/solargraph/position.rb +110 -110
- data/lib/solargraph/range.rb +83 -83
- data/lib/solargraph/server_methods.rb +14 -14
- data/lib/solargraph/shell.rb +102 -102
- data/lib/solargraph/source.rb +521 -521
- data/lib/solargraph/source/chain.rb +120 -120
- data/lib/solargraph/source/chain/call.rb +107 -107
- data/lib/solargraph/source/chain/class_variable.rb +11 -11
- data/lib/solargraph/source/chain/constant.rb +30 -30
- data/lib/solargraph/source/chain/global_variable.rb +11 -11
- data/lib/solargraph/source/chain/head.rb +33 -33
- data/lib/solargraph/source/chain/instance_variable.rb +11 -11
- data/lib/solargraph/source/chain/link.rb +33 -33
- data/lib/solargraph/source/chain/literal.rb +21 -21
- data/lib/solargraph/source/chain/variable.rb +11 -11
- data/lib/solargraph/source/change.rb +77 -77
- data/lib/solargraph/source/cursor.rb +157 -157
- data/lib/solargraph/source/node_chainer.rb +96 -96
- data/lib/solargraph/source/node_methods.rb +225 -225
- data/lib/solargraph/source/source_chainer.rb +183 -183
- data/lib/solargraph/source_map.rb +169 -169
- data/lib/solargraph/source_map/clip.rb +145 -145
- data/lib/solargraph/source_map/completion.rb +21 -21
- data/lib/solargraph/source_map/mapper.rb +149 -149
- data/lib/solargraph/source_map/node_processor.rb +78 -78
- data/lib/solargraph/source_map/node_processor/alias_node.rb +19 -19
- data/lib/solargraph/source_map/node_processor/args_node.rb +28 -28
- data/lib/solargraph/source_map/node_processor/base.rb +68 -68
- data/lib/solargraph/source_map/node_processor/begin_node.rb +11 -11
- data/lib/solargraph/source_map/node_processor/block_node.rb +14 -14
- data/lib/solargraph/source_map/node_processor/casgn_node.rb +14 -14
- data/lib/solargraph/source_map/node_processor/cvasgn_node.rb +14 -14
- data/lib/solargraph/source_map/node_processor/def_node.rb +54 -54
- data/lib/solargraph/source_map/node_processor/defs_node.rb +21 -21
- data/lib/solargraph/source_map/node_processor/gvasgn_node.rb +12 -12
- data/lib/solargraph/source_map/node_processor/ivasgn_node.rb +18 -18
- data/lib/solargraph/source_map/node_processor/lvasgn_node.rb +16 -16
- data/lib/solargraph/source_map/node_processor/namespace_node.rb +26 -26
- data/lib/solargraph/source_map/node_processor/orasgn_node.rb +12 -12
- data/lib/solargraph/source_map/node_processor/sclass_node.rb +11 -11
- data/lib/solargraph/source_map/node_processor/send_node.rb +162 -162
- data/lib/solargraph/source_map/node_processor/sym_node.rb +11 -11
- data/lib/solargraph/source_map/region.rb +58 -58
- data/lib/solargraph/version.rb +3 -3
- data/lib/solargraph/views/environment.erb +53 -53
- data/lib/solargraph/workspace.rb +183 -183
- data/lib/solargraph/workspace/config.rb +170 -170
- data/lib/solargraph/yard_map.rb +298 -298
- data/lib/solargraph/yard_map/cache.rb +17 -17
- data/lib/solargraph/yard_map/core_docs.rb +163 -163
- data/lib/solargraph/yard_map/core_gen.rb +76 -76
- data/lib/yard-coregen.rb +16 -16
- data/lib/yard-solargraph.rb +18 -18
- data/solargraph.gemspec +37 -0
- data/travis-bundler.rb +10 -0
- metadata +19 -6
data/bin/solargraph
CHANGED
|
File without changes
|
data/bin/solargraph-runtime
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
require 'solargraph/plugin/process'
|
|
4
|
-
|
|
5
|
-
Solargraph::Plugin::Process.new.run
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'solargraph/plugin/process'
|
|
4
|
+
|
|
5
|
+
Solargraph::Plugin::Process.new.run
|
data/lib/solargraph.rb
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
require 'yard'
|
|
2
|
-
require 'solargraph/version'
|
|
3
|
-
require 'rubygems/package'
|
|
4
|
-
require 'yard-solargraph'
|
|
5
|
-
|
|
6
|
-
# The top-level namespace for the Solargraph code mapping, documentation,
|
|
7
|
-
# static analysis, and language server libraries.
|
|
8
|
-
#
|
|
9
|
-
module Solargraph
|
|
10
|
-
class InvalidOffsetError < RangeError; end
|
|
11
|
-
class DiagnosticsError < RuntimeError; end
|
|
12
|
-
class FileNotFoundError < RuntimeError; end
|
|
13
|
-
class SourceNotAvailableError < StandardError; end
|
|
14
|
-
class ComplexTypeError < StandardError; end
|
|
15
|
-
class WorkspaceTooLargeError < RuntimeError; end
|
|
16
|
-
|
|
17
|
-
autoload :Position, 'solargraph/position'
|
|
18
|
-
autoload :Range, 'solargraph/range'
|
|
19
|
-
autoload :Location, 'solargraph/location'
|
|
20
|
-
autoload :Shell, 'solargraph/shell'
|
|
21
|
-
autoload :Source, 'solargraph/source'
|
|
22
|
-
autoload :SourceMap, 'solargraph/source_map'
|
|
23
|
-
autoload :ApiMap, 'solargraph/api_map'
|
|
24
|
-
autoload :YardMap, 'solargraph/yard_map'
|
|
25
|
-
autoload :Pin, 'solargraph/pin'
|
|
26
|
-
autoload :LiveMap, 'solargraph/live_map'
|
|
27
|
-
autoload :ServerMethods, 'solargraph/server_methods'
|
|
28
|
-
autoload :Plugin, 'solargraph/plugin'
|
|
29
|
-
autoload :CoreFills, 'solargraph/core_fills'
|
|
30
|
-
autoload :LanguageServer, 'solargraph/language_server'
|
|
31
|
-
autoload :Workspace, 'solargraph/workspace'
|
|
32
|
-
autoload :Page, 'solargraph/page'
|
|
33
|
-
autoload :Library, 'solargraph/library'
|
|
34
|
-
autoload :Diagnostics, 'solargraph/diagnostics'
|
|
35
|
-
autoload :ComplexType, 'solargraph/complex_type'
|
|
36
|
-
autoload :Bundle, 'solargraph/bundle'
|
|
37
|
-
autoload :Logging, 'solargraph/logging'
|
|
38
|
-
|
|
39
|
-
dir = File.dirname(__FILE__)
|
|
40
|
-
YARDOC_PATH = File.realpath(File.join(dir, '..', 'yardoc'))
|
|
41
|
-
YARD_EXTENSION_FILE = File.join(dir, 'yard-solargraph.rb')
|
|
42
|
-
VIEWS_PATH = File.join(dir, 'solargraph', 'views')
|
|
43
|
-
|
|
44
|
-
# A convenience method for Solargraph::Logging.logger.
|
|
45
|
-
#
|
|
46
|
-
# @return [Logger]
|
|
47
|
-
def self.logger
|
|
48
|
-
Solargraph::Logging.logger
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
Solargraph::YardMap::CoreDocs.require_minimum
|
|
53
|
-
# Change YARD log IO to avoid sending unexpected messages to STDOUT
|
|
54
|
-
YARD::Logger.instance.io = File.new(File::NULL, 'w')
|
|
1
|
+
require 'yard'
|
|
2
|
+
require 'solargraph/version'
|
|
3
|
+
require 'rubygems/package'
|
|
4
|
+
require 'yard-solargraph'
|
|
5
|
+
|
|
6
|
+
# The top-level namespace for the Solargraph code mapping, documentation,
|
|
7
|
+
# static analysis, and language server libraries.
|
|
8
|
+
#
|
|
9
|
+
module Solargraph
|
|
10
|
+
class InvalidOffsetError < RangeError; end
|
|
11
|
+
class DiagnosticsError < RuntimeError; end
|
|
12
|
+
class FileNotFoundError < RuntimeError; end
|
|
13
|
+
class SourceNotAvailableError < StandardError; end
|
|
14
|
+
class ComplexTypeError < StandardError; end
|
|
15
|
+
class WorkspaceTooLargeError < RuntimeError; end
|
|
16
|
+
|
|
17
|
+
autoload :Position, 'solargraph/position'
|
|
18
|
+
autoload :Range, 'solargraph/range'
|
|
19
|
+
autoload :Location, 'solargraph/location'
|
|
20
|
+
autoload :Shell, 'solargraph/shell'
|
|
21
|
+
autoload :Source, 'solargraph/source'
|
|
22
|
+
autoload :SourceMap, 'solargraph/source_map'
|
|
23
|
+
autoload :ApiMap, 'solargraph/api_map'
|
|
24
|
+
autoload :YardMap, 'solargraph/yard_map'
|
|
25
|
+
autoload :Pin, 'solargraph/pin'
|
|
26
|
+
autoload :LiveMap, 'solargraph/live_map'
|
|
27
|
+
autoload :ServerMethods, 'solargraph/server_methods'
|
|
28
|
+
autoload :Plugin, 'solargraph/plugin'
|
|
29
|
+
autoload :CoreFills, 'solargraph/core_fills'
|
|
30
|
+
autoload :LanguageServer, 'solargraph/language_server'
|
|
31
|
+
autoload :Workspace, 'solargraph/workspace'
|
|
32
|
+
autoload :Page, 'solargraph/page'
|
|
33
|
+
autoload :Library, 'solargraph/library'
|
|
34
|
+
autoload :Diagnostics, 'solargraph/diagnostics'
|
|
35
|
+
autoload :ComplexType, 'solargraph/complex_type'
|
|
36
|
+
autoload :Bundle, 'solargraph/bundle'
|
|
37
|
+
autoload :Logging, 'solargraph/logging'
|
|
38
|
+
|
|
39
|
+
dir = File.dirname(__FILE__)
|
|
40
|
+
YARDOC_PATH = File.realpath(File.join(dir, '..', 'yardoc'))
|
|
41
|
+
YARD_EXTENSION_FILE = File.join(dir, 'yard-solargraph.rb')
|
|
42
|
+
VIEWS_PATH = File.join(dir, 'solargraph', 'views')
|
|
43
|
+
|
|
44
|
+
# A convenience method for Solargraph::Logging.logger.
|
|
45
|
+
#
|
|
46
|
+
# @return [Logger]
|
|
47
|
+
def self.logger
|
|
48
|
+
Solargraph::Logging.logger
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
Solargraph::YardMap::CoreDocs.require_minimum
|
|
53
|
+
# Change YARD log IO to avoid sending unexpected messages to STDOUT
|
|
54
|
+
YARD::Logger.instance.io = File.new(File::NULL, 'w')
|
data/lib/solargraph/api_map.rb
CHANGED
|
@@ -1,659 +1,659 @@
|
|
|
1
|
-
require 'rubygems'
|
|
2
|
-
require 'set'
|
|
3
|
-
require 'pathname'
|
|
4
|
-
|
|
5
|
-
module Solargraph
|
|
6
|
-
# An aggregate provider for information about workspaces, sources, gems, and
|
|
7
|
-
# the Ruby core.
|
|
8
|
-
#
|
|
9
|
-
class ApiMap
|
|
10
|
-
autoload :Cache, 'solargraph/api_map/cache'
|
|
11
|
-
autoload :SourceToYard, 'solargraph/api_map/source_to_yard'
|
|
12
|
-
autoload :Store, 'solargraph/api_map/store'
|
|
13
|
-
|
|
14
|
-
include SourceToYard
|
|
15
|
-
|
|
16
|
-
# Get a LiveMap associated with the current workspace.
|
|
17
|
-
#
|
|
18
|
-
# @return [Solargraph::LiveMap]
|
|
19
|
-
attr_reader :live_map
|
|
20
|
-
|
|
21
|
-
# @return [Array<String>]
|
|
22
|
-
attr_reader :unresolved_requires
|
|
23
|
-
|
|
24
|
-
# @param pins [Array<Solargraph::Pin::Base>]
|
|
25
|
-
def initialize pins: []
|
|
26
|
-
# @todo Extensions don't work yet
|
|
27
|
-
# require_extensions
|
|
28
|
-
@source_map_hash = {}
|
|
29
|
-
@cache = Cache.new
|
|
30
|
-
@mutex = Mutex.new
|
|
31
|
-
index pins
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# @param pins [Array<Pin::Base>]
|
|
35
|
-
# @return [self]
|
|
36
|
-
def index pins
|
|
37
|
-
@mutex.synchronize {
|
|
38
|
-
@source_map_hash.clear
|
|
39
|
-
@cache.clear
|
|
40
|
-
@store = Store.new(pins + YardMap.new.pins)
|
|
41
|
-
@unresolved_requires = []
|
|
42
|
-
}
|
|
43
|
-
# resolve_method_aliases
|
|
44
|
-
self
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Map a single source.
|
|
48
|
-
#
|
|
49
|
-
# @param source [Source]
|
|
50
|
-
# @return [self]
|
|
51
|
-
def map source
|
|
52
|
-
catalog Bundle.new(opened: [source])
|
|
53
|
-
self
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def named_macro name
|
|
57
|
-
store.named_macros[name]
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Catalog a bundle.
|
|
61
|
-
#
|
|
62
|
-
# @param bundle [Bundle]
|
|
63
|
-
# @return [self]
|
|
64
|
-
def catalog bundle
|
|
65
|
-
new_map_hash = {}
|
|
66
|
-
# Bundle always needs to be merged if it adds or removes sources
|
|
67
|
-
merged = (bundle.sources.length == source_map_hash.values.length)
|
|
68
|
-
bundle.sources.each do |source|
|
|
69
|
-
if source_map_hash.key?(source.filename)
|
|
70
|
-
if source_map_hash[source.filename].code == source.code && source_map_hash[source.filename].source.synchronized? && source.synchronized?
|
|
71
|
-
new_map_hash[source.filename] = source_map_hash[source.filename]
|
|
72
|
-
elsif !source.synchronized?
|
|
73
|
-
new_map_hash[source.filename] = source_map_hash[source.filename]
|
|
74
|
-
# @todo Smelly instance variable access
|
|
75
|
-
new_map_hash[source.filename].instance_variable_set(:@source, source)
|
|
76
|
-
else
|
|
77
|
-
map = Solargraph::SourceMap.map(source)
|
|
78
|
-
if source_map_hash[source.filename].try_merge!(map)
|
|
79
|
-
new_map_hash[source.filename] = source_map_hash[source.filename]
|
|
80
|
-
else
|
|
81
|
-
new_map_hash[source.filename] = map
|
|
82
|
-
merged = false
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
else
|
|
86
|
-
map = Solargraph::SourceMap.map(source)
|
|
87
|
-
new_map_hash[source.filename] = map
|
|
88
|
-
merged = false
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
return self if merged
|
|
92
|
-
pins = []
|
|
93
|
-
reqs = []
|
|
94
|
-
# @param map [SourceMap]
|
|
95
|
-
new_map_hash.values.each do |map|
|
|
96
|
-
pins.concat map.pins
|
|
97
|
-
reqs.concat map.requires.map(&:name)
|
|
98
|
-
end
|
|
99
|
-
reqs.concat bundle.workspace.config.required
|
|
100
|
-
unless bundle.workspace.require_paths.empty?
|
|
101
|
-
reqs.delete_if do |r|
|
|
102
|
-
result = false
|
|
103
|
-
bundle.workspace.require_paths.each do |l|
|
|
104
|
-
pn = Pathname.new(bundle.workspace.directory).join(l, "#{r}.rb")
|
|
105
|
-
if new_map_hash.keys.include?(pn.to_s)
|
|
106
|
-
result = true
|
|
107
|
-
break
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
result
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
yard_map.change(reqs)
|
|
114
|
-
new_store = Store.new(pins + yard_map.pins)
|
|
115
|
-
@mutex.synchronize {
|
|
116
|
-
@cache.clear
|
|
117
|
-
@source_map_hash = new_map_hash
|
|
118
|
-
@store = new_store
|
|
119
|
-
@unresolved_requires = yard_map.unresolved_requires
|
|
120
|
-
}
|
|
121
|
-
# resolve_method_aliases
|
|
122
|
-
self
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# @param filename [String]
|
|
126
|
-
# @param position [Position, Array(Integer, Integer)]
|
|
127
|
-
# @return [Source::Cursor]
|
|
128
|
-
def cursor_at filename, position
|
|
129
|
-
position = Position.normalize(position)
|
|
130
|
-
raise "File not found: #{filename}" unless source_map_hash.has_key?(filename)
|
|
131
|
-
source_map_hash[filename].cursor_at(position)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# Get a clip by filename and position.
|
|
135
|
-
#
|
|
136
|
-
# @param filename [String]
|
|
137
|
-
# @param position [Position, Array(Integer, Integer)]
|
|
138
|
-
# @return [SourceMap::Clip]
|
|
139
|
-
def clip_at filename, position
|
|
140
|
-
position = Position.normalize(position)
|
|
141
|
-
SourceMap::Clip.new(self, cursor_at(filename, position))
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# Create an ApiMap with a workspace in the specified directory.
|
|
145
|
-
#
|
|
146
|
-
# @param directory [String]
|
|
147
|
-
# @return [ApiMap]
|
|
148
|
-
def self.load directory
|
|
149
|
-
api_map = self.new
|
|
150
|
-
workspace = Solargraph::Workspace.new(directory)
|
|
151
|
-
api_map.catalog Bundle.new(workspace: workspace)
|
|
152
|
-
api_map
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
# @return [Array<Solargraph::Pin::Base>]
|
|
156
|
-
def pins
|
|
157
|
-
store.pins
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# An array of pins based on Ruby keywords (`if`, `end`, etc.).
|
|
161
|
-
#
|
|
162
|
-
# @return [Array<Solargraph::Pin::Keyword>]
|
|
163
|
-
def self.keywords
|
|
164
|
-
@keywords ||= CoreFills::KEYWORDS.map{ |s|
|
|
165
|
-
Pin::Keyword.new(s)
|
|
166
|
-
}.freeze
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
# An array of namespace names defined in the ApiMap.
|
|
170
|
-
#
|
|
171
|
-
# @return [Array<String>]
|
|
172
|
-
def namespaces
|
|
173
|
-
store.namespaces
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# True if the namespace exists.
|
|
177
|
-
#
|
|
178
|
-
# @param name [String] The namespace to match
|
|
179
|
-
# @param context [String] The context to search
|
|
180
|
-
# @return [Boolean]
|
|
181
|
-
def namespace_exists? name, context = ''
|
|
182
|
-
!qualify(name, context).nil?
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# Get suggestions for constants in the specified namespace. The result
|
|
186
|
-
# may contain both constant and namespace pins.
|
|
187
|
-
#
|
|
188
|
-
# @param namespace [String] The namespace
|
|
189
|
-
# @param context [String] The context
|
|
190
|
-
# @return [Array<Solargraph::Pin::Base>]
|
|
191
|
-
def get_constants namespace, context = ''
|
|
192
|
-
namespace ||= ''
|
|
193
|
-
cached = cache.get_constants(namespace, context)
|
|
194
|
-
return cached.clone unless cached.nil?
|
|
195
|
-
skip = []
|
|
196
|
-
result = []
|
|
197
|
-
bases = context.split('::')
|
|
198
|
-
while bases.length > 0
|
|
199
|
-
built = bases.join('::')
|
|
200
|
-
fqns = qualify(namespace, built)
|
|
201
|
-
visibility = [:public]
|
|
202
|
-
visibility.push :private if fqns == context
|
|
203
|
-
result.concat inner_get_constants(fqns, visibility, skip)
|
|
204
|
-
bases.pop
|
|
205
|
-
end
|
|
206
|
-
fqns = qualify(namespace, '')
|
|
207
|
-
visibility = [:public]
|
|
208
|
-
visibility.push :private if fqns == context
|
|
209
|
-
result.concat inner_get_constants(fqns, visibility, skip)
|
|
210
|
-
cache.set_constants(namespace, context, result)
|
|
211
|
-
result
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
# Get a fully qualified namespace name. This method will start the search
|
|
215
|
-
# in the specified context until it finds a match for the name.
|
|
216
|
-
#
|
|
217
|
-
# @param namespace [String, nil] The namespace to match
|
|
218
|
-
# @param context [String] The context to search
|
|
219
|
-
# @return [String]
|
|
220
|
-
def qualify namespace, context = ''
|
|
221
|
-
# @todo The return for self might work better elsewhere
|
|
222
|
-
return nil if namespace.nil?
|
|
223
|
-
return qualify(context) if namespace == 'self'
|
|
224
|
-
cached = cache.get_qualified_namespace(namespace, context)
|
|
225
|
-
return cached.clone unless cached.nil?
|
|
226
|
-
result = if namespace.start_with?('::')
|
|
227
|
-
inner_qualify(namespace[2..-1], '', [])
|
|
228
|
-
else
|
|
229
|
-
inner_qualify(namespace, context, [])
|
|
230
|
-
end
|
|
231
|
-
cache.set_qualified_namespace(namespace, context, result)
|
|
232
|
-
result
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
# Get an array of instance variable pins defined in specified namespace
|
|
236
|
-
# and scope.
|
|
237
|
-
#
|
|
238
|
-
# @param namespace [String] A fully qualified namespace
|
|
239
|
-
# @param scope [Symbol] :instance or :class
|
|
240
|
-
# @return [Array<Solargraph::Pin::InstanceVariable>]
|
|
241
|
-
def get_instance_variable_pins(namespace, scope = :instance)
|
|
242
|
-
result = []
|
|
243
|
-
result.concat store.get_instance_variables(namespace, scope)
|
|
244
|
-
sc = qualify(store.get_superclass(namespace), namespace)
|
|
245
|
-
until sc.nil?
|
|
246
|
-
result.concat store.get_instance_variables(sc, scope)
|
|
247
|
-
sc = qualify(store.get_superclass(sc), sc)
|
|
248
|
-
end
|
|
249
|
-
result
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
# Get an array of class variable pins for a namespace.
|
|
253
|
-
#
|
|
254
|
-
# @param namespace [String] A fully qualified namespace
|
|
255
|
-
# @return [Array<Solargraph::Pin::ClassVariable>]
|
|
256
|
-
def get_class_variable_pins(namespace)
|
|
257
|
-
prefer_non_nil_variables(store.get_class_variables(namespace))
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
# @return [Array<Solargraph::Pin::Base>]
|
|
261
|
-
def get_symbols
|
|
262
|
-
store.get_symbols
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
# @return [Array<Solargraph::Pin::GlobalVariable>]
|
|
266
|
-
def get_global_variable_pins
|
|
267
|
-
# @todo Slow version
|
|
268
|
-
pins.select{|p| p.kind == Pin::GLOBAL_VARIABLE}
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
# Get an array of methods available in a particular context.
|
|
272
|
-
#
|
|
273
|
-
# @param fqns [String] The fully qualified namespace to search for methods
|
|
274
|
-
# @param scope [Symbol] :class or :instance
|
|
275
|
-
# @param visibility [Array<Symbol>] :public, :protected, and/or :private
|
|
276
|
-
# @param deep [Boolean] True to include superclasses, mixins, etc.
|
|
277
|
-
# @return [Array<Solargraph::Pin::Base>]
|
|
278
|
-
def get_methods fqns, scope: :instance, visibility: [:public], deep: true
|
|
279
|
-
cached = cache.get_methods(fqns, scope, visibility, deep)
|
|
280
|
-
return cached.clone unless cached.nil?
|
|
281
|
-
result = []
|
|
282
|
-
skip = []
|
|
283
|
-
if fqns == ''
|
|
284
|
-
# @todo Implement domains
|
|
285
|
-
# domains.each do |domain|
|
|
286
|
-
# type = ComplexType.parse(domain).first
|
|
287
|
-
# result.concat inner_get_methods(type.name, type.scope, [:public], deep, skip)
|
|
288
|
-
# end
|
|
289
|
-
result.concat inner_get_methods(fqns, :class, visibility, deep, skip)
|
|
290
|
-
result.concat inner_get_methods(fqns, :instance, visibility, deep, skip)
|
|
291
|
-
result.concat inner_get_methods('Kernel', :instance, visibility, deep, skip)
|
|
292
|
-
else
|
|
293
|
-
result.concat inner_get_methods(fqns, scope, visibility, deep, skip)
|
|
294
|
-
end
|
|
295
|
-
# live = live_map.get_methods(fqns, '', scope.to_s, visibility.include?(:private))
|
|
296
|
-
# unless live.empty?
|
|
297
|
-
# exist = result.map(&:name)
|
|
298
|
-
# result.concat live.reject{|p| exist.include?(p.name)}
|
|
299
|
-
# end
|
|
300
|
-
resolved = resolve_method_aliases(result)
|
|
301
|
-
cache.set_methods(fqns, scope, visibility, deep, resolved)
|
|
302
|
-
resolved
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
# Get an array of method pins for a complex type.
|
|
306
|
-
#
|
|
307
|
-
# The type's namespace and the context should be fully qualified. If the
|
|
308
|
-
# context matches the namespace type or is a subclass of the type,
|
|
309
|
-
# protected methods are included in the results. If protected methods are
|
|
310
|
-
# included and internal is true, private methods are also included.
|
|
311
|
-
#
|
|
312
|
-
# @example
|
|
313
|
-
# api_map = Solargraph::ApiMap.new
|
|
314
|
-
# type = Solargraph::ComplexType.parse('String')
|
|
315
|
-
# api_map.get_complex_type_methods(type)
|
|
316
|
-
#
|
|
317
|
-
# @param type [Solargraph::ComplexType] The complex type of the namespace
|
|
318
|
-
# @param context [String] The context from which the type is referenced
|
|
319
|
-
# @param internal [Boolean] True to include private methods
|
|
320
|
-
# @return [Array<Solargraph::Pin::Base>]
|
|
321
|
-
def get_complex_type_methods type, context = '', internal = false
|
|
322
|
-
# This method does not qualify the complex type's namespace because
|
|
323
|
-
# it can cause conflicts between similar names, e.g., `Foo` vs.
|
|
324
|
-
# `Other::Foo`. It still takes a context argument to determine whether
|
|
325
|
-
# protected and private methods are visible.
|
|
326
|
-
return [] if type.undefined? || type.void?
|
|
327
|
-
result = []
|
|
328
|
-
if type.duck_type?
|
|
329
|
-
type.select(&:duck_type?).each do |t|
|
|
330
|
-
result.push Pin::DuckMethod.new(nil, t.tag[1..-1])
|
|
331
|
-
end
|
|
332
|
-
result.concat get_methods('Object')
|
|
333
|
-
else
|
|
334
|
-
unless type.nil? || type.name == 'void'
|
|
335
|
-
visibility = [:public]
|
|
336
|
-
if type.namespace == context || super_and_sub?(type.namespace, context)
|
|
337
|
-
visibility.push :protected
|
|
338
|
-
visibility.push :private if internal
|
|
339
|
-
end
|
|
340
|
-
result.concat get_methods(type.namespace, scope: type.scope, visibility: visibility)
|
|
341
|
-
end
|
|
342
|
-
end
|
|
343
|
-
result
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
# Get a stack of method pins for a method name in a namespace. The order
|
|
347
|
-
# of the pins corresponds to the ancestry chain, with highest precedence
|
|
348
|
-
# first.
|
|
349
|
-
#
|
|
350
|
-
# @example
|
|
351
|
-
# api_map.get_method_stack('Subclass', 'method_name')
|
|
352
|
-
# #=> [ <Subclass#method_name pin>, <Superclass#method_name pin> ]
|
|
353
|
-
#
|
|
354
|
-
# @param fqns [String]
|
|
355
|
-
# @param name [String]
|
|
356
|
-
# @param scope [Symbol] :instance or :class
|
|
357
|
-
# @return [Array<Solargraph::Pin::Base>]
|
|
358
|
-
def get_method_stack fqns, name, scope: :instance
|
|
359
|
-
get_methods(fqns, scope: scope, visibility: [:private, :protected, :public]).select{|p| p.name == name}
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
# Get an array of all suggestions that match the specified path.
|
|
363
|
-
#
|
|
364
|
-
# @deprecated Use #get_path_pins instead.
|
|
365
|
-
#
|
|
366
|
-
# @param path [String] The path to find
|
|
367
|
-
# @return [Array<Solargraph::Pin::Base>]
|
|
368
|
-
def get_path_suggestions path
|
|
369
|
-
return [] if path.nil?
|
|
370
|
-
result = []
|
|
371
|
-
result.concat store.get_path_pins(path)
|
|
372
|
-
# if result.empty?
|
|
373
|
-
# lp = live_map.get_path_pin(path)
|
|
374
|
-
# result.push lp unless lp.nil?
|
|
375
|
-
# end
|
|
376
|
-
resolve_method_aliases(result)
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
# Get an array of pins that match the specified path.
|
|
380
|
-
#
|
|
381
|
-
# @param path [String]
|
|
382
|
-
# @return [Array<Pin::Base>]
|
|
383
|
-
def get_path_pins path
|
|
384
|
-
get_path_suggestions(path)
|
|
385
|
-
end
|
|
386
|
-
|
|
387
|
-
# Get a list of documented paths that match the query.
|
|
388
|
-
#
|
|
389
|
-
# @example
|
|
390
|
-
# api_map.query('str') # Results will include `String` and `Struct`
|
|
391
|
-
#
|
|
392
|
-
# @param query [String] The text to match
|
|
393
|
-
# @return [Array<String>]
|
|
394
|
-
def search query
|
|
395
|
-
rake_yard(store)
|
|
396
|
-
found = []
|
|
397
|
-
code_object_paths.each do |k|
|
|
398
|
-
if found.empty? || (query.include?('.') || query.include?('#')) || !(k.include?('.') || k.include?('#'))
|
|
399
|
-
found.push k if k.downcase.include?(query.downcase)
|
|
400
|
-
end
|
|
401
|
-
end
|
|
402
|
-
found
|
|
403
|
-
end
|
|
404
|
-
|
|
405
|
-
# Get YARD documentation for the specified path.
|
|
406
|
-
#
|
|
407
|
-
# @example
|
|
408
|
-
# api_map.document('String#split')
|
|
409
|
-
#
|
|
410
|
-
# @param path [String] The path to find
|
|
411
|
-
# @return [Array<YARD::CodeObject::Base>]
|
|
412
|
-
def document path
|
|
413
|
-
rake_yard(store)
|
|
414
|
-
docs = []
|
|
415
|
-
docs.push code_object_at(path) unless code_object_at(path).nil?
|
|
416
|
-
docs
|
|
417
|
-
end
|
|
418
|
-
|
|
419
|
-
# Get an array of all symbols in the workspace that match the query.
|
|
420
|
-
#
|
|
421
|
-
# @param query [String]
|
|
422
|
-
# @return [Array<Pin::Base>]
|
|
423
|
-
def query_symbols query
|
|
424
|
-
result = []
|
|
425
|
-
source_map_hash.values.each do |s|
|
|
426
|
-
result.concat s.query_symbols(query)
|
|
427
|
-
end
|
|
428
|
-
result
|
|
429
|
-
end
|
|
430
|
-
|
|
431
|
-
# @param location [Solargraph::Location]
|
|
432
|
-
# @return [Array<Solargraph::Pin::Base>]
|
|
433
|
-
def locate_pins location
|
|
434
|
-
return [] if location.nil? || !source_map_hash.has_key?(location.filename)
|
|
435
|
-
source_map_hash[location.filename].locate_pins(location)
|
|
436
|
-
end
|
|
437
|
-
|
|
438
|
-
# @raise [FileNotFoundError] if the cursor's file is not in the ApiMap
|
|
439
|
-
# @param cursor [Source::Cursor]
|
|
440
|
-
# @return [SourceMap::Clip]
|
|
441
|
-
def clip cursor
|
|
442
|
-
raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename}" unless source_map_hash.has_key?(cursor.filename)
|
|
443
|
-
SourceMap::Clip.new(self, cursor)
|
|
444
|
-
end
|
|
445
|
-
|
|
446
|
-
# Get an array of document symbols from a file.
|
|
447
|
-
#
|
|
448
|
-
# @param filename [String]
|
|
449
|
-
# @return [Array<Pin::Symbol>]
|
|
450
|
-
def document_symbols filename
|
|
451
|
-
return [] unless source_map_hash.has_key?(filename) # @todo Raise error?
|
|
452
|
-
source_map_hash[filename].document_symbols
|
|
453
|
-
end
|
|
454
|
-
|
|
455
|
-
# Get a source map by filename.
|
|
456
|
-
#
|
|
457
|
-
# @param filename [String]
|
|
458
|
-
# @return [SourceMap]
|
|
459
|
-
def source_map filename
|
|
460
|
-
raise FileNotFoundError, "Source map for `#{filename}` not found" unless source_map_hash.has_key?(filename)
|
|
461
|
-
source_map_hash[filename]
|
|
462
|
-
end
|
|
463
|
-
|
|
464
|
-
private
|
|
465
|
-
|
|
466
|
-
# @return [YardMap]
|
|
467
|
-
def yard_map
|
|
468
|
-
@yard_map ||= YardMap.new
|
|
469
|
-
end
|
|
470
|
-
|
|
471
|
-
# A hash of source maps with filename keys.
|
|
472
|
-
#
|
|
473
|
-
# @return [Hash{String => SourceMap}]
|
|
474
|
-
def source_map_hash
|
|
475
|
-
@mutex.synchronize { @source_map_hash }
|
|
476
|
-
end
|
|
477
|
-
|
|
478
|
-
# @return [ApiMap::Store]
|
|
479
|
-
def store
|
|
480
|
-
@mutex.synchronize { @store }
|
|
481
|
-
end
|
|
482
|
-
|
|
483
|
-
# @return [Solargraph::ApiMap::Cache]
|
|
484
|
-
def cache
|
|
485
|
-
@mutex.synchronize { @cache }
|
|
486
|
-
end
|
|
487
|
-
|
|
488
|
-
# @param fqns [String] A fully qualified namespace
|
|
489
|
-
# @param scope [Symbol] :class or :instance
|
|
490
|
-
# @param visibility [Array<Symbol>] :public, :protected, and/or :private
|
|
491
|
-
# @param deep [Boolean]
|
|
492
|
-
# @param skip [Array<String>]
|
|
493
|
-
# @param no_core [Boolean] Skip core classes if true
|
|
494
|
-
# @return [Array<Pin::Base>]
|
|
495
|
-
def inner_get_methods fqns, scope, visibility, deep, skip, no_core = false
|
|
496
|
-
return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module|Kernel)$/
|
|
497
|
-
reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
|
|
498
|
-
return [] if skip.include?(reqstr)
|
|
499
|
-
skip.push reqstr
|
|
500
|
-
result = []
|
|
501
|
-
result.concat store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
|
|
502
|
-
if deep
|
|
503
|
-
sc = store.get_superclass(fqns)
|
|
504
|
-
unless sc.nil?
|
|
505
|
-
fqsc = qualify(sc, fqns.split('::')[0..-2].join('::'))
|
|
506
|
-
result.concat inner_get_methods(fqsc, scope, visibility, true, skip, true) unless fqsc.nil?
|
|
507
|
-
end
|
|
508
|
-
if scope == :instance
|
|
509
|
-
store.get_includes(fqns).reverse.each do |im|
|
|
510
|
-
fqim = qualify(im, fqns)
|
|
511
|
-
result.concat inner_get_methods(fqim, scope, visibility, deep, skip, true) unless fqim.nil?
|
|
512
|
-
end
|
|
513
|
-
result.concat inner_get_methods('Object', :instance, [:public], deep, skip, no_core)
|
|
514
|
-
result.concat inner_get_methods('BasicObject', :instance, [:public], deep, skip, no_core)
|
|
515
|
-
else
|
|
516
|
-
store.get_extends(fqns).reverse.each do |em|
|
|
517
|
-
fqem = qualify(em, fqns)
|
|
518
|
-
result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
|
|
519
|
-
end
|
|
520
|
-
unless no_core || fqns.empty?
|
|
521
|
-
type = get_namespace_type(fqns)
|
|
522
|
-
result.concat inner_get_methods('Class', :instance, visibility, deep, skip, no_core) if type == :class
|
|
523
|
-
result.concat inner_get_methods('Module', :instance,visibility, deep, skip, no_core)
|
|
524
|
-
end
|
|
525
|
-
end
|
|
526
|
-
store.domains(fqns).each do |d|
|
|
527
|
-
dt = ComplexType.try_parse(d)
|
|
528
|
-
result.concat inner_get_methods(dt.namespace, dt.scope, [:public], deep, skip)
|
|
529
|
-
end
|
|
530
|
-
end
|
|
531
|
-
result
|
|
532
|
-
end
|
|
533
|
-
|
|
534
|
-
# @param fqns [String]
|
|
535
|
-
# @param visibility [Array<Symbol>]
|
|
536
|
-
# @param skip [Array<String>]
|
|
537
|
-
# @return [Array<Pin::Base>]
|
|
538
|
-
def inner_get_constants fqns, visibility, skip
|
|
539
|
-
return [] if skip.include?(fqns)
|
|
540
|
-
skip.push fqns
|
|
541
|
-
result = []
|
|
542
|
-
result.concat store.get_constants(fqns, visibility).sort{ |a, b| a.name <=> b.name }
|
|
543
|
-
store.get_includes(fqns).each do |is|
|
|
544
|
-
fqis = qualify(is, fqns)
|
|
545
|
-
result.concat inner_get_constants(fqis, [:public], skip) unless fqis.nil?
|
|
546
|
-
end
|
|
547
|
-
# result.concat live_map.get_constants(fqns)
|
|
548
|
-
result
|
|
549
|
-
end
|
|
550
|
-
|
|
551
|
-
# Require extensions for the experimental plugin architecture. Any
|
|
552
|
-
# installed gem with a name that starts with "solargraph-" is considered
|
|
553
|
-
# an extension.
|
|
554
|
-
#
|
|
555
|
-
# @return [void]
|
|
556
|
-
def require_extensions
|
|
557
|
-
Gem::Specification.all_names.select{|n| n.match(/^solargraph\-[a-z0-9_\-]*?\-ext\-[0-9\.]*$/)}.each do |n|
|
|
558
|
-
Solargraph::Logging.logger.info "Loading extension #{n}"
|
|
559
|
-
require n.match(/^(solargraph\-[a-z0-9_\-]*?\-ext)\-[0-9\.]*$/)[1]
|
|
560
|
-
end
|
|
561
|
-
end
|
|
562
|
-
|
|
563
|
-
# @return [Hash]
|
|
564
|
-
def path_macros
|
|
565
|
-
@path_macros ||= {}
|
|
566
|
-
end
|
|
567
|
-
|
|
568
|
-
# @param name [String]
|
|
569
|
-
# @param root [String]
|
|
570
|
-
# @param skip [Array<String>]
|
|
571
|
-
# @return [String]
|
|
572
|
-
def inner_qualify name, root, skip
|
|
573
|
-
return nil if name.nil?
|
|
574
|
-
return nil if skip.include?(root)
|
|
575
|
-
skip.push root
|
|
576
|
-
if name == ''
|
|
577
|
-
if root == ''
|
|
578
|
-
return ''
|
|
579
|
-
else
|
|
580
|
-
return inner_qualify(root, '', skip)
|
|
581
|
-
end
|
|
582
|
-
else
|
|
583
|
-
return name if root == '' && store.namespace_exists?(name)
|
|
584
|
-
roots = root.to_s.split('::')
|
|
585
|
-
while roots.length > 0
|
|
586
|
-
fqns = roots.join('::') + '::' + name
|
|
587
|
-
return fqns if store.namespace_exists?(fqns)
|
|
588
|
-
incs = store.get_includes(roots.join('::'))
|
|
589
|
-
incs.each do |inc|
|
|
590
|
-
foundinc = inner_qualify(name, inc, skip)
|
|
591
|
-
return foundinc unless foundinc.nil?
|
|
592
|
-
end
|
|
593
|
-
roots.pop
|
|
594
|
-
end
|
|
595
|
-
incs = store.get_includes('')
|
|
596
|
-
incs.each do |inc|
|
|
597
|
-
foundinc = inner_qualify(name, inc, skip)
|
|
598
|
-
return foundinc unless foundinc.nil?
|
|
599
|
-
end
|
|
600
|
-
return name if store.namespace_exists?(name)
|
|
601
|
-
end
|
|
602
|
-
# live_map.get_fqns(name, root)
|
|
603
|
-
end
|
|
604
|
-
|
|
605
|
-
# Get the namespace's type (Class or Module).
|
|
606
|
-
#
|
|
607
|
-
# @param fqns [String] A fully qualified namespace
|
|
608
|
-
# @return [Symbol] :class, :module, or nil
|
|
609
|
-
def get_namespace_type fqns
|
|
610
|
-
return nil if fqns.nil?
|
|
611
|
-
pin = store.get_path_pins(fqns).first
|
|
612
|
-
return nil if pin.nil?
|
|
613
|
-
pin.type
|
|
614
|
-
end
|
|
615
|
-
|
|
616
|
-
# Sort an array of pins to put nil or undefined variables last.
|
|
617
|
-
#
|
|
618
|
-
# @param pins [Array<Solargraph::Pin::Base>]
|
|
619
|
-
# @return [Array<Solargraph::Pin::Base>]
|
|
620
|
-
def prefer_non_nil_variables pins
|
|
621
|
-
result = []
|
|
622
|
-
nil_pins = []
|
|
623
|
-
pins.each do |pin|
|
|
624
|
-
if pin.variable? && pin.nil_assignment?
|
|
625
|
-
nil_pins.push pin
|
|
626
|
-
else
|
|
627
|
-
result.push pin
|
|
628
|
-
end
|
|
629
|
-
end
|
|
630
|
-
result + nil_pins
|
|
631
|
-
end
|
|
632
|
-
|
|
633
|
-
# Check if a class is a superclass of another class.
|
|
634
|
-
#
|
|
635
|
-
# @param sup [String] The superclass
|
|
636
|
-
# @param sub [String] The subclass
|
|
637
|
-
# @return [Boolean]
|
|
638
|
-
def super_and_sub?(sup, sub)
|
|
639
|
-
fqsup = qualify(sup)
|
|
640
|
-
cls = qualify(store.get_superclass(sub), sub)
|
|
641
|
-
until cls.nil?
|
|
642
|
-
return true if cls == fqsup
|
|
643
|
-
cls = qualify(store.get_superclass(cls), cls)
|
|
644
|
-
end
|
|
645
|
-
false
|
|
646
|
-
end
|
|
647
|
-
|
|
648
|
-
# @param pins [Array<Pin::Base>]
|
|
649
|
-
# @return [Array<Pin::Base>]
|
|
650
|
-
def resolve_method_aliases pins
|
|
651
|
-
pins.map do |pin|
|
|
652
|
-
next pin unless pin.kind == Pin::METHOD_ALIAS
|
|
653
|
-
origin = get_method_stack(pin.namespace, pin.original, scope: pin.scope).select{|pin| pin.is_a?(Pin::BaseMethod)}.first
|
|
654
|
-
next pin if origin.nil?
|
|
655
|
-
Pin::Method.new(pin.location, pin.namespace, pin.name, origin.comments, origin.scope, origin.visibility, origin.parameters)
|
|
656
|
-
end
|
|
657
|
-
end
|
|
658
|
-
end
|
|
659
|
-
end
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'set'
|
|
3
|
+
require 'pathname'
|
|
4
|
+
|
|
5
|
+
module Solargraph
|
|
6
|
+
# An aggregate provider for information about workspaces, sources, gems, and
|
|
7
|
+
# the Ruby core.
|
|
8
|
+
#
|
|
9
|
+
class ApiMap
|
|
10
|
+
autoload :Cache, 'solargraph/api_map/cache'
|
|
11
|
+
autoload :SourceToYard, 'solargraph/api_map/source_to_yard'
|
|
12
|
+
autoload :Store, 'solargraph/api_map/store'
|
|
13
|
+
|
|
14
|
+
include SourceToYard
|
|
15
|
+
|
|
16
|
+
# Get a LiveMap associated with the current workspace.
|
|
17
|
+
#
|
|
18
|
+
# @return [Solargraph::LiveMap]
|
|
19
|
+
attr_reader :live_map
|
|
20
|
+
|
|
21
|
+
# @return [Array<String>]
|
|
22
|
+
attr_reader :unresolved_requires
|
|
23
|
+
|
|
24
|
+
# @param pins [Array<Solargraph::Pin::Base>]
|
|
25
|
+
def initialize pins: []
|
|
26
|
+
# @todo Extensions don't work yet
|
|
27
|
+
# require_extensions
|
|
28
|
+
@source_map_hash = {}
|
|
29
|
+
@cache = Cache.new
|
|
30
|
+
@mutex = Mutex.new
|
|
31
|
+
index pins
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param pins [Array<Pin::Base>]
|
|
35
|
+
# @return [self]
|
|
36
|
+
def index pins
|
|
37
|
+
@mutex.synchronize {
|
|
38
|
+
@source_map_hash.clear
|
|
39
|
+
@cache.clear
|
|
40
|
+
@store = Store.new(pins + YardMap.new.pins)
|
|
41
|
+
@unresolved_requires = []
|
|
42
|
+
}
|
|
43
|
+
# resolve_method_aliases
|
|
44
|
+
self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Map a single source.
|
|
48
|
+
#
|
|
49
|
+
# @param source [Source]
|
|
50
|
+
# @return [self]
|
|
51
|
+
def map source
|
|
52
|
+
catalog Bundle.new(opened: [source])
|
|
53
|
+
self
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def named_macro name
|
|
57
|
+
store.named_macros[name]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Catalog a bundle.
|
|
61
|
+
#
|
|
62
|
+
# @param bundle [Bundle]
|
|
63
|
+
# @return [self]
|
|
64
|
+
def catalog bundle
|
|
65
|
+
new_map_hash = {}
|
|
66
|
+
# Bundle always needs to be merged if it adds or removes sources
|
|
67
|
+
merged = (bundle.sources.length == source_map_hash.values.length)
|
|
68
|
+
bundle.sources.each do |source|
|
|
69
|
+
if source_map_hash.key?(source.filename)
|
|
70
|
+
if source_map_hash[source.filename].code == source.code && source_map_hash[source.filename].source.synchronized? && source.synchronized?
|
|
71
|
+
new_map_hash[source.filename] = source_map_hash[source.filename]
|
|
72
|
+
elsif !source.synchronized?
|
|
73
|
+
new_map_hash[source.filename] = source_map_hash[source.filename]
|
|
74
|
+
# @todo Smelly instance variable access
|
|
75
|
+
new_map_hash[source.filename].instance_variable_set(:@source, source)
|
|
76
|
+
else
|
|
77
|
+
map = Solargraph::SourceMap.map(source)
|
|
78
|
+
if source_map_hash[source.filename].try_merge!(map)
|
|
79
|
+
new_map_hash[source.filename] = source_map_hash[source.filename]
|
|
80
|
+
else
|
|
81
|
+
new_map_hash[source.filename] = map
|
|
82
|
+
merged = false
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
else
|
|
86
|
+
map = Solargraph::SourceMap.map(source)
|
|
87
|
+
new_map_hash[source.filename] = map
|
|
88
|
+
merged = false
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
return self if merged
|
|
92
|
+
pins = []
|
|
93
|
+
reqs = []
|
|
94
|
+
# @param map [SourceMap]
|
|
95
|
+
new_map_hash.values.each do |map|
|
|
96
|
+
pins.concat map.pins
|
|
97
|
+
reqs.concat map.requires.map(&:name)
|
|
98
|
+
end
|
|
99
|
+
reqs.concat bundle.workspace.config.required
|
|
100
|
+
unless bundle.workspace.require_paths.empty?
|
|
101
|
+
reqs.delete_if do |r|
|
|
102
|
+
result = false
|
|
103
|
+
bundle.workspace.require_paths.each do |l|
|
|
104
|
+
pn = Pathname.new(bundle.workspace.directory).join(l, "#{r}.rb")
|
|
105
|
+
if new_map_hash.keys.include?(pn.to_s)
|
|
106
|
+
result = true
|
|
107
|
+
break
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
result
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
yard_map.change(reqs)
|
|
114
|
+
new_store = Store.new(pins + yard_map.pins)
|
|
115
|
+
@mutex.synchronize {
|
|
116
|
+
@cache.clear
|
|
117
|
+
@source_map_hash = new_map_hash
|
|
118
|
+
@store = new_store
|
|
119
|
+
@unresolved_requires = yard_map.unresolved_requires
|
|
120
|
+
}
|
|
121
|
+
# resolve_method_aliases
|
|
122
|
+
self
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @param filename [String]
|
|
126
|
+
# @param position [Position, Array(Integer, Integer)]
|
|
127
|
+
# @return [Source::Cursor]
|
|
128
|
+
def cursor_at filename, position
|
|
129
|
+
position = Position.normalize(position)
|
|
130
|
+
raise "File not found: #{filename}" unless source_map_hash.has_key?(filename)
|
|
131
|
+
source_map_hash[filename].cursor_at(position)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Get a clip by filename and position.
|
|
135
|
+
#
|
|
136
|
+
# @param filename [String]
|
|
137
|
+
# @param position [Position, Array(Integer, Integer)]
|
|
138
|
+
# @return [SourceMap::Clip]
|
|
139
|
+
def clip_at filename, position
|
|
140
|
+
position = Position.normalize(position)
|
|
141
|
+
SourceMap::Clip.new(self, cursor_at(filename, position))
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Create an ApiMap with a workspace in the specified directory.
|
|
145
|
+
#
|
|
146
|
+
# @param directory [String]
|
|
147
|
+
# @return [ApiMap]
|
|
148
|
+
def self.load directory
|
|
149
|
+
api_map = self.new
|
|
150
|
+
workspace = Solargraph::Workspace.new(directory)
|
|
151
|
+
api_map.catalog Bundle.new(workspace: workspace)
|
|
152
|
+
api_map
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# @return [Array<Solargraph::Pin::Base>]
|
|
156
|
+
def pins
|
|
157
|
+
store.pins
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# An array of pins based on Ruby keywords (`if`, `end`, etc.).
|
|
161
|
+
#
|
|
162
|
+
# @return [Array<Solargraph::Pin::Keyword>]
|
|
163
|
+
def self.keywords
|
|
164
|
+
@keywords ||= CoreFills::KEYWORDS.map{ |s|
|
|
165
|
+
Pin::Keyword.new(s)
|
|
166
|
+
}.freeze
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# An array of namespace names defined in the ApiMap.
|
|
170
|
+
#
|
|
171
|
+
# @return [Array<String>]
|
|
172
|
+
def namespaces
|
|
173
|
+
store.namespaces
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# True if the namespace exists.
|
|
177
|
+
#
|
|
178
|
+
# @param name [String] The namespace to match
|
|
179
|
+
# @param context [String] The context to search
|
|
180
|
+
# @return [Boolean]
|
|
181
|
+
def namespace_exists? name, context = ''
|
|
182
|
+
!qualify(name, context).nil?
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Get suggestions for constants in the specified namespace. The result
|
|
186
|
+
# may contain both constant and namespace pins.
|
|
187
|
+
#
|
|
188
|
+
# @param namespace [String] The namespace
|
|
189
|
+
# @param context [String] The context
|
|
190
|
+
# @return [Array<Solargraph::Pin::Base>]
|
|
191
|
+
def get_constants namespace, context = ''
|
|
192
|
+
namespace ||= ''
|
|
193
|
+
cached = cache.get_constants(namespace, context)
|
|
194
|
+
return cached.clone unless cached.nil?
|
|
195
|
+
skip = []
|
|
196
|
+
result = []
|
|
197
|
+
bases = context.split('::')
|
|
198
|
+
while bases.length > 0
|
|
199
|
+
built = bases.join('::')
|
|
200
|
+
fqns = qualify(namespace, built)
|
|
201
|
+
visibility = [:public]
|
|
202
|
+
visibility.push :private if fqns == context
|
|
203
|
+
result.concat inner_get_constants(fqns, visibility, skip)
|
|
204
|
+
bases.pop
|
|
205
|
+
end
|
|
206
|
+
fqns = qualify(namespace, '')
|
|
207
|
+
visibility = [:public]
|
|
208
|
+
visibility.push :private if fqns == context
|
|
209
|
+
result.concat inner_get_constants(fqns, visibility, skip)
|
|
210
|
+
cache.set_constants(namespace, context, result)
|
|
211
|
+
result
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Get a fully qualified namespace name. This method will start the search
|
|
215
|
+
# in the specified context until it finds a match for the name.
|
|
216
|
+
#
|
|
217
|
+
# @param namespace [String, nil] The namespace to match
|
|
218
|
+
# @param context [String] The context to search
|
|
219
|
+
# @return [String]
|
|
220
|
+
def qualify namespace, context = ''
|
|
221
|
+
# @todo The return for self might work better elsewhere
|
|
222
|
+
return nil if namespace.nil?
|
|
223
|
+
return qualify(context) if namespace == 'self'
|
|
224
|
+
cached = cache.get_qualified_namespace(namespace, context)
|
|
225
|
+
return cached.clone unless cached.nil?
|
|
226
|
+
result = if namespace.start_with?('::')
|
|
227
|
+
inner_qualify(namespace[2..-1], '', [])
|
|
228
|
+
else
|
|
229
|
+
inner_qualify(namespace, context, [])
|
|
230
|
+
end
|
|
231
|
+
cache.set_qualified_namespace(namespace, context, result)
|
|
232
|
+
result
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Get an array of instance variable pins defined in specified namespace
|
|
236
|
+
# and scope.
|
|
237
|
+
#
|
|
238
|
+
# @param namespace [String] A fully qualified namespace
|
|
239
|
+
# @param scope [Symbol] :instance or :class
|
|
240
|
+
# @return [Array<Solargraph::Pin::InstanceVariable>]
|
|
241
|
+
def get_instance_variable_pins(namespace, scope = :instance)
|
|
242
|
+
result = []
|
|
243
|
+
result.concat store.get_instance_variables(namespace, scope)
|
|
244
|
+
sc = qualify(store.get_superclass(namespace), namespace)
|
|
245
|
+
until sc.nil?
|
|
246
|
+
result.concat store.get_instance_variables(sc, scope)
|
|
247
|
+
sc = qualify(store.get_superclass(sc), sc)
|
|
248
|
+
end
|
|
249
|
+
result
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Get an array of class variable pins for a namespace.
|
|
253
|
+
#
|
|
254
|
+
# @param namespace [String] A fully qualified namespace
|
|
255
|
+
# @return [Array<Solargraph::Pin::ClassVariable>]
|
|
256
|
+
def get_class_variable_pins(namespace)
|
|
257
|
+
prefer_non_nil_variables(store.get_class_variables(namespace))
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# @return [Array<Solargraph::Pin::Base>]
|
|
261
|
+
def get_symbols
|
|
262
|
+
store.get_symbols
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# @return [Array<Solargraph::Pin::GlobalVariable>]
|
|
266
|
+
def get_global_variable_pins
|
|
267
|
+
# @todo Slow version
|
|
268
|
+
pins.select{|p| p.kind == Pin::GLOBAL_VARIABLE}
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Get an array of methods available in a particular context.
|
|
272
|
+
#
|
|
273
|
+
# @param fqns [String] The fully qualified namespace to search for methods
|
|
274
|
+
# @param scope [Symbol] :class or :instance
|
|
275
|
+
# @param visibility [Array<Symbol>] :public, :protected, and/or :private
|
|
276
|
+
# @param deep [Boolean] True to include superclasses, mixins, etc.
|
|
277
|
+
# @return [Array<Solargraph::Pin::Base>]
|
|
278
|
+
def get_methods fqns, scope: :instance, visibility: [:public], deep: true
|
|
279
|
+
cached = cache.get_methods(fqns, scope, visibility, deep)
|
|
280
|
+
return cached.clone unless cached.nil?
|
|
281
|
+
result = []
|
|
282
|
+
skip = []
|
|
283
|
+
if fqns == ''
|
|
284
|
+
# @todo Implement domains
|
|
285
|
+
# domains.each do |domain|
|
|
286
|
+
# type = ComplexType.parse(domain).first
|
|
287
|
+
# result.concat inner_get_methods(type.name, type.scope, [:public], deep, skip)
|
|
288
|
+
# end
|
|
289
|
+
result.concat inner_get_methods(fqns, :class, visibility, deep, skip)
|
|
290
|
+
result.concat inner_get_methods(fqns, :instance, visibility, deep, skip)
|
|
291
|
+
result.concat inner_get_methods('Kernel', :instance, visibility, deep, skip)
|
|
292
|
+
else
|
|
293
|
+
result.concat inner_get_methods(fqns, scope, visibility, deep, skip)
|
|
294
|
+
end
|
|
295
|
+
# live = live_map.get_methods(fqns, '', scope.to_s, visibility.include?(:private))
|
|
296
|
+
# unless live.empty?
|
|
297
|
+
# exist = result.map(&:name)
|
|
298
|
+
# result.concat live.reject{|p| exist.include?(p.name)}
|
|
299
|
+
# end
|
|
300
|
+
resolved = resolve_method_aliases(result)
|
|
301
|
+
cache.set_methods(fqns, scope, visibility, deep, resolved)
|
|
302
|
+
resolved
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Get an array of method pins for a complex type.
|
|
306
|
+
#
|
|
307
|
+
# The type's namespace and the context should be fully qualified. If the
|
|
308
|
+
# context matches the namespace type or is a subclass of the type,
|
|
309
|
+
# protected methods are included in the results. If protected methods are
|
|
310
|
+
# included and internal is true, private methods are also included.
|
|
311
|
+
#
|
|
312
|
+
# @example
|
|
313
|
+
# api_map = Solargraph::ApiMap.new
|
|
314
|
+
# type = Solargraph::ComplexType.parse('String')
|
|
315
|
+
# api_map.get_complex_type_methods(type)
|
|
316
|
+
#
|
|
317
|
+
# @param type [Solargraph::ComplexType] The complex type of the namespace
|
|
318
|
+
# @param context [String] The context from which the type is referenced
|
|
319
|
+
# @param internal [Boolean] True to include private methods
|
|
320
|
+
# @return [Array<Solargraph::Pin::Base>]
|
|
321
|
+
def get_complex_type_methods type, context = '', internal = false
|
|
322
|
+
# This method does not qualify the complex type's namespace because
|
|
323
|
+
# it can cause conflicts between similar names, e.g., `Foo` vs.
|
|
324
|
+
# `Other::Foo`. It still takes a context argument to determine whether
|
|
325
|
+
# protected and private methods are visible.
|
|
326
|
+
return [] if type.undefined? || type.void?
|
|
327
|
+
result = []
|
|
328
|
+
if type.duck_type?
|
|
329
|
+
type.select(&:duck_type?).each do |t|
|
|
330
|
+
result.push Pin::DuckMethod.new(nil, t.tag[1..-1])
|
|
331
|
+
end
|
|
332
|
+
result.concat get_methods('Object')
|
|
333
|
+
else
|
|
334
|
+
unless type.nil? || type.name == 'void'
|
|
335
|
+
visibility = [:public]
|
|
336
|
+
if type.namespace == context || super_and_sub?(type.namespace, context)
|
|
337
|
+
visibility.push :protected
|
|
338
|
+
visibility.push :private if internal
|
|
339
|
+
end
|
|
340
|
+
result.concat get_methods(type.namespace, scope: type.scope, visibility: visibility)
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
result
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# Get a stack of method pins for a method name in a namespace. The order
|
|
347
|
+
# of the pins corresponds to the ancestry chain, with highest precedence
|
|
348
|
+
# first.
|
|
349
|
+
#
|
|
350
|
+
# @example
|
|
351
|
+
# api_map.get_method_stack('Subclass', 'method_name')
|
|
352
|
+
# #=> [ <Subclass#method_name pin>, <Superclass#method_name pin> ]
|
|
353
|
+
#
|
|
354
|
+
# @param fqns [String]
|
|
355
|
+
# @param name [String]
|
|
356
|
+
# @param scope [Symbol] :instance or :class
|
|
357
|
+
# @return [Array<Solargraph::Pin::Base>]
|
|
358
|
+
def get_method_stack fqns, name, scope: :instance
|
|
359
|
+
get_methods(fqns, scope: scope, visibility: [:private, :protected, :public]).select{|p| p.name == name}
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Get an array of all suggestions that match the specified path.
|
|
363
|
+
#
|
|
364
|
+
# @deprecated Use #get_path_pins instead.
|
|
365
|
+
#
|
|
366
|
+
# @param path [String] The path to find
|
|
367
|
+
# @return [Array<Solargraph::Pin::Base>]
|
|
368
|
+
def get_path_suggestions path
|
|
369
|
+
return [] if path.nil?
|
|
370
|
+
result = []
|
|
371
|
+
result.concat store.get_path_pins(path)
|
|
372
|
+
# if result.empty?
|
|
373
|
+
# lp = live_map.get_path_pin(path)
|
|
374
|
+
# result.push lp unless lp.nil?
|
|
375
|
+
# end
|
|
376
|
+
resolve_method_aliases(result)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# Get an array of pins that match the specified path.
|
|
380
|
+
#
|
|
381
|
+
# @param path [String]
|
|
382
|
+
# @return [Array<Pin::Base>]
|
|
383
|
+
def get_path_pins path
|
|
384
|
+
get_path_suggestions(path)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Get a list of documented paths that match the query.
|
|
388
|
+
#
|
|
389
|
+
# @example
|
|
390
|
+
# api_map.query('str') # Results will include `String` and `Struct`
|
|
391
|
+
#
|
|
392
|
+
# @param query [String] The text to match
|
|
393
|
+
# @return [Array<String>]
|
|
394
|
+
def search query
|
|
395
|
+
rake_yard(store)
|
|
396
|
+
found = []
|
|
397
|
+
code_object_paths.each do |k|
|
|
398
|
+
if found.empty? || (query.include?('.') || query.include?('#')) || !(k.include?('.') || k.include?('#'))
|
|
399
|
+
found.push k if k.downcase.include?(query.downcase)
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
found
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# Get YARD documentation for the specified path.
|
|
406
|
+
#
|
|
407
|
+
# @example
|
|
408
|
+
# api_map.document('String#split')
|
|
409
|
+
#
|
|
410
|
+
# @param path [String] The path to find
|
|
411
|
+
# @return [Array<YARD::CodeObject::Base>]
|
|
412
|
+
def document path
|
|
413
|
+
rake_yard(store)
|
|
414
|
+
docs = []
|
|
415
|
+
docs.push code_object_at(path) unless code_object_at(path).nil?
|
|
416
|
+
docs
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
# Get an array of all symbols in the workspace that match the query.
|
|
420
|
+
#
|
|
421
|
+
# @param query [String]
|
|
422
|
+
# @return [Array<Pin::Base>]
|
|
423
|
+
def query_symbols query
|
|
424
|
+
result = []
|
|
425
|
+
source_map_hash.values.each do |s|
|
|
426
|
+
result.concat s.query_symbols(query)
|
|
427
|
+
end
|
|
428
|
+
result
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
# @param location [Solargraph::Location]
|
|
432
|
+
# @return [Array<Solargraph::Pin::Base>]
|
|
433
|
+
def locate_pins location
|
|
434
|
+
return [] if location.nil? || !source_map_hash.has_key?(location.filename)
|
|
435
|
+
source_map_hash[location.filename].locate_pins(location)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
# @raise [FileNotFoundError] if the cursor's file is not in the ApiMap
|
|
439
|
+
# @param cursor [Source::Cursor]
|
|
440
|
+
# @return [SourceMap::Clip]
|
|
441
|
+
def clip cursor
|
|
442
|
+
raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename}" unless source_map_hash.has_key?(cursor.filename)
|
|
443
|
+
SourceMap::Clip.new(self, cursor)
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
# Get an array of document symbols from a file.
|
|
447
|
+
#
|
|
448
|
+
# @param filename [String]
|
|
449
|
+
# @return [Array<Pin::Symbol>]
|
|
450
|
+
def document_symbols filename
|
|
451
|
+
return [] unless source_map_hash.has_key?(filename) # @todo Raise error?
|
|
452
|
+
source_map_hash[filename].document_symbols
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
# Get a source map by filename.
|
|
456
|
+
#
|
|
457
|
+
# @param filename [String]
|
|
458
|
+
# @return [SourceMap]
|
|
459
|
+
def source_map filename
|
|
460
|
+
raise FileNotFoundError, "Source map for `#{filename}` not found" unless source_map_hash.has_key?(filename)
|
|
461
|
+
source_map_hash[filename]
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
private
|
|
465
|
+
|
|
466
|
+
# @return [YardMap]
|
|
467
|
+
def yard_map
|
|
468
|
+
@yard_map ||= YardMap.new
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# A hash of source maps with filename keys.
|
|
472
|
+
#
|
|
473
|
+
# @return [Hash{String => SourceMap}]
|
|
474
|
+
def source_map_hash
|
|
475
|
+
@mutex.synchronize { @source_map_hash }
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
# @return [ApiMap::Store]
|
|
479
|
+
def store
|
|
480
|
+
@mutex.synchronize { @store }
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
# @return [Solargraph::ApiMap::Cache]
|
|
484
|
+
def cache
|
|
485
|
+
@mutex.synchronize { @cache }
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
# @param fqns [String] A fully qualified namespace
|
|
489
|
+
# @param scope [Symbol] :class or :instance
|
|
490
|
+
# @param visibility [Array<Symbol>] :public, :protected, and/or :private
|
|
491
|
+
# @param deep [Boolean]
|
|
492
|
+
# @param skip [Array<String>]
|
|
493
|
+
# @param no_core [Boolean] Skip core classes if true
|
|
494
|
+
# @return [Array<Pin::Base>]
|
|
495
|
+
def inner_get_methods fqns, scope, visibility, deep, skip, no_core = false
|
|
496
|
+
return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module|Kernel)$/
|
|
497
|
+
reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
|
|
498
|
+
return [] if skip.include?(reqstr)
|
|
499
|
+
skip.push reqstr
|
|
500
|
+
result = []
|
|
501
|
+
result.concat store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
|
|
502
|
+
if deep
|
|
503
|
+
sc = store.get_superclass(fqns)
|
|
504
|
+
unless sc.nil?
|
|
505
|
+
fqsc = qualify(sc, fqns.split('::')[0..-2].join('::'))
|
|
506
|
+
result.concat inner_get_methods(fqsc, scope, visibility, true, skip, true) unless fqsc.nil?
|
|
507
|
+
end
|
|
508
|
+
if scope == :instance
|
|
509
|
+
store.get_includes(fqns).reverse.each do |im|
|
|
510
|
+
fqim = qualify(im, fqns)
|
|
511
|
+
result.concat inner_get_methods(fqim, scope, visibility, deep, skip, true) unless fqim.nil?
|
|
512
|
+
end
|
|
513
|
+
result.concat inner_get_methods('Object', :instance, [:public], deep, skip, no_core)
|
|
514
|
+
result.concat inner_get_methods('BasicObject', :instance, [:public], deep, skip, no_core)
|
|
515
|
+
else
|
|
516
|
+
store.get_extends(fqns).reverse.each do |em|
|
|
517
|
+
fqem = qualify(em, fqns)
|
|
518
|
+
result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
|
|
519
|
+
end
|
|
520
|
+
unless no_core || fqns.empty?
|
|
521
|
+
type = get_namespace_type(fqns)
|
|
522
|
+
result.concat inner_get_methods('Class', :instance, visibility, deep, skip, no_core) if type == :class
|
|
523
|
+
result.concat inner_get_methods('Module', :instance,visibility, deep, skip, no_core)
|
|
524
|
+
end
|
|
525
|
+
end
|
|
526
|
+
store.domains(fqns).each do |d|
|
|
527
|
+
dt = ComplexType.try_parse(d)
|
|
528
|
+
result.concat inner_get_methods(dt.namespace, dt.scope, [:public], deep, skip)
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
result
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
# @param fqns [String]
|
|
535
|
+
# @param visibility [Array<Symbol>]
|
|
536
|
+
# @param skip [Array<String>]
|
|
537
|
+
# @return [Array<Pin::Base>]
|
|
538
|
+
def inner_get_constants fqns, visibility, skip
|
|
539
|
+
return [] if skip.include?(fqns)
|
|
540
|
+
skip.push fqns
|
|
541
|
+
result = []
|
|
542
|
+
result.concat store.get_constants(fqns, visibility).sort{ |a, b| a.name <=> b.name }
|
|
543
|
+
store.get_includes(fqns).each do |is|
|
|
544
|
+
fqis = qualify(is, fqns)
|
|
545
|
+
result.concat inner_get_constants(fqis, [:public], skip) unless fqis.nil?
|
|
546
|
+
end
|
|
547
|
+
# result.concat live_map.get_constants(fqns)
|
|
548
|
+
result
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
# Require extensions for the experimental plugin architecture. Any
|
|
552
|
+
# installed gem with a name that starts with "solargraph-" is considered
|
|
553
|
+
# an extension.
|
|
554
|
+
#
|
|
555
|
+
# @return [void]
|
|
556
|
+
def require_extensions
|
|
557
|
+
Gem::Specification.all_names.select{|n| n.match(/^solargraph\-[a-z0-9_\-]*?\-ext\-[0-9\.]*$/)}.each do |n|
|
|
558
|
+
Solargraph::Logging.logger.info "Loading extension #{n}"
|
|
559
|
+
require n.match(/^(solargraph\-[a-z0-9_\-]*?\-ext)\-[0-9\.]*$/)[1]
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
# @return [Hash]
|
|
564
|
+
def path_macros
|
|
565
|
+
@path_macros ||= {}
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
# @param name [String]
|
|
569
|
+
# @param root [String]
|
|
570
|
+
# @param skip [Array<String>]
|
|
571
|
+
# @return [String]
|
|
572
|
+
def inner_qualify name, root, skip
|
|
573
|
+
return nil if name.nil?
|
|
574
|
+
return nil if skip.include?(root)
|
|
575
|
+
skip.push root
|
|
576
|
+
if name == ''
|
|
577
|
+
if root == ''
|
|
578
|
+
return ''
|
|
579
|
+
else
|
|
580
|
+
return inner_qualify(root, '', skip)
|
|
581
|
+
end
|
|
582
|
+
else
|
|
583
|
+
return name if root == '' && store.namespace_exists?(name)
|
|
584
|
+
roots = root.to_s.split('::')
|
|
585
|
+
while roots.length > 0
|
|
586
|
+
fqns = roots.join('::') + '::' + name
|
|
587
|
+
return fqns if store.namespace_exists?(fqns)
|
|
588
|
+
incs = store.get_includes(roots.join('::'))
|
|
589
|
+
incs.each do |inc|
|
|
590
|
+
foundinc = inner_qualify(name, inc, skip)
|
|
591
|
+
return foundinc unless foundinc.nil?
|
|
592
|
+
end
|
|
593
|
+
roots.pop
|
|
594
|
+
end
|
|
595
|
+
incs = store.get_includes('')
|
|
596
|
+
incs.each do |inc|
|
|
597
|
+
foundinc = inner_qualify(name, inc, skip)
|
|
598
|
+
return foundinc unless foundinc.nil?
|
|
599
|
+
end
|
|
600
|
+
return name if store.namespace_exists?(name)
|
|
601
|
+
end
|
|
602
|
+
# live_map.get_fqns(name, root)
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
# Get the namespace's type (Class or Module).
|
|
606
|
+
#
|
|
607
|
+
# @param fqns [String] A fully qualified namespace
|
|
608
|
+
# @return [Symbol] :class, :module, or nil
|
|
609
|
+
def get_namespace_type fqns
|
|
610
|
+
return nil if fqns.nil?
|
|
611
|
+
pin = store.get_path_pins(fqns).first
|
|
612
|
+
return nil if pin.nil?
|
|
613
|
+
pin.type
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
# Sort an array of pins to put nil or undefined variables last.
|
|
617
|
+
#
|
|
618
|
+
# @param pins [Array<Solargraph::Pin::Base>]
|
|
619
|
+
# @return [Array<Solargraph::Pin::Base>]
|
|
620
|
+
def prefer_non_nil_variables pins
|
|
621
|
+
result = []
|
|
622
|
+
nil_pins = []
|
|
623
|
+
pins.each do |pin|
|
|
624
|
+
if pin.variable? && pin.nil_assignment?
|
|
625
|
+
nil_pins.push pin
|
|
626
|
+
else
|
|
627
|
+
result.push pin
|
|
628
|
+
end
|
|
629
|
+
end
|
|
630
|
+
result + nil_pins
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
# Check if a class is a superclass of another class.
|
|
634
|
+
#
|
|
635
|
+
# @param sup [String] The superclass
|
|
636
|
+
# @param sub [String] The subclass
|
|
637
|
+
# @return [Boolean]
|
|
638
|
+
def super_and_sub?(sup, sub)
|
|
639
|
+
fqsup = qualify(sup)
|
|
640
|
+
cls = qualify(store.get_superclass(sub), sub)
|
|
641
|
+
until cls.nil?
|
|
642
|
+
return true if cls == fqsup
|
|
643
|
+
cls = qualify(store.get_superclass(cls), cls)
|
|
644
|
+
end
|
|
645
|
+
false
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
# @param pins [Array<Pin::Base>]
|
|
649
|
+
# @return [Array<Pin::Base>]
|
|
650
|
+
def resolve_method_aliases pins
|
|
651
|
+
pins.map do |pin|
|
|
652
|
+
next pin unless pin.kind == Pin::METHOD_ALIAS
|
|
653
|
+
origin = get_method_stack(pin.namespace, pin.original, scope: pin.scope).select{|pin| pin.is_a?(Pin::BaseMethod)}.first
|
|
654
|
+
next pin if origin.nil?
|
|
655
|
+
Pin::Method.new(pin.location, pin.namespace, pin.name, origin.comments, origin.scope, origin.visibility, origin.parameters)
|
|
656
|
+
end
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
end
|