yoda-language-server 0.8.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/README.md +22 -27
  4. data/client/vscode/README.md +17 -2
  5. data/client/vscode/package-lock.json +908 -817
  6. data/client/vscode/package.json +17 -7
  7. data/client/vscode/src/check-versions.ts +49 -0
  8. data/client/vscode/src/config.ts +8 -1
  9. data/client/vscode/src/extension.ts +4 -3
  10. data/client/vscode/src/install-tools.ts +56 -16
  11. data/client/vscode/src/language-server.ts +5 -0
  12. data/client/vscode/src/test/runTest.ts +1 -1
  13. data/client/vscode/src/test/suite/hover.test.ts +13 -25
  14. data/client/vscode/src/test/suite/index.ts +1 -2
  15. data/client/vscode/src/utils.ts +11 -0
  16. data/images/hover-method.png +0 -0
  17. data/images/method-complete.png +0 -0
  18. data/lib/yoda/cli/analyze_deps.rb +46 -25
  19. data/lib/yoda/id_mask.rb +84 -0
  20. data/lib/yoda/instrument.rb +7 -0
  21. data/lib/yoda/model/descriptions/require_path_description.rb +45 -0
  22. data/lib/yoda/model/descriptions.rb +1 -0
  23. data/lib/yoda/model/node_signatures/node.rb +9 -1
  24. data/lib/yoda/model/values/literal_value.rb +3 -0
  25. data/lib/yoda/parsing/location.rb +9 -0
  26. data/lib/yoda/server/concurrent_writer.rb +1 -1
  27. data/lib/yoda/server/lifecycle_handler.rb +101 -78
  28. data/lib/yoda/server/notifier.rb +2 -55
  29. data/lib/yoda/services/loadable_path_resolver.rb +40 -0
  30. data/lib/yoda/services.rb +1 -0
  31. data/lib/yoda/store/actions/read_project_files.rb +9 -7
  32. data/lib/yoda/store/adapters/gdbm_adapter/namespace_accessor.rb +1 -1
  33. data/lib/yoda/store/adapters/memory_adapter.rb +1 -1
  34. data/lib/yoda/store/objects/libraries_status.rb +1 -1
  35. data/lib/yoda/store/objects/library/core.rb +8 -0
  36. data/lib/yoda/store/objects/library/gem.rb +14 -3
  37. data/lib/yoda/store/objects/library/path_resolvable.rb +29 -0
  38. data/lib/yoda/store/objects/library/std.rb +9 -0
  39. data/lib/yoda/store/objects/library.rb +1 -0
  40. data/lib/yoda/store/objects/patch.rb +1 -1
  41. data/lib/yoda/store/objects/patch_set.rb +2 -2
  42. data/lib/yoda/store/project/dependency.rb +22 -4
  43. data/lib/yoda/store/project/file_finder.rb +20 -0
  44. data/lib/yoda/store/project.rb +2 -0
  45. data/lib/yoda/store/registry/cache.rb +2 -2
  46. data/lib/yoda/store/registry/composer.rb +9 -7
  47. data/lib/yoda/store/registry/index.rb +14 -10
  48. data/lib/yoda/store/registry/library_registry.rb +3 -1
  49. data/lib/yoda/store/registry.rb +1 -1
  50. data/lib/yoda/typing/constant_resolver/code_query.rb +25 -0
  51. data/lib/yoda/typing/constant_resolver/query.rb +12 -1
  52. data/lib/yoda/typing/constant_resolver.rb +13 -8
  53. data/lib/yoda/typing/inferencer/load_resolver.rb +37 -0
  54. data/lib/yoda/typing/inferencer/tracer.rb +32 -0
  55. data/lib/yoda/typing/inferencer.rb +3 -2
  56. data/lib/yoda/typing/node_info.rb +5 -0
  57. data/lib/yoda/typing/tree/{defined.rb → ask_defined.rb} +3 -2
  58. data/lib/yoda/typing/tree/base.rb +65 -20
  59. data/lib/yoda/typing/tree/begin.rb +5 -5
  60. data/lib/yoda/typing/tree/block_call.rb +26 -0
  61. data/lib/yoda/typing/tree/case.rb +8 -19
  62. data/lib/yoda/typing/tree/class_tree.rb +10 -18
  63. data/lib/yoda/typing/tree/conditional_loop.rb +15 -0
  64. data/lib/yoda/typing/tree/constant.rb +19 -0
  65. data/lib/yoda/typing/tree/constant_assignment.rb +2 -2
  66. data/lib/yoda/typing/tree/ensure.rb +17 -0
  67. data/lib/yoda/typing/tree/for.rb +7 -0
  68. data/lib/yoda/typing/tree/hash_tree.rb +32 -0
  69. data/lib/yoda/typing/tree/if.rb +10 -5
  70. data/lib/yoda/typing/tree/interpolation_text.rb +21 -0
  71. data/lib/yoda/typing/tree/literal.rb +8 -36
  72. data/lib/yoda/typing/tree/literal_inferable.rb +48 -0
  73. data/lib/yoda/typing/tree/local_exit.rb +15 -0
  74. data/lib/yoda/typing/tree/logical_assignment.rb +5 -5
  75. data/lib/yoda/typing/tree/logical_operator.rb +6 -5
  76. data/lib/yoda/typing/tree/method_def.rb +41 -0
  77. data/lib/yoda/typing/tree/method_inferable.rb +51 -0
  78. data/lib/yoda/typing/tree/module_tree.rb +7 -20
  79. data/lib/yoda/typing/tree/multiple_assignment.rb +6 -10
  80. data/lib/yoda/typing/tree/namespace_inferable.rb +20 -0
  81. data/lib/yoda/typing/tree/rescue.rb +18 -0
  82. data/lib/yoda/typing/tree/rescue_clause.rb +42 -0
  83. data/lib/yoda/typing/tree/self.rb +2 -1
  84. data/lib/yoda/typing/tree/send.rb +8 -60
  85. data/lib/yoda/typing/tree/send_inferable.rb +89 -0
  86. data/lib/yoda/typing/tree/singleton_class_tree.rb +24 -0
  87. data/lib/yoda/typing/tree/singleton_method_def.rb +41 -0
  88. data/lib/yoda/typing/tree/super.rb +9 -2
  89. data/lib/yoda/typing/tree/variable.rb +5 -10
  90. data/lib/yoda/typing/tree/variable_assignment.rb +11 -8
  91. data/lib/yoda/typing/tree/yield.rb +9 -2
  92. data/lib/yoda/typing/tree.rb +55 -22
  93. data/lib/yoda/typing.rb +1 -0
  94. data/lib/yoda/version.rb +1 -1
  95. data/lib/yoda.rb +1 -0
  96. data/yoda-language-server.gemspec +1 -1
  97. metadata +35 -18
  98. data/lib/yoda/typing/inferencer/ast_traverser.rb +0 -408
  99. data/lib/yoda/typing/tree/block.rb +0 -12
  100. data/lib/yoda/typing/tree/const.rb +0 -12
  101. data/lib/yoda/typing/tree/escape.rb +0 -12
  102. data/lib/yoda/typing/tree/hash_body.rb +0 -36
  103. data/lib/yoda/typing/tree/literal_with_interpolation.rb +0 -21
  104. data/lib/yoda/typing/tree/method.rb +0 -43
  105. data/lib/yoda/typing/tree/rescue_body.rb +0 -12
  106. data/lib/yoda/typing/tree/singleton_method.rb +0 -47
  107. data/lib/yoda/typing/tree/while.rb +0 -12
@@ -1,9 +1,12 @@
1
1
  require 'concurrent'
2
+ require 'yoda/server/providers'
2
3
 
3
4
  module Yoda
4
5
  class Server
5
6
  # Handle
6
7
  class LifecycleHandler
8
+ include Providers::ReportableProgress
9
+
7
10
  # @return [Session, nil]
8
11
  attr_reader :session
9
12
 
@@ -41,83 +44,92 @@ module Yoda
41
44
 
42
45
  # @param params [LanguageServer::Protocol::Interface::InitializeParams]
43
46
  def handle_initialize(params)
44
- Instrument.instance.hear(initialization_progress: method(:notify_initialization_progress)) do
45
- @session = begin
46
- if params[:workspace_folders]
47
- workspace_folders = params[:workspace_folders].map { |hash| LanguageServer::Protocol::Interface::WorkspaceFolder.new(name: hash[:name], uri: hash[:uri]) }
48
- Session.from_workspace_folders(workspace_folders)
49
- elsif params[:root_uri]
50
- Session.from_root_uri(params[:root_uri])
51
- else
52
- Session.new(workspaces: [])
47
+ in_progress(params, title: "Initializing Yoda") do |progress_reporter|
48
+ reporter = InitializationProgressReporter.new(progress_reporter)
49
+
50
+ subscriptions = {
51
+ initialization_progress: reporter.public_method(:notify_initialization_progress),
52
+ build_library_registry: reporter.public_method(:notify_build_library_registry),
53
+ }
54
+
55
+ Instrument.instance.hear(**subscriptions) do
56
+ @session = begin
57
+ if params[:workspace_folders]
58
+ workspace_folders = params[:workspace_folders].map { |hash| LanguageServer::Protocol::Interface::WorkspaceFolder.new(name: hash[:name], uri: hash[:uri]) }
59
+ Session.from_workspace_folders(workspace_folders)
60
+ elsif params[:root_uri]
61
+ Session.from_root_uri(params[:root_uri])
62
+ else
63
+ Session.new(workspaces: [])
64
+ end
53
65
  end
54
- end
55
66
 
56
- send_warnings(@session.setup || [])
67
+ send_warnings(@session.setup || [])
68
+ end
69
+ end
57
70
 
58
- LanguageServer::Protocol::Interface::InitializeResult.new(
59
- server_info: {
60
- name: "yoda",
61
- version: Yoda::VERSION,
62
- },
63
- capabilities: LanguageServer::Protocol::Interface::ServerCapabilities.new(
64
- text_document_sync: LanguageServer::Protocol::Interface::TextDocumentSyncOptions.new(
65
- open_close: true,
66
- change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::FULL,
67
- save: LanguageServer::Protocol::Interface::SaveOptions.new(
68
- include_text: true,
69
- ),
70
- ),
71
- completion_provider: LanguageServer::Protocol::Interface::CompletionOptions.new(
72
- resolve_provider: false,
73
- trigger_characters: ['.', '@', '[', ':', '!', '<'],
71
+ LanguageServer::Protocol::Interface::InitializeResult.new(
72
+ server_info: {
73
+ name: "yoda",
74
+ version: Yoda::VERSION,
75
+ },
76
+ capabilities: LanguageServer::Protocol::Interface::ServerCapabilities.new(
77
+ text_document_sync: LanguageServer::Protocol::Interface::TextDocumentSyncOptions.new(
78
+ open_close: true,
79
+ change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::FULL,
80
+ save: LanguageServer::Protocol::Interface::SaveOptions.new(
81
+ include_text: true,
74
82
  ),
75
- hover_provider: true,
76
- definition_provider: true,
77
- signature_help_provider: LanguageServer::Protocol::Interface::SignatureHelpOptions.new(
78
- trigger_characters: ['(', ','],
79
- ),
80
- workspace_symbol_provider: LanguageServer::Protocol::Interface::WorkspaceSymbolOptions.new(
81
- work_done_progress: true,
83
+ ),
84
+ completion_provider: LanguageServer::Protocol::Interface::CompletionOptions.new(
85
+ resolve_provider: false,
86
+ trigger_characters: ['.', '@', '[', ':', '!', '<'],
87
+ ),
88
+ hover_provider: true,
89
+ definition_provider: true,
90
+ signature_help_provider: LanguageServer::Protocol::Interface::SignatureHelpOptions.new(
91
+ trigger_characters: ['(', ','],
92
+ ),
93
+ workspace_symbol_provider: LanguageServer::Protocol::Interface::WorkspaceSymbolOptions.new(
94
+ work_done_progress: true,
95
+ ),
96
+ workspace: {
97
+ workspaceFolders: LanguageServer::Protocol::Interface::WorkspaceFoldersServerCapabilities.new(
98
+ supported: true,
99
+ change_notifications: true,
82
100
  ),
83
- workspace: {
84
- workspaceFolders: LanguageServer::Protocol::Interface::WorkspaceFoldersServerCapabilities.new(
85
- supported: true,
86
- change_notifications: true,
87
- ),
88
- fileOperations: {
89
- didCreate: LanguageServer::Protocol::Interface::FileOperationRegistrationOptions.new(
90
- filters: [
91
- LanguageServer::Protocol::Interface::FileOperationFilter.new(
92
- pattern: LanguageServer::Protocol::Interface::FileOperationPattern.new(
93
- glob: "**/*",
94
- ),
101
+ fileOperations: {
102
+ didCreate: LanguageServer::Protocol::Interface::FileOperationRegistrationOptions.new(
103
+ filters: [
104
+ LanguageServer::Protocol::Interface::FileOperationFilter.new(
105
+ pattern: LanguageServer::Protocol::Interface::FileOperationPattern.new(
106
+ glob: "**/*",
95
107
  ),
96
- ],
97
- ),
98
- didRename: LanguageServer::Protocol::Interface::FileOperationRegistrationOptions.new(
99
- filters: [
100
- LanguageServer::Protocol::Interface::FileOperationFilter.new(
101
- pattern: LanguageServer::Protocol::Interface::FileOperationPattern.new(
102
- glob: "**/*",
103
- ),
108
+ ),
109
+ ],
110
+ ),
111
+ didRename: LanguageServer::Protocol::Interface::FileOperationRegistrationOptions.new(
112
+ filters: [
113
+ LanguageServer::Protocol::Interface::FileOperationFilter.new(
114
+ pattern: LanguageServer::Protocol::Interface::FileOperationPattern.new(
115
+ glob: "**/*",
104
116
  ),
105
- ],
106
- ),
107
- didDelete: LanguageServer::Protocol::Interface::FileOperationRegistrationOptions.new(
108
- filters: [
109
- LanguageServer::Protocol::Interface::FileOperationFilter.new(
110
- pattern: LanguageServer::Protocol::Interface::FileOperationPattern.new(
111
- glob: "**/*",
112
- ),
117
+ ),
118
+ ],
119
+ ),
120
+ didDelete: LanguageServer::Protocol::Interface::FileOperationRegistrationOptions.new(
121
+ filters: [
122
+ LanguageServer::Protocol::Interface::FileOperationFilter.new(
123
+ pattern: LanguageServer::Protocol::Interface::FileOperationPattern.new(
124
+ glob: "**/*",
113
125
  ),
114
- ],
115
- ),
116
- },
126
+ ),
127
+ ],
128
+ ),
117
129
  },
118
- ),
119
- )
120
- end
130
+ },
131
+ ),
132
+ )
121
133
  rescue => e
122
134
  Logger.warn e.full_message
123
135
  LanguageServer::Protocol::Interface::ResponseError.new(
@@ -206,18 +218,29 @@ module Yoda
206
218
  EOS
207
219
  end
208
220
 
209
- def notify_initialization_progress(phase: nil, message: nil, index:, length:)
210
- if length && length > 0
211
- percentage = (index || 0) * 100 / length
212
- if index <= 0
213
- notifier.start_progress(id: phase, title: phase, message: message, percentage: percentage)
214
- elsif index >= length
215
- notifier.done_progress(id: phase)
221
+ class InitializationProgressReporter
222
+ # @return [Providers::ReportableProgress::ProgressReporter]
223
+ attr_reader :progress_reporter
224
+
225
+ # @param progress_reporter [Providers::ReportableProgress::ProgressReporter]
226
+ def initialize(progress_reporter)
227
+ @progress_reporter = progress_reporter
228
+ end
229
+
230
+ def notify_initialization_progress(phase: nil, message: nil, index:, length:)
231
+ if length && length > 0
232
+ percentage = (index || 0) * 100 / length
233
+
234
+ progress_reporter.report(message: message, percentage: percentage)
216
235
  else
217
- notifier.report_progress(id: phase, message: message, percentage: percentage)
236
+ progress_reporter.report(message: message)
218
237
  end
219
- else
220
- notifier.event(type: :initialization, phase: phase, message: message)
238
+
239
+ progress_reporter.notifier.event(type: :initialization, phase: phase, message: message)
240
+ end
241
+
242
+ def notify_build_library_registry(message: nil, name: nil, version: nil)
243
+ progress_reporter.report(message: message)
221
244
  end
222
245
  end
223
246
  end
@@ -10,19 +10,18 @@ module Yoda
10
10
  def busy(type:, id: nil)
11
11
  failed = false
12
12
  event(type: type, phase: :begin, id: id)
13
- start_progress(id: id, title: type)
14
13
  yield
15
14
  rescue => e
16
15
  Logger.warn(e.full_message)
17
16
  failed = true
17
+ event(type: type, phage: :failed, id: id)
18
18
  raise e
19
19
  ensure
20
20
  event(type: type, phase: :end, id: id)
21
- failed ? cancel_progress(id: id) : done_progress(id: id)
22
21
  end
23
22
 
24
23
  # @param params [Hash]
25
- def event(params)
24
+ def event(**params)
26
25
  write(method: 'telemetry/event', params: params)
27
26
  end
28
27
 
@@ -121,58 +120,6 @@ module Yoda
121
120
  )
122
121
  end
123
122
 
124
- # @param id [String, Symbol]
125
- # @param title [String]
126
- # @param calcellable [Boolean]
127
- # @param message [String, nil]
128
- # @param percentage [Integer]
129
- def start_progress(id:, title:, cancellable: false, message: nil, percentage: 0)
130
- write(
131
- method: 'window/progress/start',
132
- params: {
133
- id: id,
134
- title: title,
135
- calcellable: cancellable,
136
- message: message,
137
- percentage: percentage,
138
- }
139
- )
140
- end
141
-
142
- # @param id [String, Symbol]
143
- # @param message [String, nil]
144
- # @param percentage [Integer, nil]
145
- def report_progress(id:, message: nil, percentage: nil)
146
- write(
147
- method: 'window/progress/report',
148
- params: {
149
- id: id,
150
- message: message,
151
- percentage: percentage,
152
- }
153
- )
154
- end
155
-
156
- # @param id [String, Symbol]
157
- def done_progress(id:)
158
- write(
159
- method: 'window/progress/done',
160
- params: {
161
- id: id,
162
- }
163
- )
164
- end
165
-
166
- # @param id [String, Symbol]
167
- def cancel_progress(id:)
168
- write(
169
- method: 'window/progress/cancel',
170
- params: {
171
- id: id,
172
- }
173
- )
174
- end
175
-
176
123
  private
177
124
 
178
125
  def write(params)
@@ -0,0 +1,40 @@
1
+ module Yoda
2
+ module Services
3
+ class LoadablePathResolver
4
+ def initialize
5
+ end
6
+
7
+ # @param base_paths [Array<String>]
8
+ # @param pattern [String]
9
+ # @return [String, nil]
10
+ def find_loadable_path(base_paths, pattern)
11
+ # TODO: Support absolute path
12
+ return nil if absolute_path?(pattern)
13
+ return nil if pattern.start_with?("~/")
14
+ return nil if pattern.start_with?("./")
15
+ return nil if pattern.start_with?("../")
16
+
17
+ base_paths.each do |base_path|
18
+ path = File.join(base_path, pattern)
19
+
20
+ if File.extname(path).empty?
21
+ paths_with_suffix = ::Gem.suffixes.map { |suffix| path + suffix }
22
+ matched_path = paths_with_suffix.find { |path| File.file?(path) }
23
+ return matched_path if matched_path
24
+ else
25
+ return path if File.file?(path)
26
+ end
27
+ end
28
+
29
+ return nil
30
+ end
31
+
32
+ # This is a workaround for Ruby 2.6 (This version does not have `File.absolute_path?`)
33
+ # @param path [String]
34
+ # @return [Boolean]
35
+ def absolute_path?(path)
36
+ path[0] == "/"
37
+ end
38
+ end
39
+ end
40
+ end
data/lib/yoda/services.rb CHANGED
@@ -4,6 +4,7 @@ module Yoda
4
4
  require 'yoda/services/current_node_explain'
5
5
  require 'yoda/services/comment_completion'
6
6
  require 'yoda/services/code_completion'
7
+ require 'yoda/services/loadable_path_resolver'
7
8
  require 'yoda/services/signature_discovery'
8
9
  end
9
10
  end
@@ -2,21 +2,23 @@ module Yoda
2
2
  module Store
3
3
  module Actions
4
4
  class ReadProjectFiles
5
+ # @return [Project]
6
+ attr_reader :project
7
+
5
8
  # @return [Registry]
6
9
  attr_reader :registry
7
10
 
8
- # @return [String]
9
- attr_reader :root_path
10
-
11
11
  # @param project [Project]
12
12
  # @return [ReadProjectFiles]
13
13
  def self.for_project(project)
14
- new(project.registry, project.root_path)
14
+ new(project, project.registry)
15
15
  end
16
16
 
17
- def initialize(registry, root_path)
17
+ # @param project [Project]
18
+ # @param registry [Project]
19
+ def initialize(project, registry)
20
+ @project = project
18
21
  @registry = registry
19
- @root_path = root_path
20
22
  end
21
23
 
22
24
  def run
@@ -35,7 +37,7 @@ module Yoda
35
37
 
36
38
  # @return [Array<String>]
37
39
  def project_files
38
- Dir.chdir(root_path) { Dir.glob(["{lib,app}/**/*.rb", "ext/**/*.c", ".yoda/*.rb"]).map { |name| File.expand_path(name, root_path) } }
40
+ project.project_source_paths
39
41
  end
40
42
  end
41
43
  end
@@ -20,7 +20,7 @@ module Yoda
20
20
 
21
21
  # @param address [String, Symbol]
22
22
  # @return [Object, nil]
23
- def get(address)
23
+ def get(address, **)
24
24
  JSON.load(database[build_key(address)], symbolize_names: true)
25
25
  end
26
26
 
@@ -28,7 +28,7 @@ module Yoda
28
28
 
29
29
  # @param address [String]
30
30
  # @return [any]
31
- def get(address)
31
+ def get(address, **)
32
32
  JSON.load(db[address.to_s], symbolize_names: true)
33
33
  end
34
34
 
@@ -10,7 +10,7 @@ module Yoda
10
10
  # @param dependency [Project::Dependency]
11
11
  # @return [Array<Object::Library::Core, Object::Library::Std, Object::Library::Gem>]
12
12
  def self.libraies_from_dependency(dependency)
13
- [dependency.core, dependency.std, *dependency.gems.select(&:installed?)]
13
+ [dependency.core, dependency.std, *dependency.autoload_gems.select(&:installed?)]
14
14
  end
15
15
 
16
16
  # @param libraries [Array<Library::Core, Library::Std, Library::Gem>]
@@ -1,3 +1,5 @@
1
+ require 'yoda/store/objects/library/path_resolvable'
2
+
1
3
  module Yoda
2
4
  module Store
3
5
  module Objects
@@ -42,6 +44,7 @@ module Yoda
42
44
  class Connected
43
45
  extend ConnectedDelegation
44
46
  include WithRegistry
47
+ include PathResolvable
45
48
 
46
49
  delegate_to_object :version
47
50
  delegate_to_object :id, :name, :doc_path, :to_h, :with_project_connection
@@ -64,6 +67,11 @@ module Yoda
64
67
  def registry_path
65
68
  VersionStore.for_current_version.registry_path_for_core
66
69
  end
70
+
71
+ # @return [Array<String>]
72
+ def require_paths
73
+ []
74
+ end
67
75
  end
68
76
  end
69
77
  end
@@ -1,18 +1,24 @@
1
+ require 'yoda/store/objects/library/path_resolvable'
2
+
1
3
  module Yoda
2
4
  module Store
3
5
  module Objects
4
6
  module Library
5
7
  class Gem
6
8
  include Serializable
9
+ include PathResolvable
7
10
 
8
11
  # @return [String]
9
12
  attr_reader :name, :version, :source_path, :full_gem_path, :doc_dir
10
13
 
14
+ # @return [Array<String>]
15
+ attr_reader :require_paths
16
+
11
17
  # @return [Symbol, nil]
12
18
  attr_reader :source_type
13
19
 
14
20
  class << self
15
- # @param spec [Bundler::LazySpecification]
21
+ # @param spec [Bundler::LazySpecification, Gem::Specification]
16
22
  def from_gem_spec(spec)
17
23
  if spec.respond_to?(:full_gem_path)
18
24
  # Installed
@@ -20,6 +26,7 @@ module Yoda
20
26
  name: spec.name,
21
27
  version: spec.version.version,
22
28
  source_path: spec.source.respond_to?(:path) ? spec.source.path : nil,
29
+ require_paths: spec.full_require_paths,
23
30
  full_gem_path: spec.full_gem_path,
24
31
  doc_dir: spec.doc_dir,
25
32
  source_type: source_type_of(spec.source),
@@ -30,6 +37,7 @@ module Yoda
30
37
  name: spec.name,
31
38
  version: spec.version.version,
32
39
  source_path: nil,
40
+ require_paths: [],
33
41
  full_gem_path: nil,
34
42
  doc_dir: nil,
35
43
  source_type: nil,
@@ -57,11 +65,12 @@ module Yoda
57
65
  end
58
66
  end
59
67
 
60
- def initialize(name:, version:, source_path:, full_gem_path:, doc_dir:, source_type:)
68
+ def initialize(name:, version:, source_path:, full_gem_path:, require_paths:, doc_dir:, source_type:)
61
69
  @name = name
62
70
  @version = version
63
71
  @source_path = source_path
64
72
  @full_gem_path = full_gem_path
73
+ @require_paths = require_paths
65
74
  @doc_dir = doc_dir
66
75
  @source_type = source_type&.to_sym
67
76
  end
@@ -80,6 +89,7 @@ module Yoda
80
89
  version: version,
81
90
  source_path: source_path,
82
91
  full_gem_path: full_gem_path,
92
+ require_paths: require_paths,
83
93
  doc_dir: doc_dir,
84
94
  source_type: source_type,
85
95
  }
@@ -103,9 +113,10 @@ module Yoda
103
113
  extend ConnectedDelegation
104
114
  include WithRegistry
105
115
 
106
- delegate_to_object :name, :version, :source_path, :full_gem_path, :doc_dir, :source_type
116
+ delegate_to_object :name, :version, :source_path, :full_gem_path, :doc_dir, :source_type, :require_paths
107
117
  delegate_to_object :id, :local?, :to_h, :installed?, :managed_by_rubygems?, :with_project_connection
108
118
  delegate_to_object :hash, :eql?, :==, :to_json, :derive
119
+ delegate_to_object :contain_requirable_file?, :find_requirable_file
109
120
 
110
121
  # @return [Gem]
111
122
  attr_reader :object
@@ -0,0 +1,29 @@
1
+ require 'yoda/store/objects/library/path_resolvable'
2
+
3
+ module Yoda
4
+ module Store
5
+ module Objects
6
+ module Library
7
+ module PathResolvable
8
+ # @abstract
9
+ # @return [Array<String>]
10
+ def require_paths
11
+ fail NotImplementedError
12
+ end
13
+
14
+ # @param relative_path [String]
15
+ # @return [Boolean]
16
+ def contain_requirable_file?(relative_path)
17
+ !!find_requirable_file(relative_path)
18
+ end
19
+
20
+ # @param relative_path [String]
21
+ # @return [String, nil]
22
+ def find_requirable_file(relative_path)
23
+ Services::LoadablePathResolver.new.find_loadable_path(require_paths, relative_path)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,3 +1,5 @@
1
+ require 'yoda/store/objects/library/path_resolvable'
2
+
1
3
  module Yoda
2
4
  module Store
3
5
  module Objects
@@ -42,6 +44,7 @@ module Yoda
42
44
  class Connected
43
45
  extend ConnectedDelegation
44
46
  include WithRegistry
47
+ include PathResolvable
45
48
 
46
49
  delegate_to_object :version
47
50
  delegate_to_object :id, :name, :doc_path, :to_h, :with_project_connection
@@ -64,6 +67,12 @@ module Yoda
64
67
  def registry_path
65
68
  VersionStore.for_current_version.registry_path_for_stdlib
66
69
  end
70
+
71
+ # @return [Array<String>]
72
+ def require_paths
73
+ # TODO: Calculate from registry
74
+ [File.join(VersionStore.for_current_version.ruby_source_path, "lib/")]
75
+ end
67
76
  end
68
77
  end
69
78
  end
@@ -6,6 +6,7 @@ module Yoda
6
6
  require 'yoda/store/objects/library/core'
7
7
  require 'yoda/store/objects/library/std'
8
8
  require 'yoda/store/objects/library/gem'
9
+ require 'yoda/store/objects/library/path_resolvable'
9
10
 
10
11
  class << self
11
12
  # @return [Core]
@@ -17,7 +17,7 @@ module Yoda
17
17
 
18
18
  # @param address [String, Symbol]
19
19
  # @return [Addressable, nil]
20
- def find(address)
20
+ def find(address, **)
21
21
  @registry[address.to_sym]
22
22
  end
23
23
  alias get find
@@ -13,7 +13,7 @@ module Yoda
13
13
 
14
14
  # @param address [Symbol]
15
15
  # @return [Set<Symbol>]
16
- def get(address)
16
+ def get(address, **)
17
17
  index[address] ||= Set.new
18
18
  end
19
19
 
@@ -73,7 +73,7 @@ module Yoda
73
73
 
74
74
  # @param address [String, Symbol]
75
75
  # @return [Addressable, nil]
76
- def find(address)
76
+ def find(address, **)
77
77
  if (patches = get_patches(address)).empty?
78
78
  nil
79
79
  else
@@ -12,8 +12,13 @@ module Yoda
12
12
  end
13
13
 
14
14
  # @return [Array<Objects::Library::Gem>]
15
- def gems
16
- builder.gems
15
+ def loadable_gems
16
+ builder.loadable_gems
17
+ end
18
+
19
+ # @return [Array<Objects::Library::Gem>]
20
+ def autoload_gems
21
+ builder.autoload_gems
17
22
  end
18
23
 
19
24
  # @param name [String]
@@ -33,6 +38,7 @@ module Yoda
33
38
  @std ||= Objects::Library.std
34
39
  end
35
40
 
41
+ # @return [Builder]
36
42
  def builder
37
43
  @builder ||= Builder.new(project)
38
44
  end
@@ -47,14 +53,21 @@ module Yoda
47
53
  end
48
54
 
49
55
  # @return [Array<Objects::Library::Gem>]
50
- def gems
51
- @gems ||= begin
56
+ def loadable_gems
57
+ @loadable_gems ||= begin
52
58
  dependencies
53
59
  .map { |attrs| Objects::Library::Gem.new(**attrs) }
54
60
  .reject { |spec| project.config.ignored_gems.include?(spec.name) }
55
61
  end
56
62
  end
57
63
 
64
+ # @return [Array<Objects::Library::Gem>]
65
+ def autoload_gems
66
+ @autoload_gems ||= begin
67
+ loadable_gems.select { |gem| autoload_dependency_ids.include?(gem.id) }
68
+ end
69
+ end
70
+
58
71
  private
59
72
 
60
73
  # @return [Array<Hash>]
@@ -62,6 +75,11 @@ module Yoda
62
75
  analyzed_deps[:dependencies] || []
63
76
  end
64
77
 
78
+ # @return [Array<String>]
79
+ def autoload_dependency_ids
80
+ analyzed_deps[:autoload_dependency_ids] || []
81
+ end
82
+
65
83
  # @return [Hash]
66
84
  def analyzed_deps
67
85
  @analyzed_deps ||= begin