solargraph 0.54.4 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +125 -0
  3. data/.github/workflows/plugins.yml +149 -5
  4. data/.github/workflows/rspec.yml +39 -4
  5. data/.github/workflows/typecheck.yml +8 -3
  6. data/.gitignore +7 -0
  7. data/.overcommit.yml +72 -0
  8. data/.rspec +1 -0
  9. data/.rubocop.yml +66 -0
  10. data/.rubocop_todo.yml +2627 -0
  11. data/.yardopts +1 -0
  12. data/CHANGELOG.md +104 -0
  13. data/README.md +20 -6
  14. data/Rakefile +125 -13
  15. data/lib/solargraph/api_map/cache.rb +3 -2
  16. data/lib/solargraph/api_map/constants.rb +218 -0
  17. data/lib/solargraph/api_map/index.rb +44 -42
  18. data/lib/solargraph/api_map/source_to_yard.rb +10 -4
  19. data/lib/solargraph/api_map/store.rb +165 -32
  20. data/lib/solargraph/api_map.rb +319 -243
  21. data/lib/solargraph/bench.rb +18 -1
  22. data/lib/solargraph/complex_type/type_methods.rb +7 -1
  23. data/lib/solargraph/complex_type/unique_type.rb +105 -16
  24. data/lib/solargraph/complex_type.rb +40 -7
  25. data/lib/solargraph/convention/active_support_concern.rb +111 -0
  26. data/lib/solargraph/convention/base.rb +20 -3
  27. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -0
  28. data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -0
  29. data/lib/solargraph/convention/data_definition.rb +105 -0
  30. data/lib/solargraph/convention/gemspec.rb +3 -2
  31. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -0
  32. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -0
  33. data/lib/solargraph/convention/struct_definition.rb +164 -0
  34. data/lib/solargraph/convention.rb +35 -4
  35. data/lib/solargraph/diagnostics/rubocop.rb +6 -1
  36. data/lib/solargraph/diagnostics/rubocop_helpers.rb +1 -1
  37. data/lib/solargraph/doc_map.rb +313 -65
  38. data/lib/solargraph/environ.rb +9 -2
  39. data/lib/solargraph/gem_pins.rb +60 -38
  40. data/lib/solargraph/language_server/host/dispatch.rb +2 -0
  41. data/lib/solargraph/language_server/host/message_worker.rb +13 -7
  42. data/lib/solargraph/language_server/host.rb +14 -3
  43. data/lib/solargraph/language_server/message/base.rb +2 -1
  44. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  45. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  46. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  47. data/lib/solargraph/language_server/message/text_document/definition.rb +2 -0
  48. data/lib/solargraph/language_server/message/text_document/formatting.rb +16 -2
  49. data/lib/solargraph/language_server/message/text_document/type_definition.rb +1 -0
  50. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +2 -0
  51. data/lib/solargraph/language_server/progress.rb +8 -0
  52. data/lib/solargraph/language_server/request.rb +1 -0
  53. data/lib/solargraph/library.rb +53 -32
  54. data/lib/solargraph/location.rb +23 -0
  55. data/lib/solargraph/logging.rb +12 -2
  56. data/lib/solargraph/page.rb +4 -0
  57. data/lib/solargraph/parser/comment_ripper.rb +20 -7
  58. data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -0
  59. data/lib/solargraph/parser/node_methods.rb +16 -2
  60. data/lib/solargraph/parser/node_processor/base.rb +10 -5
  61. data/lib/solargraph/parser/node_processor.rb +26 -9
  62. data/lib/solargraph/parser/parser_gem/class_methods.rb +17 -15
  63. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -0
  64. data/lib/solargraph/parser/parser_gem/node_chainer.rb +13 -11
  65. data/lib/solargraph/parser/parser_gem/node_methods.rb +8 -4
  66. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  67. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  68. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
  69. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +7 -4
  70. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
  71. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  72. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  73. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  74. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  75. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -0
  76. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  77. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
  78. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +7 -1
  79. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
  80. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +42 -0
  81. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
  82. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +3 -1
  83. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
  84. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +63 -30
  85. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
  86. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  87. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  88. data/lib/solargraph/parser/parser_gem/node_processors.rb +14 -0
  89. data/lib/solargraph/parser/region.rb +4 -1
  90. data/lib/solargraph/parser/snippet.rb +2 -0
  91. data/lib/solargraph/parser.rb +1 -0
  92. data/lib/solargraph/pin/base.rb +360 -30
  93. data/lib/solargraph/pin/base_variable.rb +16 -10
  94. data/lib/solargraph/pin/block.rb +2 -0
  95. data/lib/solargraph/pin/breakable.rb +9 -0
  96. data/lib/solargraph/pin/callable.rb +83 -3
  97. data/lib/solargraph/pin/closure.rb +20 -1
  98. data/lib/solargraph/pin/common.rb +10 -1
  99. data/lib/solargraph/pin/constant.rb +2 -0
  100. data/lib/solargraph/pin/delegated_method.rb +21 -1
  101. data/lib/solargraph/pin/documenting.rb +16 -0
  102. data/lib/solargraph/pin/keyword.rb +7 -2
  103. data/lib/solargraph/pin/local_variable.rb +18 -6
  104. data/lib/solargraph/pin/method.rb +175 -46
  105. data/lib/solargraph/pin/method_alias.rb +3 -0
  106. data/lib/solargraph/pin/namespace.rb +17 -9
  107. data/lib/solargraph/pin/parameter.rb +78 -19
  108. data/lib/solargraph/pin/proxy_type.rb +13 -6
  109. data/lib/solargraph/pin/reference/override.rb +24 -6
  110. data/lib/solargraph/pin/reference/require.rb +2 -2
  111. data/lib/solargraph/pin/reference/superclass.rb +5 -0
  112. data/lib/solargraph/pin/reference.rb +26 -0
  113. data/lib/solargraph/pin/search.rb +3 -1
  114. data/lib/solargraph/pin/signature.rb +44 -0
  115. data/lib/solargraph/pin/singleton.rb +1 -1
  116. data/lib/solargraph/pin/symbol.rb +8 -2
  117. data/lib/solargraph/pin/until.rb +18 -0
  118. data/lib/solargraph/pin/while.rb +18 -0
  119. data/lib/solargraph/pin.rb +4 -1
  120. data/lib/solargraph/pin_cache.rb +245 -0
  121. data/lib/solargraph/position.rb +11 -0
  122. data/lib/solargraph/range.rb +10 -0
  123. data/lib/solargraph/rbs_map/conversions.rb +226 -70
  124. data/lib/solargraph/rbs_map/core_fills.rb +32 -16
  125. data/lib/solargraph/rbs_map/core_map.rb +37 -11
  126. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  127. data/lib/solargraph/rbs_map.rb +88 -18
  128. data/lib/solargraph/shell.rb +20 -18
  129. data/lib/solargraph/source/chain/array.rb +11 -7
  130. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  131. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  132. data/lib/solargraph/source/chain/call.rb +53 -23
  133. data/lib/solargraph/source/chain/constant.rb +1 -1
  134. data/lib/solargraph/source/chain/hash.rb +4 -3
  135. data/lib/solargraph/source/chain/head.rb +1 -1
  136. data/lib/solargraph/source/chain/if.rb +1 -1
  137. data/lib/solargraph/source/chain/link.rb +12 -1
  138. data/lib/solargraph/source/chain/literal.rb +22 -2
  139. data/lib/solargraph/source/chain/or.rb +1 -1
  140. data/lib/solargraph/source/chain/z_super.rb +1 -1
  141. data/lib/solargraph/source/chain.rb +84 -47
  142. data/lib/solargraph/source/change.rb +2 -2
  143. data/lib/solargraph/source/cursor.rb +2 -3
  144. data/lib/solargraph/source/source_chainer.rb +3 -3
  145. data/lib/solargraph/source.rb +5 -2
  146. data/lib/solargraph/source_map/clip.rb +4 -2
  147. data/lib/solargraph/source_map/data.rb +4 -0
  148. data/lib/solargraph/source_map/mapper.rb +13 -7
  149. data/lib/solargraph/source_map.rb +21 -31
  150. data/lib/solargraph/type_checker/checks.rb +4 -0
  151. data/lib/solargraph/type_checker/param_def.rb +2 -0
  152. data/lib/solargraph/type_checker/rules.rb +8 -0
  153. data/lib/solargraph/type_checker.rb +208 -128
  154. data/lib/solargraph/version.rb +1 -1
  155. data/lib/solargraph/views/_method.erb +10 -10
  156. data/lib/solargraph/views/_namespace.erb +3 -3
  157. data/lib/solargraph/views/document.erb +10 -10
  158. data/lib/solargraph/workspace/config.rb +1 -3
  159. data/lib/solargraph/workspace/require_paths.rb +98 -0
  160. data/lib/solargraph/workspace.rb +38 -52
  161. data/lib/solargraph/yard_map/helpers.rb +29 -1
  162. data/lib/solargraph/yard_map/mapper/to_constant.rb +7 -5
  163. data/lib/solargraph/yard_map/mapper/to_method.rb +53 -18
  164. data/lib/solargraph/yard_map/mapper/to_namespace.rb +9 -7
  165. data/lib/solargraph/yard_map/mapper.rb +4 -3
  166. data/lib/solargraph/yard_map/to_method.rb +4 -2
  167. data/lib/solargraph/yardoc.rb +22 -10
  168. data/lib/solargraph.rb +34 -1
  169. data/rbs/fills/tuple.rbs +149 -0
  170. data/rbs_collection.yaml +19 -0
  171. data/sig/shims/parser/3.2.0.1/builders/default.rbs +195 -0
  172. data/sig/shims/thor/1.2.0.1/.rbs_meta.yaml +9 -0
  173. data/sig/shims/thor/1.2.0.1/manifest.yaml +7 -0
  174. data/sig/shims/thor/1.2.0.1/thor.rbs +17 -0
  175. data/solargraph.gemspec +15 -4
  176. metadata +157 -15
  177. data/lib/.rubocop.yml +0 -22
  178. data/lib/solargraph/cache.rb +0 -77
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+
5
+ module Solargraph
6
+ # A workspace consists of the files in a project's directory and the
7
+ # project's configuration. It provides a Source for each file to be used
8
+ # in an associated Library or ApiMap.
9
+ #
10
+ class Workspace
11
+ # Manages determining which gemspecs are available in a workspace
12
+ class RequirePaths
13
+ attr_reader :directory, :config
14
+
15
+ # @param directory [String, nil]
16
+ # @param config [Config, nil]
17
+ def initialize directory, config
18
+ @directory = directory
19
+ @config = config
20
+ end
21
+
22
+ # Generate require paths from gemspecs if they exist or assume the default
23
+ # lib directory.
24
+ #
25
+ # @return [Array<String>]
26
+ def generate
27
+ result = require_paths_from_gemspec_files
28
+ return configured_require_paths if result.empty?
29
+ result.concat(config.require_paths.map { |p| File.join(directory, p) }) if config
30
+ result
31
+ end
32
+
33
+ private
34
+
35
+ # @return [Array<String>]
36
+ def require_paths_from_gemspec_files
37
+ results = []
38
+ gemspec_file_paths.each do |gemspec_file_path|
39
+ results.concat require_path_from_gemspec_file(gemspec_file_path)
40
+ end
41
+ results
42
+ end
43
+
44
+ # Get an array of all gemspec files in the workspace.
45
+ #
46
+ # @return [Array<String>]
47
+ def gemspec_file_paths
48
+ return [] if directory.nil?
49
+ @gemspec_file_paths ||= Dir[File.join(directory, '**/*.gemspec')].select do |gs|
50
+ config.nil? || config.allow?(gs)
51
+ end
52
+ end
53
+
54
+ # Get additional require paths defined in the configuration.
55
+ #
56
+ # @return [Array<String>]
57
+ def configured_require_paths
58
+ return ['lib'] unless directory
59
+ return [File.join(directory, 'lib')] if !config || config.require_paths.empty?
60
+ config.require_paths.map { |p| File.join(directory, p) }
61
+ end
62
+
63
+ # Generate require paths from gemspecs if they exist or assume the default
64
+ # lib directory.
65
+ #
66
+ # @param gemspec_file_path [String]
67
+ # @return [Array<String>]
68
+ def require_path_from_gemspec_file gemspec_file_path
69
+ base = File.dirname(gemspec_file_path)
70
+ # HACK: Evaluating gemspec files violates the goal of not running
71
+ # workspace code, but this is how Gem::Specification.load does it
72
+ # anyway.
73
+ cmd = ['ruby', '-e',
74
+ "require 'rubygems'; " \
75
+ "require 'json'; " \
76
+ "spec = eval(File.read('#{gemspec_file_path}'), TOPLEVEL_BINDING, '#{gemspec_file_path}'); " \
77
+ 'return unless Gem::Specification === spec; ' \
78
+ 'puts({name: spec.name, paths: spec.require_paths}.to_json)']
79
+ # @sg-ignore Unresolved call to capture3 on Module<Open3>
80
+ o, e, s = Open3.capture3(*cmd)
81
+ if s.success?
82
+ begin
83
+ hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {}
84
+ return [] if hash.empty?
85
+ hash['paths'].map { |path| File.join(base, path) }
86
+ rescue StandardError => e
87
+ Solargraph.logger.warn "Error reading #{gemspec_file_path}: [#{e.class}] #{e.message}"
88
+ []
89
+ end
90
+ else
91
+ Solargraph.logger.warn "Error reading #{gemspec_file_path}"
92
+ Solargraph.logger.warn e
93
+ []
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -10,15 +10,11 @@ module Solargraph
10
10
  #
11
11
  class Workspace
12
12
  autoload :Config, 'solargraph/workspace/config'
13
+ autoload :RequirePaths, 'solargraph/workspace/require_paths'
13
14
 
14
15
  # @return [String]
15
16
  attr_reader :directory
16
17
 
17
- # The require paths associated with the workspace.
18
- #
19
- # @return [Array<String>]
20
- attr_reader :require_paths
21
-
22
18
  # @return [Array<String>]
23
19
  attr_reader :gemnames
24
20
  alias source_gems gemnames
@@ -32,10 +28,17 @@ module Solargraph
32
28
  @server = server
33
29
  load_sources
34
30
  @gemnames = []
35
- @require_paths = generate_require_paths
36
31
  require_plugins
37
32
  end
38
33
 
34
+ # The require paths associated with the workspace.
35
+ #
36
+ # @return [Array<String>]
37
+ def require_paths
38
+ # @todo are the semantics of '*' the same as '', meaning 'don't send back any require paths'?
39
+ @require_paths ||= RequirePaths.new(directory_or_nil, config).generate
40
+ end
41
+
39
42
  # @return [Solargraph::Workspace::Config]
40
43
  def config
41
44
  @config ||= Solargraph::Workspace::Config.new(directory)
@@ -106,7 +109,7 @@ module Solargraph
106
109
  def would_require? path
107
110
  require_paths.each do |rp|
108
111
  full = File.join rp, path
109
- return true if File.exist?(full) or File.exist?(full << ".rb")
112
+ return true if File.file?(full) || File.file?(full << ".rb")
110
113
  end
111
114
  false
112
115
  end
@@ -133,6 +136,16 @@ module Solargraph
133
136
  @gem_rbs_collection ||= read_rbs_collection_path
134
137
  end
135
138
 
139
+ # @return [String, nil]
140
+ def rbs_collection_config_path
141
+ @rbs_collection_config_path ||= begin
142
+ unless directory.empty? || directory == '*'
143
+ yaml_file = File.join(directory, 'rbs_collection.yaml')
144
+ yaml_file if File.file?(yaml_file)
145
+ end
146
+ end
147
+ end
148
+
136
149
  # Synchronize the workspace from the provided updater.
137
150
  #
138
151
  # @param updater [Source::Updater]
@@ -146,6 +159,20 @@ module Solargraph
146
159
  server['commandPath'] || 'solargraph'
147
160
  end
148
161
 
162
+ # @return [String, nil]
163
+ def directory_or_nil
164
+ return nil if directory.empty? || directory == '*'
165
+ directory
166
+ end
167
+
168
+ # True if the workspace has a root Gemfile.
169
+ #
170
+ # @todo Handle projects with custom Bundler/Gemfile setups (see DocMap#gemspecs_required_from_bundler)
171
+ #
172
+ def gemfile?
173
+ directory && File.file?(File.join(directory, 'Gemfile'))
174
+ end
175
+
149
176
  private
150
177
 
151
178
  # The language server configuration (or an empty hash if the workspace was
@@ -175,48 +202,6 @@ module Solargraph
175
202
  end
176
203
  end
177
204
 
178
- # Generate require paths from gemspecs if they exist or assume the default
179
- # lib directory.
180
- #
181
- # @return [Array<String>]
182
- def generate_require_paths
183
- return configured_require_paths unless gemspec?
184
- result = []
185
- gemspecs.each do |file|
186
- base = File.dirname(file)
187
- # HACK: Evaluating gemspec files violates the goal of not running
188
- # workspace code, but this is how Gem::Specification.load does it
189
- # anyway.
190
- cmd = ['ruby', '-e', "require 'rubygems'; require 'json'; spec = eval(File.read('#{file}'), TOPLEVEL_BINDING, '#{file}'); return unless Gem::Specification === spec; puts({name: spec.name, paths: spec.require_paths}.to_json)"]
191
- o, e, s = Open3.capture3(*cmd)
192
- if s.success?
193
- begin
194
- hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {}
195
- next if hash.empty?
196
- @gemnames.push hash['name']
197
- result.concat(hash['paths'].map { |path| File.join(base, path) })
198
- rescue StandardError => e
199
- Solargraph.logger.warn "Error reading #{file}: [#{e.class}] #{e.message}"
200
- end
201
- else
202
- Solargraph.logger.warn "Error reading #{file}"
203
- Solargraph.logger.warn e
204
- end
205
- end
206
- result.concat(config.require_paths.map { |p| File.join(directory, p) })
207
- result.push File.join(directory, 'lib') if result.empty?
208
- result
209
- end
210
-
211
- # Get additional require paths defined in the configuration.
212
- #
213
- # @return [Array<String>]
214
- def configured_require_paths
215
- return ['lib'] if directory.empty?
216
- return [File.join(directory, 'lib')] if config.require_paths.empty?
217
- config.require_paths.map{|p| File.join(directory, p)}
218
- end
219
-
220
205
  # @return [void]
221
206
  def require_plugins
222
207
  config.plugins.each do |plugin|
@@ -230,10 +215,11 @@ module Solargraph
230
215
 
231
216
  # @return [String, nil]
232
217
  def read_rbs_collection_path
233
- yaml_file = File.join(directory, 'rbs_collection.yaml')
234
- return unless File.file?(yaml_file)
218
+ return unless rbs_collection_config_path
235
219
 
236
- YAML.load_file(yaml_file)&.fetch('path')
220
+ path = YAML.load_file(rbs_collection_config_path)&.fetch('path')
221
+ # make fully qualified
222
+ File.expand_path(path, directory)
237
223
  end
238
224
  end
239
225
  end
@@ -7,10 +7,38 @@ module Solargraph
7
7
  # @param spec [Gem::Specification, nil]
8
8
  # @return [Solargraph::Location, nil]
9
9
  def object_location code_object, spec
10
- return nil if spec.nil? || code_object.nil? || code_object.file.nil? || code_object.line.nil?
10
+ if spec.nil? || code_object.nil? || code_object.file.nil? || code_object.line.nil?
11
+ if code_object.namespace.is_a?(YARD::CodeObjects::NamespaceObject)
12
+ # If the code object is a namespace, use the namespace's location
13
+ return object_location(code_object.namespace, spec)
14
+ end
15
+ return Solargraph::Location.new(__FILE__, Solargraph::Range.from_to(__LINE__ - 1, 0, __LINE__ - 1, 0))
16
+ end
11
17
  file = File.join(spec.full_gem_path, code_object.file)
12
18
  Solargraph::Location.new(file, Solargraph::Range.from_to(code_object.line - 1, 0, code_object.line - 1, 0))
13
19
  end
20
+
21
+ # @param code_object [YARD::CodeObjects::Base]
22
+ # @param spec [Gem::Specification, nil]
23
+ # @return [Solargraph::Pin::Namespace]
24
+ def create_closure_namespace_for(code_object, spec)
25
+ code_object_for_location = code_object
26
+ # code_object.namespace is sometimes a YARD proxy object pointing to a method path ("Object#new")
27
+ code_object_for_location = code_object.namespace if code_object.namespace.is_a?(YARD::CodeObjects::NamespaceObject)
28
+ namespace_location = object_location(code_object_for_location, spec)
29
+ ns_name = code_object.namespace.to_s
30
+ if ns_name.empty?
31
+ Solargraph::Pin::ROOT_PIN
32
+ else
33
+ Solargraph::Pin::Namespace.new(
34
+ name: ns_name,
35
+ closure: Pin::ROOT_PIN,
36
+ gates: [code_object.namespace.to_s],
37
+ source: :yardoc,
38
+ location: namespace_location
39
+ )
40
+ end
41
+ end
14
42
  end
15
43
  end
16
44
  end
@@ -7,17 +7,19 @@ module Solargraph
7
7
  extend YardMap::Helpers
8
8
 
9
9
  # @param code_object [YARD::CodeObjects::Base]
10
+ # @param closure [Pin::Closure, nil]
11
+ # @param spec [Gem::Specification, nil]
12
+ # @return [Pin::Constant]
10
13
  def self.make code_object, closure = nil, spec = nil
11
- closure ||= Solargraph::Pin::Namespace.new(
12
- name: code_object.namespace.to_s,
13
- gates: [code_object.namespace.to_s]
14
- )
14
+ closure ||= create_closure_namespace_for(code_object, spec)
15
+
15
16
  Pin::Constant.new(
16
17
  location: object_location(code_object, spec),
17
18
  closure: closure,
18
19
  name: code_object.name.to_s,
19
20
  comments: code_object.docstring ? code_object.docstring.all.to_s : '',
20
- visibility: code_object.visibility
21
+ visibility: code_object.visibility,
22
+ source: :yardoc
21
23
  )
22
24
  end
23
25
  end
@@ -6,6 +6,11 @@ module Solargraph
6
6
  module ToMethod
7
7
  extend YardMap::Helpers
8
8
 
9
+ VISIBILITY_OVERRIDE = {
10
+ # YARD pays attention to 'private' statements prior to class methods but shouldn't
11
+ ["Rails::Engine", :class, "find_root_with_flag"] => :public
12
+ }
13
+
9
14
  # @param code_object [YARD::CodeObjects::Base]
10
15
  # @param name [String, nil]
11
16
  # @param scope [Symbol, nil]
@@ -14,31 +19,60 @@ module Solargraph
14
19
  # @param spec [Gem::Specification, nil]
15
20
  # @return [Solargraph::Pin::Method]
16
21
  def self.make code_object, name = nil, scope = nil, visibility = nil, closure = nil, spec = nil
17
- closure ||= Solargraph::Pin::Namespace.new(
18
- name: code_object.namespace.to_s,
19
- gates: [code_object.namespace.to_s]
20
- )
22
+ closure ||= create_closure_namespace_for(code_object, spec)
21
23
  location = object_location(code_object, spec)
22
24
  name ||= code_object.name.to_s
23
25
  return_type = ComplexType::SELF if name == 'new'
24
26
  comments = code_object.docstring ? code_object.docstring.all.to_s : ''
25
- pin = Pin::Method.new(
26
- location: location,
27
- closure: closure,
28
- name: name,
29
- comments: comments,
30
- scope: scope || code_object.scope,
31
- visibility: visibility || code_object.visibility,
32
- # @todo Might need to convert overloads to signatures
33
- parameters: [],
34
- explicit: code_object.is_explicit?,
35
- return_type: return_type
36
- )
37
- pin.parameters.concat get_parameters(code_object, location, comments, pin)
27
+ final_scope = scope || code_object.scope
28
+ override_key = [closure.path, final_scope, name]
29
+ final_visibility = VISIBILITY_OVERRIDE[override_key]
30
+ final_visibility ||= VISIBILITY_OVERRIDE[[closure.path, final_scope]]
31
+ final_visibility ||= :private if closure.path == 'Kernel' && Kernel.private_instance_methods(false).include?(name.to_sym)
32
+ final_visibility ||= visibility
33
+ final_visibility ||= :private if code_object.module_function? && final_scope == :instance
34
+ final_visibility ||= :public if code_object.module_function? && final_scope == :class
35
+ final_visibility ||= code_object.visibility
36
+ if code_object.is_alias?
37
+ origin_code_object = code_object.namespace.aliases[code_object]
38
+ pin = Pin::MethodAlias.new(
39
+ name: name,
40
+ location: location,
41
+ original: origin_code_object.name.to_s,
42
+ closure: closure,
43
+ comments: comments,
44
+ scope: final_scope,
45
+ visibility: final_visibility,
46
+ explicit: code_object.is_explicit?,
47
+ return_type: return_type,
48
+ parameters: [],
49
+ source: :yardoc,
50
+ )
51
+ else
52
+ pin = Pin::Method.new(
53
+ location: location,
54
+ closure: closure,
55
+ name: name,
56
+ comments: comments,
57
+ scope: final_scope,
58
+ visibility: final_visibility,
59
+ # @todo Might need to convert overloads to signatures
60
+ explicit: code_object.is_explicit?,
61
+ return_type: return_type,
62
+ attribute: code_object.is_attribute?,
63
+ parameters: [],
64
+ source: :yardoc,
65
+ )
66
+ pin.parameters.concat get_parameters(code_object, location, comments, pin)
67
+ pin.parameters.freeze
68
+ end
69
+ logger.debug { "ToMethod.make: Just created method pin: #{pin.inspect}" }
38
70
  pin
39
71
  end
40
72
 
41
73
  class << self
74
+ include Logging
75
+
42
76
  private
43
77
 
44
78
  # @param code_object [YARD::CodeObjects::Base]
@@ -59,7 +93,8 @@ module Solargraph
59
93
  name: arg_name(a),
60
94
  presence: nil,
61
95
  decl: arg_type(a),
62
- asgn_code: a[1]
96
+ asgn_code: a[1],
97
+ source: :yardoc,
63
98
  )
64
99
  end
65
100
  end
@@ -7,19 +7,21 @@ module Solargraph
7
7
  extend YardMap::Helpers
8
8
 
9
9
  # @param code_object [YARD::CodeObjects::NamespaceObject]
10
+ # @param spec [Gem::Specification, nil]
11
+ # @param closure [Pin::Closure, nil]
12
+ # @return [Pin::Namespace]
10
13
  def self.make code_object, spec, closure = nil
11
- closure ||= Solargraph::Pin::Namespace.new(
12
- name: code_object.namespace.to_s,
13
- closure: Pin::ROOT_PIN,
14
- gates: [code_object.namespace.to_s]
15
- )
14
+ closure ||= create_closure_namespace_for(code_object, spec)
15
+ location = object_location(code_object, spec)
16
+
16
17
  Pin::Namespace.new(
17
- location: object_location(code_object, spec),
18
+ location: location,
18
19
  name: code_object.name.to_s,
19
20
  comments: code_object.docstring ? code_object.docstring.all.to_s : '',
20
21
  type: code_object.is_a?(YARD::CodeObjects::ClassObject) ? :class : :module,
21
22
  visibility: code_object.visibility,
22
- closure: closure
23
+ closure: closure,
24
+ source: :yardoc,
23
25
  )
24
26
  end
25
27
  end
@@ -47,15 +47,16 @@ module Solargraph
47
47
  else
48
48
  code_object.superclass.to_s
49
49
  end
50
- result.push Solargraph::Pin::Reference::Superclass.new(name: superclass, closure: nspin)
50
+ result.push Solargraph::Pin::Reference::Superclass.new(name: superclass, closure: nspin, source: :yard_map)
51
51
  end
52
52
  code_object.class_mixins.each do |m|
53
- result.push Solargraph::Pin::Reference::Extend.new(closure: nspin, name: m.path)
53
+ result.push Solargraph::Pin::Reference::Extend.new(closure: nspin, name: m.path, source: :yard_map)
54
54
  end
55
55
  code_object.instance_mixins.each do |m|
56
56
  result.push Solargraph::Pin::Reference::Include.new(
57
57
  closure: nspin, # @todo Fix this
58
- name: m.path
58
+ name: m.path,
59
+ source: :yard_map
59
60
  )
60
61
  end
61
62
  elsif code_object.is_a?(YARD::CodeObjects::MethodObject)
@@ -23,7 +23,8 @@ module Solargraph
23
23
  name: arg_name(a),
24
24
  presence: nil,
25
25
  decl: arg_type(a),
26
- asgn_code: a[1]
26
+ asgn_code: a[1],
27
+ source: :yard_map
27
28
  )
28
29
  end
29
30
  end
@@ -78,7 +79,8 @@ module Solargraph
78
79
  scope: scope || code_object.scope,
79
80
  visibility: visibility || code_object.visibility,
80
81
  parameters: InnerMethods.get_parameters(code_object, location, comments),
81
- explicit: code_object.is_explicit?
82
+ explicit: code_object.is_explicit?,
83
+ source: :yard_map
82
84
  )
83
85
  end
84
86
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'open3'
4
+
3
5
  module Solargraph
4
6
  # Methods for caching and loading YARD documentation for gems.
5
7
  #
@@ -9,15 +11,25 @@ module Solargraph
9
11
  # Build and cache a gem's yardoc and return the path. If the cache already
10
12
  # exists, do nothing and return the path.
11
13
  #
14
+ # @param yard_plugins [Array<String>] The names of YARD plugins to use.
12
15
  # @param gemspec [Gem::Specification]
13
16
  # @return [String] The path to the cached yardoc.
14
- def cache(gemspec)
15
- path = path_for(gemspec)
17
+ def cache(yard_plugins, gemspec)
18
+ path = PinCache.yardoc_path gemspec
16
19
  return path if cached?(gemspec)
17
20
 
18
21
  Solargraph.logger.info "Caching yardoc for #{gemspec.name} #{gemspec.version}"
19
- Dir.chdir gemspec.gem_dir do
20
- `yardoc --db #{path} --no-output --plugin solargraph`
22
+ cmd = "yardoc --db #{path} --no-output --plugin solargraph"
23
+ yard_plugins.each { |plugin| cmd << " --plugin #{plugin}" }
24
+ Solargraph.logger.debug { "Running: #{cmd}" }
25
+ # @todo set these up to run in parallel
26
+ #
27
+ # @sg-ignore RBS gem doesn't reflect that Open3.* also include
28
+ # kwopts from Process.spawn()
29
+ stdout_and_stderr_str, status = Open3.capture2e(cmd, chdir: gemspec.gem_dir)
30
+ unless status.success?
31
+ Solargraph.logger.warn { "YARD failed running #{cmd.inspect} in #{gemspec.gem_dir}" }
32
+ Solargraph.logger.info stdout_and_stderr_str
21
33
  end
22
34
  path
23
35
  end
@@ -26,16 +38,16 @@ module Solargraph
26
38
  #
27
39
  # @param gemspec [Gem::Specification]
28
40
  def cached?(gemspec)
29
- yardoc = File.join(path_for(gemspec), 'complete')
41
+ yardoc = File.join(PinCache.yardoc_path(gemspec), 'complete')
30
42
  File.exist?(yardoc)
31
43
  end
32
44
 
33
- # Get the absolute path for a cached gem yardoc.
45
+ # True if another process is currently building the yardoc cache.
34
46
  #
35
47
  # @param gemspec [Gem::Specification]
36
- # @return [String]
37
- def path_for(gemspec)
38
- File.join(Solargraph::Cache.work_dir, 'gems', "#{gemspec.name}-#{gemspec.version}.yardoc")
48
+ def processing?(gemspec)
49
+ yardoc = File.join(PinCache.yardoc_path(gemspec), 'processing')
50
+ File.exist?(yardoc)
39
51
  end
40
52
 
41
53
  # Load a gem's yardoc and return its code objects.
@@ -45,7 +57,7 @@ module Solargraph
45
57
  # @param gemspec [Gem::Specification]
46
58
  # @return [Array<YARD::CodeObjects::Base>]
47
59
  def load!(gemspec)
48
- YARD::Registry.load! path_for(gemspec)
60
+ YARD::Registry.load! PinCache.yardoc_path gemspec
49
61
  YARD::Registry.all
50
62
  end
51
63
  end
data/lib/solargraph.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  Encoding.default_external = 'UTF-8'
4
4
 
5
+ require 'bundler'
5
6
  require 'set'
6
7
  require 'yard-solargraph'
7
8
  require 'solargraph/yard_tags'
@@ -47,11 +48,34 @@ module Solargraph
47
48
  autoload :Parser, 'solargraph/parser'
48
49
  autoload :RbsMap, 'solargraph/rbs_map'
49
50
  autoload :GemPins, 'solargraph/gem_pins'
50
- autoload :Cache, 'solargraph/cache'
51
+ autoload :PinCache, 'solargraph/pin_cache'
51
52
 
52
53
  dir = File.dirname(__FILE__)
53
54
  VIEWS_PATH = File.join(dir, 'solargraph', 'views')
54
55
 
56
+ CHDIR_MUTEX = Mutex.new
57
+
58
+ # @param type [Symbol] Type of assert.
59
+ def self.asserts_on?(type)
60
+ if ENV['SOLARGRAPH_ASSERTS'].nil? || ENV['SOLARGRAPH_ASSERTS'].empty?
61
+ false
62
+ elsif ENV['SOLARGRAPH_ASSERTS'] == 'on'
63
+ true
64
+ else
65
+ logger.warn "Unrecognized SOLARGRAPH_ASSERTS value: #{ENV['SOLARGRAPH_ASSERTS']}"
66
+ false
67
+ end
68
+ end
69
+
70
+ # @param type [Symbol] The type of assertion to perform.
71
+ # @param msg [String, nil] An optional message to log
72
+ # @param block [Proc] A block that returns a message to log
73
+ # @return [void]
74
+ def self.assert_or_log(type, msg = nil, &block)
75
+ raise (msg || block.call) if asserts_on?(type) && ![:combine_with_visibility].include?(type)
76
+ logger.info msg, &block
77
+ end
78
+
55
79
  # A convenience method for Solargraph::Logging.logger.
56
80
  #
57
81
  # @return [Logger]
@@ -61,6 +85,11 @@ module Solargraph
61
85
 
62
86
  # A helper method that runs Bundler.with_unbundled_env or falls back to
63
87
  # Bundler.with_clean_env for earlier versions of Bundler.
88
+ #
89
+ # @generic T
90
+ # @yieldreturn [generic<T>]
91
+ # @sg-ignore dynamic call, but both functions behave the same
92
+ # @return [generic<T>]
64
93
  def self.with_clean_env &block
65
94
  meth = if Bundler.respond_to?(:with_original_env)
66
95
  :with_original_env
@@ -70,3 +99,7 @@ module Solargraph
70
99
  Bundler.send meth, &block
71
100
  end
72
101
  end
102
+
103
+ # Ensure that ParserGem node processors are properly loaded to avoid conflicts
104
+ # with Convention node processors
105
+ require 'solargraph/parser/parser_gem/node_processors'