solargraph 0.58.1 → 0.58.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.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +7 -1
  4. data/lib/solargraph/api_map/cache.rb +110 -110
  5. data/lib/solargraph/api_map/constants.rb +279 -279
  6. data/lib/solargraph/api_map/index.rb +193 -193
  7. data/lib/solargraph/api_map/source_to_yard.rb +97 -97
  8. data/lib/solargraph/api_map/store.rb +384 -384
  9. data/lib/solargraph/api_map.rb +945 -945
  10. data/lib/solargraph/complex_type/type_methods.rb +228 -228
  11. data/lib/solargraph/complex_type/unique_type.rb +482 -482
  12. data/lib/solargraph/complex_type.rb +444 -444
  13. data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -91
  14. data/lib/solargraph/convention/data_definition.rb +105 -105
  15. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -61
  16. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -102
  17. data/lib/solargraph/convention/struct_definition.rb +164 -164
  18. data/lib/solargraph/diagnostics/require_not_found.rb +53 -53
  19. data/lib/solargraph/diagnostics/rubocop.rb +118 -118
  20. data/lib/solargraph/diagnostics/rubocop_helpers.rb +68 -68
  21. data/lib/solargraph/diagnostics/type_check.rb +55 -55
  22. data/lib/solargraph/doc_map.rb +439 -439
  23. data/lib/solargraph/equality.rb +34 -34
  24. data/lib/solargraph/gem_pins.rb +98 -98
  25. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  26. data/lib/solargraph/language_server/host/dispatch.rb +130 -130
  27. data/lib/solargraph/language_server/host/message_worker.rb +112 -112
  28. data/lib/solargraph/language_server/host/sources.rb +99 -99
  29. data/lib/solargraph/language_server/host.rb +878 -878
  30. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +114 -114
  31. data/lib/solargraph/language_server/message/extended/document.rb +23 -23
  32. data/lib/solargraph/language_server/message/text_document/completion.rb +56 -56
  33. data/lib/solargraph/language_server/message/text_document/definition.rb +40 -40
  34. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +26 -26
  35. data/lib/solargraph/language_server/message/text_document/formatting.rb +148 -148
  36. data/lib/solargraph/language_server/message/text_document/hover.rb +58 -58
  37. data/lib/solargraph/language_server/message/text_document/signature_help.rb +24 -24
  38. data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -25
  39. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -23
  40. data/lib/solargraph/library.rb +683 -683
  41. data/lib/solargraph/location.rb +82 -82
  42. data/lib/solargraph/logging.rb +37 -37
  43. data/lib/solargraph/parser/comment_ripper.rb +69 -69
  44. data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -255
  45. data/lib/solargraph/parser/node_processor/base.rb +92 -92
  46. data/lib/solargraph/parser/node_processor.rb +62 -62
  47. data/lib/solargraph/parser/parser_gem/class_methods.rb +149 -149
  48. data/lib/solargraph/parser/parser_gem/node_chainer.rb +166 -166
  49. data/lib/solargraph/parser/parser_gem/node_methods.rb +486 -486
  50. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -22
  51. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +59 -59
  52. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +15 -15
  53. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +46 -46
  54. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +53 -53
  55. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -23
  56. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +40 -40
  57. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +29 -29
  58. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +59 -59
  59. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -98
  60. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
  61. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +38 -38
  62. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +52 -52
  63. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +291 -291
  64. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -29
  65. data/lib/solargraph/parser/parser_gem/node_processors.rb +70 -70
  66. data/lib/solargraph/parser/region.rb +69 -69
  67. data/lib/solargraph/parser/snippet.rb +17 -17
  68. data/lib/solargraph/pin/base.rb +729 -729
  69. data/lib/solargraph/pin/base_variable.rb +126 -126
  70. data/lib/solargraph/pin/block.rb +104 -104
  71. data/lib/solargraph/pin/breakable.rb +9 -9
  72. data/lib/solargraph/pin/callable.rb +231 -231
  73. data/lib/solargraph/pin/closure.rb +72 -72
  74. data/lib/solargraph/pin/common.rb +79 -79
  75. data/lib/solargraph/pin/conversions.rb +123 -123
  76. data/lib/solargraph/pin/delegated_method.rb +120 -120
  77. data/lib/solargraph/pin/documenting.rb +114 -114
  78. data/lib/solargraph/pin/instance_variable.rb +34 -34
  79. data/lib/solargraph/pin/keyword.rb +20 -20
  80. data/lib/solargraph/pin/local_variable.rb +75 -75
  81. data/lib/solargraph/pin/method.rb +672 -672
  82. data/lib/solargraph/pin/method_alias.rb +34 -34
  83. data/lib/solargraph/pin/namespace.rb +115 -115
  84. data/lib/solargraph/pin/parameter.rb +275 -275
  85. data/lib/solargraph/pin/proxy_type.rb +39 -39
  86. data/lib/solargraph/pin/reference/override.rb +47 -47
  87. data/lib/solargraph/pin/reference/superclass.rb +15 -15
  88. data/lib/solargraph/pin/reference.rb +39 -39
  89. data/lib/solargraph/pin/search.rb +61 -61
  90. data/lib/solargraph/pin/signature.rb +61 -61
  91. data/lib/solargraph/pin/symbol.rb +53 -53
  92. data/lib/solargraph/pin/until.rb +18 -18
  93. data/lib/solargraph/pin/while.rb +18 -18
  94. data/lib/solargraph/pin.rb +44 -44
  95. data/lib/solargraph/pin_cache.rb +245 -245
  96. data/lib/solargraph/position.rb +132 -119
  97. data/lib/solargraph/range.rb +112 -112
  98. data/lib/solargraph/rbs_map/conversions.rb +823 -823
  99. data/lib/solargraph/rbs_map/core_map.rb +58 -58
  100. data/lib/solargraph/rbs_map/stdlib_map.rb +43 -43
  101. data/lib/solargraph/rbs_map.rb +163 -163
  102. data/lib/solargraph/shell.rb +352 -352
  103. data/lib/solargraph/source/chain/call.rb +337 -337
  104. data/lib/solargraph/source/chain/constant.rb +26 -26
  105. data/lib/solargraph/source/chain/hash.rb +34 -34
  106. data/lib/solargraph/source/chain/if.rb +28 -28
  107. data/lib/solargraph/source/chain/instance_variable.rb +13 -13
  108. data/lib/solargraph/source/chain/literal.rb +48 -48
  109. data/lib/solargraph/source/chain/or.rb +23 -23
  110. data/lib/solargraph/source/chain.rb +291 -291
  111. data/lib/solargraph/source/change.rb +82 -82
  112. data/lib/solargraph/source/cursor.rb +166 -166
  113. data/lib/solargraph/source/source_chainer.rb +194 -194
  114. data/lib/solargraph/source/updater.rb +55 -55
  115. data/lib/solargraph/source.rb +498 -498
  116. data/lib/solargraph/source_map/clip.rb +226 -226
  117. data/lib/solargraph/source_map/data.rb +34 -34
  118. data/lib/solargraph/source_map/mapper.rb +259 -259
  119. data/lib/solargraph/source_map.rb +212 -212
  120. data/lib/solargraph/type_checker/checks.rb +124 -124
  121. data/lib/solargraph/type_checker/param_def.rb +37 -37
  122. data/lib/solargraph/type_checker/problem.rb +32 -32
  123. data/lib/solargraph/type_checker/rules.rb +84 -84
  124. data/lib/solargraph/type_checker.rb +814 -814
  125. data/lib/solargraph/version.rb +1 -1
  126. data/lib/solargraph/workspace/config.rb +255 -255
  127. data/lib/solargraph/workspace/require_paths.rb +97 -97
  128. data/lib/solargraph/workspace.rb +220 -220
  129. data/lib/solargraph/yard_map/helpers.rb +44 -44
  130. data/lib/solargraph/yard_map/mapper/to_method.rb +130 -130
  131. data/lib/solargraph/yard_map/mapper/to_namespace.rb +31 -31
  132. data/lib/solargraph/yard_map/mapper.rb +79 -79
  133. data/lib/solargraph/yard_map/to_method.rb +89 -89
  134. data/lib/solargraph/yardoc.rb +87 -87
  135. data/lib/solargraph.rb +105 -105
  136. data/rbs_collection.yaml +1 -1
  137. metadata +12 -12
  138. /data/{sig → rbs}/shims/ast/0/node.rbs +0 -0
  139. /data/{sig → rbs}/shims/ast/2.4/.rbs_meta.yaml +0 -0
  140. /data/{sig → rbs}/shims/ast/2.4/ast.rbs +0 -0
  141. /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
  142. /data/{sig → rbs}/shims/parser/3.2.0.1/manifest.yaml +0 -0
  143. /data/{sig → rbs}/shims/parser/3.2.0.1/parser.rbs +0 -0
  144. /data/{sig → rbs}/shims/parser/3.2.0.1/polyfill.rbs +0 -0
  145. /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
  146. /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
  147. /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
@@ -1,97 +1,97 @@
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
- o, e, s = Open3.capture3(*cmd)
80
- if s.success?
81
- begin
82
- hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {}
83
- return [] if hash.empty?
84
- hash['paths'].map { |path| File.join(base, path) }
85
- rescue StandardError => e
86
- Solargraph.logger.warn "Error reading #{gemspec_file_path}: [#{e.class}] #{e.message}"
87
- []
88
- end
89
- else
90
- Solargraph.logger.warn "Error reading #{gemspec_file_path}"
91
- Solargraph.logger.warn e
92
- []
93
- end
94
- end
95
- end
96
- end
97
- end
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
+ o, e, s = Open3.capture3(*cmd)
80
+ if s.success?
81
+ begin
82
+ hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {}
83
+ return [] if hash.empty?
84
+ hash['paths'].map { |path| File.join(base, path) }
85
+ rescue StandardError => e
86
+ Solargraph.logger.warn "Error reading #{gemspec_file_path}: [#{e.class}] #{e.message}"
87
+ []
88
+ end
89
+ else
90
+ Solargraph.logger.warn "Error reading #{gemspec_file_path}"
91
+ Solargraph.logger.warn e
92
+ []
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -1,220 +1,220 @@
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
+
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