solargraph 0.58.1 → 0.59.0.dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.github/workflows/linting.yml +4 -5
  4. data/.github/workflows/plugins.yml +40 -36
  5. data/.github/workflows/rspec.yml +45 -13
  6. data/.github/workflows/typecheck.yml +2 -2
  7. data/.rubocop_todo.yml +27 -49
  8. data/README.md +3 -3
  9. data/Rakefile +1 -0
  10. data/lib/solargraph/api_map/cache.rb +110 -110
  11. data/lib/solargraph/api_map/constants.rb +289 -279
  12. data/lib/solargraph/api_map/index.rb +204 -193
  13. data/lib/solargraph/api_map/source_to_yard.rb +109 -97
  14. data/lib/solargraph/api_map/store.rb +387 -384
  15. data/lib/solargraph/api_map.rb +1000 -945
  16. data/lib/solargraph/complex_type/conformance.rb +176 -0
  17. data/lib/solargraph/complex_type/type_methods.rb +242 -228
  18. data/lib/solargraph/complex_type/unique_type.rb +632 -482
  19. data/lib/solargraph/complex_type.rb +549 -444
  20. data/lib/solargraph/convention/data_definition/data_definition_node.rb +93 -91
  21. data/lib/solargraph/convention/data_definition.rb +108 -105
  22. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +62 -61
  23. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +103 -102
  24. data/lib/solargraph/convention/struct_definition.rb +168 -164
  25. data/lib/solargraph/diagnostics/require_not_found.rb +54 -53
  26. data/lib/solargraph/diagnostics/rubocop.rb +119 -118
  27. data/lib/solargraph/diagnostics/rubocop_helpers.rb +70 -68
  28. data/lib/solargraph/diagnostics/type_check.rb +56 -55
  29. data/lib/solargraph/doc_map.rb +200 -439
  30. data/lib/solargraph/equality.rb +34 -34
  31. data/lib/solargraph/gem_pins.rb +97 -98
  32. data/lib/solargraph/language_server/host/dispatch.rb +131 -130
  33. data/lib/solargraph/language_server/host/message_worker.rb +113 -112
  34. data/lib/solargraph/language_server/host/sources.rb +100 -99
  35. data/lib/solargraph/language_server/host.rb +883 -878
  36. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +109 -114
  37. data/lib/solargraph/language_server/message/extended/document.rb +24 -23
  38. data/lib/solargraph/language_server/message/text_document/completion.rb +58 -56
  39. data/lib/solargraph/language_server/message/text_document/definition.rb +42 -40
  40. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +28 -26
  41. data/lib/solargraph/language_server/message/text_document/formatting.rb +150 -148
  42. data/lib/solargraph/language_server/message/text_document/hover.rb +60 -58
  43. data/lib/solargraph/language_server/message/text_document/signature_help.rb +25 -24
  44. data/lib/solargraph/language_server/message/text_document/type_definition.rb +27 -25
  45. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +25 -23
  46. data/lib/solargraph/library.rb +729 -683
  47. data/lib/solargraph/location.rb +87 -82
  48. data/lib/solargraph/logging.rb +57 -37
  49. data/lib/solargraph/parser/comment_ripper.rb +76 -69
  50. data/lib/solargraph/parser/flow_sensitive_typing.rb +483 -255
  51. data/lib/solargraph/parser/node_processor/base.rb +122 -92
  52. data/lib/solargraph/parser/node_processor.rb +63 -62
  53. data/lib/solargraph/parser/parser_gem/class_methods.rb +167 -149
  54. data/lib/solargraph/parser/parser_gem/node_chainer.rb +191 -166
  55. data/lib/solargraph/parser/parser_gem/node_methods.rb +506 -486
  56. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -22
  57. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +61 -59
  58. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +24 -15
  59. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +46 -46
  60. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +60 -53
  61. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +53 -23
  62. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +41 -40
  63. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +30 -29
  64. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +61 -59
  65. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -98
  66. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  67. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
  68. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +39 -38
  69. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +53 -52
  70. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +296 -291
  71. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  72. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +33 -29
  73. data/lib/solargraph/parser/parser_gem/node_processors.rb +74 -70
  74. data/lib/solargraph/parser/region.rb +75 -69
  75. data/lib/solargraph/parser/snippet.rb +17 -17
  76. data/lib/solargraph/pin/base.rb +761 -729
  77. data/lib/solargraph/pin/base_variable.rb +418 -126
  78. data/lib/solargraph/pin/block.rb +126 -104
  79. data/lib/solargraph/pin/breakable.rb +13 -9
  80. data/lib/solargraph/pin/callable.rb +278 -231
  81. data/lib/solargraph/pin/closure.rb +68 -72
  82. data/lib/solargraph/pin/common.rb +94 -79
  83. data/lib/solargraph/pin/compound_statement.rb +55 -0
  84. data/lib/solargraph/pin/conversions.rb +124 -123
  85. data/lib/solargraph/pin/delegated_method.rb +131 -120
  86. data/lib/solargraph/pin/documenting.rb +115 -114
  87. data/lib/solargraph/pin/instance_variable.rb +38 -34
  88. data/lib/solargraph/pin/keyword.rb +16 -20
  89. data/lib/solargraph/pin/local_variable.rb +31 -75
  90. data/lib/solargraph/pin/method.rb +720 -672
  91. data/lib/solargraph/pin/method_alias.rb +42 -34
  92. data/lib/solargraph/pin/namespace.rb +121 -115
  93. data/lib/solargraph/pin/parameter.rb +338 -275
  94. data/lib/solargraph/pin/proxy_type.rb +40 -39
  95. data/lib/solargraph/pin/reference/override.rb +47 -47
  96. data/lib/solargraph/pin/reference/superclass.rb +17 -15
  97. data/lib/solargraph/pin/reference.rb +41 -39
  98. data/lib/solargraph/pin/search.rb +62 -61
  99. data/lib/solargraph/pin/signature.rb +69 -61
  100. data/lib/solargraph/pin/symbol.rb +53 -53
  101. data/lib/solargraph/pin/until.rb +18 -18
  102. data/lib/solargraph/pin/while.rb +18 -18
  103. data/lib/solargraph/pin.rb +46 -44
  104. data/lib/solargraph/pin_cache.rb +665 -245
  105. data/lib/solargraph/position.rb +118 -119
  106. data/lib/solargraph/range.rb +112 -112
  107. data/lib/solargraph/rbs_map/conversions.rb +846 -823
  108. data/lib/solargraph/rbs_map/core_map.rb +65 -58
  109. data/lib/solargraph/rbs_map/stdlib_map.rb +72 -43
  110. data/lib/solargraph/rbs_map.rb +217 -163
  111. data/lib/solargraph/shell.rb +397 -352
  112. data/lib/solargraph/source/chain/call.rb +372 -337
  113. data/lib/solargraph/source/chain/constant.rb +28 -26
  114. data/lib/solargraph/source/chain/hash.rb +35 -34
  115. data/lib/solargraph/source/chain/if.rb +29 -28
  116. data/lib/solargraph/source/chain/instance_variable.rb +34 -13
  117. data/lib/solargraph/source/chain/literal.rb +53 -48
  118. data/lib/solargraph/source/chain/or.rb +31 -23
  119. data/lib/solargraph/source/chain.rb +294 -291
  120. data/lib/solargraph/source/change.rb +89 -82
  121. data/lib/solargraph/source/cursor.rb +172 -166
  122. data/lib/solargraph/source/source_chainer.rb +204 -194
  123. data/lib/solargraph/source/updater.rb +59 -55
  124. data/lib/solargraph/source.rb +524 -498
  125. data/lib/solargraph/source_map/clip.rb +237 -226
  126. data/lib/solargraph/source_map/data.rb +37 -34
  127. data/lib/solargraph/source_map/mapper.rb +282 -259
  128. data/lib/solargraph/source_map.rb +220 -212
  129. data/lib/solargraph/type_checker/problem.rb +34 -32
  130. data/lib/solargraph/type_checker/rules.rb +157 -84
  131. data/lib/solargraph/type_checker.rb +895 -814
  132. data/lib/solargraph/version.rb +1 -1
  133. data/lib/solargraph/workspace/config.rb +257 -255
  134. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  135. data/lib/solargraph/workspace/require_paths.rb +98 -97
  136. data/lib/solargraph/workspace.rb +362 -220
  137. data/lib/solargraph/yard_map/helpers.rb +45 -44
  138. data/lib/solargraph/yard_map/mapper/to_method.rb +134 -130
  139. data/lib/solargraph/yard_map/mapper/to_namespace.rb +32 -31
  140. data/lib/solargraph/yard_map/mapper.rb +84 -79
  141. data/lib/solargraph/yardoc.rb +97 -87
  142. data/lib/solargraph.rb +126 -105
  143. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  144. data/rbs/fills/tuple/tuple.rbs +28 -0
  145. data/rbs/shims/ast/0/node.rbs +5 -0
  146. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  147. data/rbs_collection.yaml +1 -1
  148. data/solargraph.gemspec +2 -1
  149. metadata +22 -17
  150. data/lib/solargraph/type_checker/checks.rb +0 -124
  151. data/lib/solargraph/type_checker/param_def.rb +0 -37
  152. data/lib/solargraph/yard_map/to_method.rb +0 -89
  153. data/sig/shims/ast/0/node.rbs +0 -5
  154. /data/{sig → rbs}/shims/ast/2.4/.rbs_meta.yaml +0 -0
  155. /data/{sig → rbs}/shims/ast/2.4/ast.rbs +0 -0
  156. /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
  157. /data/{sig → rbs}/shims/parser/3.2.0.1/manifest.yaml +0 -0
  158. /data/{sig → rbs}/shims/parser/3.2.0.1/parser.rbs +0 -0
  159. /data/{sig → rbs}/shims/parser/3.2.0.1/polyfill.rbs +0 -0
  160. /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
  161. /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
  162. /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
@@ -1,220 +1,362 @@
1
- # frozen_string_literal: true
2
-
3
- require 'open3'
4
- require 'json'
5
-
6
- module Solargraph
7
- # A workspace consists of the files in a project's directory and the
8
- # project's configuration. It provides a Source for each file to be used
9
- # in an associated Library or ApiMap.
10
- #
11
- class Workspace
12
- autoload :Config, 'solargraph/workspace/config'
13
- autoload :RequirePaths, 'solargraph/workspace/require_paths'
14
-
15
- # @return [String]
16
- attr_reader :directory
17
-
18
- # @return [Array<String>]
19
- attr_reader :gemnames
20
- alias source_gems gemnames
21
-
22
- # @param directory [String] TODO: Remove '' and '*' special cases
23
- # @param config [Config, nil]
24
- # @param server [Hash]
25
- def initialize directory = '', config = nil, server = {}
26
- raise ArgumentError, 'directory must be a String' unless directory.is_a?(String)
27
-
28
- @directory = if ['*', ''].include?(directory)
29
- directory
30
- else
31
- File.absolute_path(directory)
32
- end
33
- @config = config
34
- @server = server
35
- load_sources
36
- @gemnames = []
37
- require_plugins
38
- end
39
-
40
- # The require paths associated with the workspace.
41
- #
42
- # @return [Array<String>]
43
- def require_paths
44
- # @todo are the semantics of '*' the same as '', meaning 'don't send back any require paths'?
45
- @require_paths ||= RequirePaths.new(directory_or_nil, config).generate
46
- end
47
-
48
- # @return [Solargraph::Workspace::Config]
49
- def config
50
- @config ||= Solargraph::Workspace::Config.new(directory)
51
- end
52
-
53
- # @param level [Symbol]
54
- # @return [TypeChecker::Rules]
55
- def rules(level)
56
- @rules ||= TypeChecker::Rules.new(level, config.type_checker_rules)
57
- end
58
-
59
- # Merge the source. A merge will update the existing source for the file
60
- # or add it to the sources if the workspace is configured to include it.
61
- # The source is ignored if the configuration excludes it.
62
- #
63
- # @param sources [Array<Solargraph::Source>]
64
- # @return [Boolean] True if the source was added to the workspace
65
- def merge *sources
66
- unless directory == '*' || sources.all? { |source| source_hash.key?(source.filename) }
67
- # Reload the config to determine if a new source should be included
68
- @config = Solargraph::Workspace::Config.new(directory)
69
- end
70
-
71
- includes_any = false
72
- sources.each do |source|
73
- if directory == "*" || config.calculated.include?(source.filename)
74
- source_hash[source.filename] = source
75
- includes_any = true
76
- end
77
- end
78
-
79
- includes_any
80
- end
81
-
82
- # Remove a source from the workspace. The source will not be removed if
83
- # its file exists and the workspace is configured to include it.
84
- #
85
- # @param filename [String]
86
- # @return [Boolean] True if the source was removed from the workspace
87
- def remove filename
88
- return false unless source_hash.key?(filename)
89
- source_hash.delete filename
90
- true
91
- end
92
-
93
- # @return [Array<String>]
94
- def filenames
95
- source_hash.keys
96
- end
97
-
98
- # @return [Array<Solargraph::Source>]
99
- def sources
100
- source_hash.values
101
- end
102
-
103
- # @param filename [String]
104
- # @return [Boolean]
105
- def has_file? filename
106
- source_hash.key?(filename)
107
- end
108
-
109
- # Get a source by its filename.
110
- #
111
- # @param filename [String]
112
- # @return [Solargraph::Source]
113
- def source filename
114
- source_hash[filename]
115
- end
116
-
117
- # True if the path resolves to a file in the workspace's require paths.
118
- #
119
- # @param path [String]
120
- # @return [Boolean]
121
- def would_require? path
122
- require_paths.each do |rp|
123
- full = File.join rp, path
124
- return true if File.file?(full) || File.file?(full << ".rb")
125
- end
126
- false
127
- end
128
-
129
- # @return [String, nil]
130
- def rbs_collection_path
131
- @gem_rbs_collection ||= read_rbs_collection_path
132
- end
133
-
134
- # @return [String, nil]
135
- def rbs_collection_config_path
136
- @rbs_collection_config_path ||= begin
137
- unless directory.empty? || directory == '*'
138
- yaml_file = File.join(directory, 'rbs_collection.yaml')
139
- yaml_file if File.file?(yaml_file)
140
- end
141
- end
142
- end
143
-
144
- # Synchronize the workspace from the provided updater.
145
- #
146
- # @param updater [Source::Updater]
147
- # @return [void]
148
- def synchronize! updater
149
- source_hash[updater.filename] = source_hash[updater.filename].synchronize(updater)
150
- end
151
-
152
- # @return [String]
153
- def command_path
154
- server['commandPath'] || 'solargraph'
155
- end
156
-
157
- # @return [String, nil]
158
- def directory_or_nil
159
- return nil if directory.empty? || directory == '*'
160
- directory
161
- end
162
-
163
- # True if the workspace has a root Gemfile.
164
- #
165
- # @todo Handle projects with custom Bundler/Gemfile setups (see DocMap#gemspecs_required_from_bundler)
166
- #
167
- def gemfile?
168
- directory && File.file?(File.join(directory, 'Gemfile'))
169
- end
170
-
171
- private
172
-
173
- # The language server configuration (or an empty hash if the workspace was
174
- # not initialized from a server).
175
- #
176
- # @return [Hash]
177
- attr_reader :server
178
-
179
- # @return [Hash{String => Solargraph::Source}]
180
- def source_hash
181
- @source_hash ||= {}
182
- end
183
-
184
- # @return [void]
185
- def load_sources
186
- source_hash.clear
187
- unless directory.empty? || directory == '*'
188
- size = config.calculated.length
189
- raise WorkspaceTooLargeError, "The workspace is too large to index (#{size} files, #{config.max_files} max)" if config.max_files > 0 and size > config.max_files
190
- config.calculated.each do |filename|
191
- begin
192
- source_hash[filename] = Solargraph::Source.load(filename)
193
- rescue Errno::ENOENT => e
194
- Solargraph.logger.warn("Error loading #{filename}: [#{e.class}] #{e.message}")
195
- end
196
- end
197
- end
198
- end
199
-
200
- # @return [void]
201
- def require_plugins
202
- config.plugins.each do |plugin|
203
- begin
204
- require plugin
205
- rescue LoadError
206
- Solargraph.logger.warn "Failed to load plugin '#{plugin}'"
207
- end
208
- end
209
- end
210
-
211
- # @return [String, nil]
212
- def read_rbs_collection_path
213
- return unless rbs_collection_config_path
214
-
215
- path = YAML.load_file(rbs_collection_config_path)&.fetch('path')
216
- # make fully qualified
217
- File.expand_path(path, directory)
218
- end
219
- end
220
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+ require 'json'
5
+ require 'yaml'
6
+
7
+ module Solargraph
8
+ # A workspace consists of the files in a project's directory and the
9
+ # project's configuration. It provides a Source for each file to be used
10
+ # in an associated Library or ApiMap.
11
+ #
12
+ class Workspace
13
+ include Logging
14
+
15
+ autoload :Config, 'solargraph/workspace/config'
16
+ autoload :Gemspecs, 'solargraph/workspace/gemspecs'
17
+ autoload :RequirePaths, 'solargraph/workspace/require_paths'
18
+
19
+ # @return [String]
20
+ attr_reader :directory
21
+
22
+ # @return [Array<String>]
23
+ attr_reader :gemnames
24
+ alias source_gems gemnames
25
+
26
+ # @todo Remove '' and '*' special cases
27
+ # @param directory [String]
28
+ # @param config [Config, nil]
29
+ # @param server [Hash]
30
+ def initialize directory = '', config = nil, server = {}
31
+ raise ArgumentError, 'directory must be a String' unless directory.is_a?(String)
32
+
33
+ @directory = if ['*', ''].include?(directory)
34
+ directory
35
+ else
36
+ File.absolute_path(directory)
37
+ end
38
+ @config = config
39
+ @server = server
40
+ load_sources
41
+ @gemnames = []
42
+ require_plugins
43
+ end
44
+
45
+ # The require paths associated with the workspace.
46
+ #
47
+ # @return [Array<String>]
48
+ def require_paths
49
+ # @todo are the semantics of '*' the same as '', meaning 'don't send back any require paths'?
50
+ @require_paths ||= RequirePaths.new(directory_or_nil, config).generate
51
+ end
52
+
53
+ # @return [Solargraph::Workspace::Config]
54
+ def config
55
+ @config ||= Solargraph::Workspace::Config.new(directory)
56
+ end
57
+
58
+ # @param stdlib_name [String]
59
+ #
60
+ # @return [Array<String>]
61
+ def stdlib_dependencies stdlib_name
62
+ gemspecs.stdlib_dependencies(stdlib_name)
63
+ end
64
+
65
+ # @param out [IO, nil] output stream for logging
66
+ # @param gemspec [Gem::Specification]
67
+ # @return [Array<Gem::Specification>]
68
+ def fetch_dependencies gemspec, out: $stderr
69
+ gemspecs.fetch_dependencies(gemspec, out: out)
70
+ end
71
+
72
+ # @param require [String] The string sent to 'require' in the code to resolve, e.g. 'rails', 'bundler/require'
73
+ # @return [Array<Gem::Specification>, nil]
74
+ def resolve_require require
75
+ gemspecs.resolve_require(require)
76
+ end
77
+
78
+ # @return [Solargraph::PinCache]
79
+ def pin_cache
80
+ @pin_cache ||= fresh_pincache
81
+ end
82
+
83
+ # @param stdlib_name [String]
84
+ #
85
+ # @return [Array<String>]
86
+ def stdlib_dependencies stdlib_name
87
+ deps = RbsMap::StdlibMap.stdlib_dependencies(stdlib_name, nil) || []
88
+ deps.map { |dep| dep['name'] }.compact
89
+ end
90
+
91
+ # @return [Environ]
92
+ def global_environ
93
+ # empty docmap, since the result needs to work in any possible
94
+ # context here
95
+ @global_environ ||= Convention.for_global(DocMap.new([], self, out: nil))
96
+ end
97
+
98
+ # @param gemspec [Gem::Specification]
99
+ # @param out [StringIO, IO, nil] output stream for logging
100
+ # @param rebuild [Boolean] whether to rebuild the pins even if they are cached
101
+ #
102
+ # @return [void]
103
+ def cache_gem gemspec, out: nil, rebuild: false
104
+ pin_cache.cache_gem(gemspec: gemspec, out: out, rebuild: rebuild)
105
+ end
106
+
107
+ # @param gemspec [Gem::Specification, Bundler::LazySpecification]
108
+ # @param out [StringIO, IO, nil] output stream for logging
109
+ #
110
+ # @return [void]
111
+ def uncache_gem gemspec, out: nil
112
+ pin_cache.uncache_gem(gemspec, out: out)
113
+ end
114
+
115
+ # @return [Solargraph::PinCache]
116
+ def fresh_pincache
117
+ PinCache.new(rbs_collection_path: rbs_collection_path,
118
+ rbs_collection_config_path: rbs_collection_config_path,
119
+ yard_plugins: yard_plugins,
120
+ directory: directory)
121
+ end
122
+
123
+ # @return [Array<String>]
124
+ def yard_plugins
125
+ @yard_plugins ||= global_environ.yard_plugins.sort.uniq
126
+ end
127
+
128
+ # @param level [Symbol]
129
+ # @return [TypeChecker::Rules]
130
+ def rules(level)
131
+ @rules ||= TypeChecker::Rules.new(level, config.type_checker_rules)
132
+ end
133
+
134
+ # Merge the source. A merge will update the existing source for the file
135
+ # or add it to the sources if the workspace is configured to include it.
136
+ # The source is ignored if the configuration excludes it.
137
+ #
138
+ # @param sources [Array<Solargraph::Source>]
139
+ # @return [Boolean] True if the source was added to the workspace
140
+ def merge *sources
141
+ # @sg-ignore Need to add nil check here
142
+ unless directory == '*' || sources.all? { |source| source_hash.key?(source.filename) }
143
+ # Reload the config to determine if a new source should be included
144
+ @config = Solargraph::Workspace::Config.new(directory)
145
+ end
146
+
147
+ includes_any = false
148
+ sources.each do |source|
149
+ # @sg-ignore Need to add nil check here
150
+ next unless directory == "*" || config.calculated.include?(source.filename)
151
+
152
+ # @sg-ignore Need to add nil check here
153
+ source_hash[source.filename] = source
154
+ includes_any = true
155
+ end
156
+
157
+ includes_any
158
+ end
159
+
160
+ # Remove a source from the workspace. The source will not be removed if
161
+ # its file exists and the workspace is configured to include it.
162
+ #
163
+ # @param filename [String]
164
+ # @return [Boolean] True if the source was removed from the workspace
165
+ def remove filename
166
+ return false unless source_hash.key?(filename)
167
+ source_hash.delete filename
168
+ true
169
+ end
170
+
171
+ # @return [Array<String>]
172
+ def filenames
173
+ source_hash.keys
174
+ end
175
+
176
+ # @return [Array<Solargraph::Source>]
177
+ def sources
178
+ source_hash.values
179
+ end
180
+
181
+ # @param filename [String]
182
+ # @return [Boolean]
183
+ def has_file? filename
184
+ source_hash.key?(filename)
185
+ end
186
+
187
+ # Get a source by its filename.
188
+ #
189
+ # @param filename [String]
190
+ # @return [Solargraph::Source]
191
+ def source filename
192
+ source_hash[filename]
193
+ end
194
+
195
+ # True if the path resolves to a file in the workspace's require paths.
196
+ #
197
+ # @param path [String]
198
+ # @return [Boolean]
199
+ def would_require? path
200
+ require_paths.each do |rp|
201
+ full = File.join rp, path
202
+ return true if File.file?(full) || File.file?(full << ".rb")
203
+ end
204
+ false
205
+ end
206
+
207
+ # True if the workspace contains at least one gemspec file.
208
+ #
209
+ # @return [Boolean]
210
+ def gemspec?
211
+ !gemspec_files.empty?
212
+ end
213
+
214
+ # Get an array of all gemspec files in the workspace.
215
+ #
216
+ # @return [Array<String>]
217
+ def gemspec_files
218
+ return [] if directory.empty? || directory == '*'
219
+ @gemspec_files ||= Dir[File.join(directory, '**/*.gemspec')].select do |gs|
220
+ config.allow? gs
221
+ end
222
+ end
223
+
224
+ # @return [String, nil]
225
+ def rbs_collection_path
226
+ @gem_rbs_collection ||= read_rbs_collection_path
227
+ end
228
+
229
+ # @return [String, nil]
230
+ def rbs_collection_config_path
231
+ @rbs_collection_config_path ||=
232
+ begin
233
+ unless directory.empty? || directory == '*'
234
+ yaml_file = File.join(directory, 'rbs_collection.yaml')
235
+ yaml_file if File.file?(yaml_file)
236
+ end
237
+ end
238
+ end
239
+
240
+ # @param name [String]
241
+ # @param version [String, nil]
242
+ # @param out [IO, nil]
243
+ #
244
+ # @return [Gem::Specification, nil]
245
+ def find_gem name, version = nil, out: nil
246
+ gemspecs.find_gem(name, version, out: out)
247
+ end
248
+
249
+ # @return [Array<Gem::Specification>]
250
+ def all_gemspecs_from_bundle
251
+ gemspecs.all_gemspecs_from_bundle
252
+ end
253
+
254
+ # @todo make this actually work against bundle instead of pulling
255
+ # all installed gemspecs -
256
+ # https://github.com/apiology/solargraph/pull/10
257
+ # @return [Array<Gem::Specification>]
258
+ def all_gemspecs_from_bundle
259
+ Gem::Specification.to_a
260
+ end
261
+
262
+ # @param out [StringIO, IO, nil] output stream for logging
263
+ # @param rebuild [Boolean] whether to rebuild the pins even if they are cached
264
+ # @return [void]
265
+ def cache_all_for_workspace! out, rebuild: false
266
+ PinCache.cache_core(out: out) unless PinCache.core? && !rebuild
267
+
268
+ gem_specs = all_gemspecs_from_bundle
269
+ # try any possible standard libraries, but be quiet about it
270
+ stdlib_specs = pin_cache.possible_stdlibs.map { |stdlib| find_gem(stdlib, out: nil) }.compact
271
+ specs = (gem_specs + stdlib_specs)
272
+ specs.each do |spec|
273
+ pin_cache.cache_gem(gemspec: spec, rebuild: rebuild, out: out) unless pin_cache.cached?(spec)
274
+ end
275
+ out&.puts "Documentation cached for all #{specs.length} gems."
276
+
277
+ # do this after so that we prefer stdlib requires from gems,
278
+ # which are likely to be newer and have more pins
279
+ pin_cache.cache_all_stdlibs(out: out, rebuild: rebuild)
280
+
281
+ out&.puts "Documentation cached for core, standard library and gems."
282
+ end
283
+
284
+ # Synchronize the workspace from the provided updater.
285
+ #
286
+ # @param updater [Source::Updater]
287
+ # @return [void]
288
+ def synchronize! updater
289
+ source_hash[updater.filename] = source_hash[updater.filename].synchronize(updater)
290
+ end
291
+
292
+ # @sg-ignore Need to validate config
293
+ # @return [String]
294
+ # @sg-ignore Need to validate config
295
+ def command_path
296
+ server['commandPath'] || 'solargraph'
297
+ end
298
+
299
+ # @return [String, nil]
300
+ def directory_or_nil
301
+ return nil if directory.empty? || directory == '*'
302
+ directory
303
+ end
304
+
305
+ # @return [Solargraph::Workspace::Gemspecs]
306
+ def gemspecs
307
+ @gemspecs ||= Solargraph::Workspace::Gemspecs.new(directory_or_nil)
308
+ end
309
+
310
+ private
311
+
312
+ # The language server configuration (or an empty hash if the workspace was
313
+ # not initialized from a server).
314
+ #
315
+ # @return [Hash]
316
+ attr_reader :server
317
+
318
+ # @return [Hash{String => Solargraph::Source}]
319
+ def source_hash
320
+ @source_hash ||= {}
321
+ end
322
+
323
+ # @return [void]
324
+ def load_sources
325
+ source_hash.clear
326
+ unless directory.empty? || directory == '*'
327
+ size = config.calculated.length
328
+ if config.max_files > 0 and size > config.max_files
329
+ raise WorkspaceTooLargeError,
330
+ "The workspace is too large to index (#{size} files, #{config.max_files} max)"
331
+ end
332
+ config.calculated.each do |filename|
333
+ begin
334
+ source_hash[filename] = Solargraph::Source.load(filename)
335
+ rescue Errno::ENOENT => e
336
+ Solargraph.logger.warn("Error loading #{filename}: [#{e.class}] #{e.message}")
337
+ end
338
+ end
339
+ end
340
+ end
341
+
342
+ # @return [void]
343
+ def require_plugins
344
+ config.plugins.each do |plugin|
345
+ begin
346
+ require plugin
347
+ rescue LoadError
348
+ Solargraph.logger.warn "Failed to load plugin '#{plugin}'"
349
+ end
350
+ end
351
+ end
352
+
353
+ # @return [String, nil]
354
+ def read_rbs_collection_path
355
+ return unless rbs_collection_config_path
356
+
357
+ path = YAML.load_file(rbs_collection_config_path)&.fetch('path')
358
+ # make fully qualified
359
+ File.expand_path(path, directory)
360
+ end
361
+ end
362
+ end