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