yoda-language-server 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +5 -3
  3. data/README.md +50 -48
  4. data/client/atom/main.js +13 -3
  5. data/client/vscode/.vscode/launch.json +7 -4
  6. data/client/vscode/package-lock.json +585 -1454
  7. data/client/vscode/package.json +10 -7
  8. data/client/vscode/src/extension.ts +3 -3
  9. data/client/vscode/src/test/completion.test.ts +39 -0
  10. data/client/vscode/src/test/helper.ts +38 -0
  11. data/client/vscode/src/test/index.ts +5 -3
  12. data/client/vscode/testFixture/completion.rb +1 -0
  13. data/exe/yoda +1 -20
  14. data/lib/yoda.rb +2 -1
  15. data/lib/yoda/commands.rb +34 -0
  16. data/lib/yoda/commands/base.rb +10 -0
  17. data/lib/yoda/commands/complete.rb +36 -0
  18. data/lib/yoda/commands/file_cursor_parsable.rb +29 -0
  19. data/lib/yoda/{runner → commands}/infer.rb +4 -9
  20. data/lib/yoda/commands/setup.rb +37 -0
  21. data/lib/yoda/errors.rb +34 -0
  22. data/lib/yoda/evaluation/evaluator.rb +2 -0
  23. data/lib/yoda/server.rb +60 -15
  24. data/lib/yoda/server/completion_provider.rb +8 -8
  25. data/lib/yoda/server/definition_provider.rb +8 -8
  26. data/lib/yoda/server/hover_provider.rb +6 -6
  27. data/lib/yoda/server/initialization_provider.rb +85 -0
  28. data/lib/yoda/server/{client_info.rb → session.rb} +6 -2
  29. data/lib/yoda/server/signature_provider.rb +6 -6
  30. data/lib/yoda/store/actions.rb +3 -1
  31. data/lib/yoda/store/actions/build_core_index.rb +44 -0
  32. data/lib/yoda/store/actions/import_core_library.rb +14 -9
  33. data/lib/yoda/store/actions/import_gem.rb +98 -0
  34. data/lib/yoda/store/actions/import_std_library.rb +35 -0
  35. data/lib/yoda/store/adapters.rb +1 -0
  36. data/lib/yoda/store/adapters/memory_adapter.rb +81 -0
  37. data/lib/yoda/store/objects.rb +4 -0
  38. data/lib/yoda/store/objects/addressable.rb +0 -12
  39. data/lib/yoda/store/objects/base.rb +4 -14
  40. data/lib/yoda/store/objects/merger.rb +4 -4
  41. data/lib/yoda/store/objects/patchable.rb +19 -0
  42. data/lib/yoda/store/objects/project_status.rb +169 -0
  43. data/lib/yoda/store/objects/serializable.rb +39 -0
  44. data/lib/yoda/store/project.rb +28 -114
  45. data/lib/yoda/store/project/cache.rb +79 -0
  46. data/lib/yoda/store/project/library_doc_loader.rb +102 -0
  47. data/lib/yoda/store/query/find_constant.rb +2 -1
  48. data/lib/yoda/store/registry.rb +15 -0
  49. data/lib/yoda/store/yard_importer.rb +58 -28
  50. data/lib/yoda/typing/evaluator.rb +8 -5
  51. data/lib/yoda/version.rb +1 -1
  52. data/package.json +32 -11
  53. data/scripts/benchmark.rb +1 -1
  54. data/yoda-language-server.gemspec +1 -0
  55. metadata +37 -7
  56. data/client/vscode/src/test/extension.test.ts +0 -22
  57. data/lib/yoda/runner/setup.rb +0 -26
  58. data/lib/yoda/store/actions/import_gems.rb +0 -91
@@ -1,11 +1,11 @@
1
- require 'bundler'
2
- require 'tmpdir'
3
1
  require 'fileutils'
4
- require 'digest'
5
2
 
6
3
  module Yoda
7
4
  module Store
8
5
  class Project
6
+ require 'yoda/store/project/cache'
7
+ require 'yoda/store/project/library_doc_loader'
8
+
9
9
  # @type String
10
10
  attr_reader :root_path
11
11
 
@@ -20,20 +20,33 @@ module Yoda
20
20
  @registry = Registry.new
21
21
  end
22
22
 
23
- def clean
24
- end
25
-
26
23
  def setup
27
24
  YARD::Logger.instance(STDERR)
28
25
  make_dir
29
- cache.setup
26
+ cache.register_adapter(registry)
27
+ end
28
+
29
+ def clear
30
+ setup
31
+ registry.adapter.clear
32
+ end
33
+
34
+ # @return [Array<BaseError>]
35
+ def build_cache(progress: false)
36
+ setup
37
+ loader = LibraryDocLoader.build_for(self)
38
+ loader.run(progress: progress)
30
39
  load_project_files
31
- self
40
+ loader.errors
32
41
  end
33
42
 
34
43
  def rebuild_cache(progress: false)
35
- make_dir
36
- cache.build(progress: progress)
44
+ clear
45
+ build_cache(progress: progress)
46
+ end
47
+
48
+ def yoda_dir
49
+ File.expand_path('.yoda', root_path)
37
50
  end
38
51
 
39
52
  # @param source_path [String]
@@ -41,118 +54,19 @@ module Yoda
41
54
  Actions::ReadFile.run(registry, source_path)
42
55
  end
43
56
 
44
- def yoda_dir
45
- File.expand_path('.yoda', root_path)
46
- end
47
-
48
57
  private
49
58
 
50
- def make_dir
51
- File.exist?(yoda_dir) || FileUtils.mkdir(yoda_dir)
52
- end
53
-
54
59
  def load_project_files
55
60
  Actions::ReadProjectFiles.new(registry, root_path).run
56
61
  end
57
62
 
58
- def cache
59
- @cache ||= Cache.new(self)
63
+ def make_dir
64
+ File.exist?(yoda_dir) || FileUtils.mkdir(yoda_dir)
60
65
  end
61
66
 
62
- class Cache
63
- class Builder
64
- # @return [Registry]
65
- attr_reader :registry
66
-
67
- # @return [String]
68
- attr_reader :root_path
69
-
70
- # @return [String]
71
- attr_reader :gemfile_lock_path
72
-
73
- def initialize(registry, root_path, gemfile_lock_path)
74
- @registry = registry
75
- @root_path = root_path
76
- @gemfile_lock_path = gemfile_lock_path
77
- end
78
-
79
- def run(progress: false)
80
- Actions::ImportCoreLibrary.new(registry).run
81
- if File.exist?(gemfile_lock_path)
82
- Actions::ImportGems.new(registry, gemfile_lock_parser.specs).run
83
- end
84
- registry.compress_and_save(progress: progress)
85
- end
86
-
87
- def gemfile_lock_parser
88
- Dir.chdir(root_path) do
89
- Bundler::LockfileParser.new(File.read(gemfile_lock_path))
90
- end
91
- end
92
- end
93
-
94
- # @return [Project]
95
- attr_reader :project
96
-
97
- def initialize(project)
98
- @project = project
99
- end
100
-
101
- def build(progress: false)
102
- STDERR.puts 'Constructing database for the current project.'
103
- YARD::Logger.instance(STDERR)
104
- make_cache_dir
105
- register_adapter
106
- project.registry.adapter.clear
107
- Builder.new(project.registry, project.root_path, gemfile_lock_path).run(progress: progress)
108
- STDERR.puts 'Finished to construct database for the current project.'
109
- end
110
-
111
- def setup
112
- if present?
113
- register_adapter
114
- else
115
- build
116
- end
117
- end
118
-
119
- private
120
-
121
- # @return [true, false]
122
- def present?
123
- File.exist?(cache_path)
124
- end
125
-
126
- def register_adapter
127
- return if project.registry.adapter
128
- project.registry.adapter = Adapters.default_adapter_class.for(cache_path)
129
- end
130
-
131
- def cache_dir
132
- File.expand_path('cache', project.yoda_dir)
133
- end
134
-
135
- def cache_name
136
- @cache_path ||= begin
137
- digest = Digest::SHA256.new
138
- digest.file(gemfile_lock_path) if File.exist?(gemfile_lock_path)
139
- digest.update(Yoda::VERSION)
140
- digest.update(Adapters.default_adapter_class.type.to_s)
141
- digest.hexdigest
142
- end
143
- end
144
-
145
- def cache_path
146
- File.expand_path(cache_name, cache_dir)
147
- end
148
-
149
- def make_cache_dir
150
- File.exist?(cache_dir) || FileUtils.mkdir_p(cache_dir)
151
- end
152
-
153
- def gemfile_lock_path
154
- File.absolute_path('Gemfile.lock', project.root_path)
155
- end
67
+ # @return [Cache]
68
+ def cache
69
+ @cache ||= Cache.build_for(self)
156
70
  end
157
71
  end
158
72
  end
@@ -0,0 +1,79 @@
1
+ require 'fileutils'
2
+ require 'bundler'
3
+ require 'tmpdir'
4
+ require 'digest'
5
+
6
+ module Yoda
7
+ module Store
8
+ class Project
9
+ # Find registry file for the current project settings.
10
+ class Cache
11
+ class << self
12
+ # @param project_dir [String]
13
+ # @return [String]
14
+ def cache_dir(project_dir)
15
+ File.expand_path('.yoda/cache', project_dir)
16
+ end
17
+
18
+ # @param project_dir [String]
19
+ # @return [String]
20
+ def gemfile_lock_path(project_dir)
21
+ File.absolute_path('Gemfile.lock', project_dir)
22
+ end
23
+
24
+ # @param project [Project]
25
+ def build_for(project)
26
+ new(cache_dir_path: cache_dir(project.root_path), gemfile_lock_path: gemfile_lock_path(project.root_path))
27
+ end
28
+ end
29
+
30
+ # @return [String]
31
+ attr_reader :cache_dir_path
32
+
33
+ # @return [String, nil]
34
+ attr_reader :gemfile_lock_path
35
+
36
+ # @param cache_dir_path [String]
37
+ # @param gemfile_lock_path [String, nil]
38
+ def initialize(cache_dir_path:, gemfile_lock_path: nil)
39
+ @cache_dir_path = cache_dir_path
40
+ @gemfile_lock_path = gemfile_lock_path
41
+ end
42
+
43
+ # @return [true, false]
44
+ def present?
45
+ File.exist?(cache_path)
46
+ end
47
+
48
+ # @param registry [Registry]
49
+ def register_adapter(registry)
50
+ return if registry.adapter
51
+ make_cache_dir
52
+ registry.adapter = Adapters.default_adapter_class.for(cache_path)
53
+ end
54
+
55
+ # @return [String]
56
+ def cache_path
57
+ File.expand_path(cache_name, cache_dir_path)
58
+ end
59
+
60
+ private
61
+
62
+ # @return [String]
63
+ def cache_name
64
+ @cache_path ||= begin
65
+ digest = Digest::SHA256.new
66
+ digest.file(gemfile_lock_path) if gemfile_lock_path && File.exist?(gemfile_lock_path)
67
+ digest.update(Registry::REGISTRY_VERSION.to_s)
68
+ digest.update(Adapters.default_adapter_class.type.to_s)
69
+ digest.hexdigest
70
+ end
71
+ end
72
+
73
+ def make_cache_dir
74
+ File.exist?(cache_dir_path) || FileUtils.mkdir_p(cache_dir_path)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,102 @@
1
+ module Yoda
2
+ module Store
3
+ class Project
4
+ class LibraryDocLoader
5
+ # @return [Registry]
6
+ attr_reader :registry
7
+
8
+ # @param gem_specs [Array<Objects::ProjectStatus::GemStatus, Bundler::LazySpecification>]
9
+ attr_reader :gem_specs
10
+
11
+ # @param errors [Array<BaseError>]
12
+ attr_reader :errors
13
+
14
+ class << self
15
+ # @param project [Project]
16
+ # @return [LibraryDocLoader]
17
+ def build_for(project)
18
+ lockfile_parser = parse_gemfile_lock(project.root_path, Cache.gemfile_lock_path(project.root_path))
19
+ new(registry: project.registry, gem_specs: lockfile_parser&.specs || [])
20
+ end
21
+
22
+ private
23
+
24
+ # @return [Bundler::LockfileParser, nil]
25
+ def parse_gemfile_lock(root_path, gemfile_lock_path)
26
+ return if !gemfile_lock_path || !File.exists?(gemfile_lock_path)
27
+ Dir.chdir(root_path) do
28
+ Bundler::LockfileParser.new(File.read(gemfile_lock_path))
29
+ end
30
+ end
31
+ end
32
+
33
+ # @param registry [Registry]
34
+ # @param gem_specs [Array<Objects::ProjectStatus::GemStatus, Bundler::LazySpecification>]
35
+ def initialize(registry:, gem_specs:)
36
+ @registry = registry
37
+ @gem_specs = gem_specs
38
+ @errors = []
39
+ end
40
+
41
+ def run(progress: false)
42
+ project_status = registry.project_status || Objects::ProjectStatus.initial_build(specs: gem_specs)
43
+ new_bundle_status = update_bundle(project_status.bundle, progress: progress)
44
+ registry.save_project_status(project_status.derive(bundle: new_bundle_status))
45
+ end
46
+
47
+ private
48
+
49
+ # @param bundle_status [Objects::ProjectStatus::BundleStatus]
50
+ # @return [Objects::ProjectStatus::BundleStatus]
51
+ def update_bundle(bundle_status, progress: false)
52
+ unless bundle_status.all_present?
53
+ STDERR.puts 'Constructing database for the current project.'
54
+ bundle_status = import_deps(bundle_status)
55
+ registry.compress_and_save(progress: progress)
56
+ end
57
+ bundle_status
58
+ end
59
+
60
+ # Try to import missing gems and core libraries.
61
+ # @param bundle_status [Objects::ProjectStatus::BundleStatus]
62
+ # @return [Objects::ProjectStatus::BundleStatus]
63
+ def import_deps(bundle_status)
64
+ bundle_status = import_core(bundle_status) unless bundle_status.std_status.core_present?
65
+ bundle_status = import_std(bundle_status) unless bundle_status.std_status.std_present?
66
+ import_gems(bundle_status)
67
+ end
68
+
69
+ # @param bundle_status [Objects::ProjectStatus::BundleStatus]
70
+ # @return [Objects::ProjectStatus::BundleStatus]
71
+ def import_core(bundle_status)
72
+ result = Actions::ImportCoreLibrary.run(registry)
73
+ errors.push(CoreImportError.new('core')) unless result
74
+ bundle_status.derive(std_status: bundle_status.std_status.derive(core_present: !!result))
75
+ end
76
+
77
+ # @param bundle_status [Objects::ProjectStatus::BundleStatus]
78
+ # @return [Objects::ProjectStatus::BundleStatus]
79
+ def import_std(bundle_status)
80
+ result = Actions::ImportStdLibrary.run(registry)
81
+ errors.push(CoreImportError.new('std')) unless result
82
+ bundle_status.derive(std_status: bundle_status.std_status.derive(std_present: !!result))
83
+ end
84
+
85
+ # @param bundle_status [Objects::ProjectStatus::BundleStatus]
86
+ # @return [Objects::ProjectStatus::BundleStatus]
87
+ def import_gems(bundle_status)
88
+ gem_statuses = bundle_status.gem_statuses.map do |gem_status|
89
+ if gem_status.present?
90
+ gem_status
91
+ else
92
+ result = Actions::ImportGem.run(registry: registry, gem_name: gem_status.name, gem_version: gem_status.version)
93
+ errors.push(GemImportError.new(name: gem_status.name, version: gem_status.version)) unless result
94
+ gem_status.derive(present: result)
95
+ end
96
+ end
97
+ bundle_status.derive(gem_statuses: gem_statuses)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -62,7 +62,8 @@ module Yoda
62
62
  constant_names = path_of(name).split
63
63
  namespace = registry.find('Object') unless namespace
64
64
  constant_names.reduce(namespace) do |namespace, name|
65
- if namespace
65
+ # @todo resolve its value if namespace is ValueObject
66
+ if namespace && namespace.is_a?(Objects::NamespaceObject)
66
67
  select_constants_from_ancestors(namespace, name).first
67
68
  else
68
69
  return nil
@@ -4,6 +4,9 @@ require 'ruby-progressbar'
4
4
  module Yoda
5
5
  module Store
6
6
  class Registry
7
+ # @note This number must be updated when breaking change is added.
8
+ REGISTRY_VERSION = 1
9
+
7
10
  # @return [Adapters::LmdbAdapter, nil]
8
11
  attr_reader :adapter
9
12
 
@@ -13,11 +16,23 @@ module Yoda
13
16
  # @return [Objects::PatchSet]
14
17
  attr_reader :patch_set
15
18
 
19
+ PROJECT_STATUS_KEY = '%project_status'
20
+
16
21
  def initialize(adapter = nil)
17
22
  @patch_set = Objects::PatchSet.new
18
23
  @adapter = adapter
19
24
  end
20
25
 
26
+ # @return [Objects::ProjectStatus, nil]
27
+ def project_status
28
+ adapter&.exist?(PROJECT_STATUS_KEY) && adapter.get(PROJECT_STATUS_KEY)
29
+ end
30
+
31
+ # @param new_project_status [Objects::ProjectStatus]
32
+ def save_project_status(new_project_status)
33
+ adapter.put(PROJECT_STATUS_KEY, new_project_status)
34
+ end
35
+
21
36
  # @param path [String]
22
37
  # @return [Objects::Base, nil]
23
38
  def find(path)
@@ -73,26 +73,33 @@ module Yoda
73
73
  # @param code_object [::YARD::CodeObjects::NamespaceObject]
74
74
  # @return [Objects::ClassObject]
75
75
  def convert_root_object(code_object)
76
- # @todo Add meta class for main object.
77
- Objects::ClassObject.new(
78
- path: 'Object',
76
+ object_class = Objects::ClassObject.new(
77
+ path: path_to_store(code_object),
79
78
  document: code_object.docstring.to_s,
80
79
  tag_list: code_object.tags.map { |tag| convert_tag(tag, '') },
81
80
  sources: code_object.files.map(&method(:convert_source)),
82
81
  primary_source: code_object[:current_file_has_comments] ? convert_source(code_object.files.first) : nil,
83
82
  instance_method_addresses: code_object.meths(included: false, scope: :instance).map(&:path),
84
- mixin_addresses: code_object.instance_mixins.map { |mixin| mixin.path },
83
+ mixin_addresses: code_object.instance_mixins.map { |mixin| path_to_store(mixin) },
85
84
  constant_addresses: (code_object.children.select{ |child| %i(constant module class).include?(child.type) }.map { |constant| constant.path } + ['Object']).uniq,
86
85
  )
86
+ object_meta_class = Objects::MetaClassObject.new(
87
+ path: path_to_store(code_object),
88
+ sources: code_object.files.map(&method(:convert_source)),
89
+ primary_source: code_object[:current_file_has_comments] ? convert_source(code_object.files.first) : nil,
90
+ instance_method_addresses: code_object.meths(included: false, scope: :class).map(&:path),
91
+ mixin_addresses: code_object.instance_mixins.map { |mixin| path_to_store(mixin) },
92
+ )
93
+ [object_class, object_meta_class]
87
94
  end
88
95
 
89
96
  # @param code_object [::YARD::CodeObjects::ConstantObject]
90
97
  # @return [Objects::ValueObject]
91
98
  def convert_constant_object(code_object)
92
99
  Objects::ValueObject.new(
93
- path: code_object.path,
100
+ path: path_to_store(code_object),
94
101
  document: code_object.docstring.to_s,
95
- tag_list: code_object.tags.map { |tag| convert_tag(tag, code_object.namespace.path) },
102
+ tag_list: code_object.tags.map { |tag| convert_tag(tag, path_to_store(code_object.namespace)) },
96
103
  sources: code_object.files.map(&method(:convert_source)),
97
104
  primary_source: code_object[:current_file_has_comments] ? convert_source(code_object.files.first) : nil,
98
105
  value: code_object.value,
@@ -107,8 +114,8 @@ module Yoda
107
114
  method_object = Objects::MethodObject.new(
108
115
  path: "Object#{code_object.sep}#{code_object.name}",
109
116
  document: code_object.docstring.to_s,
110
- tag_list: code_object.tags.map { |tag| convert_tag(tag, code_object.namespace.path) },
111
- overloads: code_object.tags(:overload).map { |tag| convert_overload_tag(tag, code_object.namespace.path) },
117
+ tag_list: code_object.tags.map { |tag| convert_tag(tag, path_to_store(code_object.namespace)) },
118
+ overloads: code_object.tags(:overload).map { |tag| convert_overload_tag(tag, path_to_store(code_object.namespace)) },
112
119
  sources: code_object.files.map(&method(:convert_source)),
113
120
  primary_source: code_object[:current_file_has_comments] ? convert_source(code_object.files.first) : nil,
114
121
  parameters: code_object.parameters,
@@ -121,10 +128,10 @@ module Yoda
121
128
  [method_object, object_object]
122
129
  else
123
130
  Objects::MethodObject.new(
124
- path: code_object.path,
131
+ path: path_to_store(code_object),
125
132
  document: code_object.docstring.to_s,
126
- tag_list: code_object.tags.map { |tag| convert_tag(tag, code_object.namespace.path) },
127
- overloads: code_object.tags(:overload).map { |tag| convert_overload_tag(tag, code_object.namespace.path) },
133
+ tag_list: code_object.tags.map { |tag| convert_tag(tag, path_to_store(code_object.namespace)) },
134
+ overloads: code_object.tags(:overload).map { |tag| convert_overload_tag(tag, path_to_store(code_object.namespace)) },
128
135
  sources: code_object.files.map(&method(:convert_source)),
129
136
  primary_source: code_object[:current_file_has_comments] ? convert_source(code_object.files.first) : nil,
130
137
  parameters: code_object.parameters,
@@ -137,22 +144,22 @@ module Yoda
137
144
  # @return [Array<Objects::ModuleObject, Objects::MetaClassObject>]
138
145
  def convert_module_object(code_object)
139
146
  module_object = Objects::ModuleObject.new(
140
- path: code_object.path,
147
+ path: path_to_store(code_object),
141
148
  document: code_object.docstring.to_s,
142
- tag_list: code_object.tags.map { |tag| convert_tag(tag, code_object.path) },
149
+ tag_list: code_object.tags.map { |tag| convert_tag(tag, path_to_store(code_object)) },
143
150
  sources: code_object.files.map(&method(:convert_source)),
144
151
  primary_source: code_object[:current_file_has_comments] ? convert_source(code_object.files.first) : nil,
145
152
  instance_method_addresses: code_object.meths(included: false, scope: :instance).map(&:path),
146
- mixin_addresses: code_object.instance_mixins.map { |mixin| mixin.path },
153
+ mixin_addresses: code_object.instance_mixins.map { |mixin| path_to_store(mixin) },
147
154
  constant_addresses: code_object.children.select{ |child| %i(constant module class).include?(child.type) }.map { |constant| constant.path },
148
155
  )
149
156
 
150
157
  meta_class_object = Objects::MetaClassObject.new(
151
- path: code_object.path,
158
+ path: path_to_store(code_object),
152
159
  sources: code_object.files.map(&method(:convert_source)),
153
160
  primary_source: code_object[:current_file_has_comments] ? convert_source(code_object.files.first) : nil,
154
161
  instance_method_addresses: code_object.meths(included: false, scope: :class).map(&:path),
155
- mixin_addresses: code_object.instance_mixins.map { |mixin| mixin.path },
162
+ mixin_addresses: code_object.instance_mixins.map { |mixin| path_to_store(mixin) },
156
163
  )
157
164
 
158
165
  [module_object, meta_class_object]
@@ -162,23 +169,23 @@ module Yoda
162
169
  # @return [Array<Objects::ClassObject, Objects::MetaClassObject>]
163
170
  def convert_class_object(code_object)
164
171
  class_object = Objects::ClassObject.new(
165
- path: code_object.path,
172
+ path: path_to_store(code_object),
166
173
  document: code_object.docstring.to_s,
167
- tag_list: code_object.tags.map { |tag| convert_tag(tag, code_object.path) },
174
+ tag_list: code_object.tags.map { |tag| convert_tag(tag, path_to_store(code_object)) },
168
175
  sources: code_object.files.map(&method(:convert_source)),
169
176
  primary_source: code_object[:current_file_has_comments] ? convert_source(code_object.files.first) : nil,
170
177
  instance_method_addresses: code_object.meths(included: false, scope: :instance).map(&:path),
171
- mixin_addresses: code_object.instance_mixins.map { |mixin| mixin.path },
172
- constant_addresses: code_object.children.select{ |child| %i(constant module class).include?(child.type) }.map { |constant| constant.path },
173
- superclass_path: code_object.superclass&.path == 'Qnil' ? nil : code_object.superclass&.path,
178
+ mixin_addresses: code_object.instance_mixins.map { |mixin| path_to_store(mixin) },
179
+ constant_addresses: code_object.children.select{ |child| %i(constant module class).include?(child.type) }.map { |constant| path_to_store(constant) },
180
+ superclass_path: !code_object.superclass || code_object.superclass&.path == 'Qnil' ? nil : path_to_store(code_object.superclass),
174
181
  )
175
182
 
176
183
  meta_class_object = Objects::MetaClassObject.new(
177
- path: code_object.path,
184
+ path: path_to_store(code_object),
178
185
  sources: code_object.files.map(&method(:convert_source)),
179
186
  primary_source: code_object[:current_file_has_comments] ? convert_source(code_object.files.first) : nil,
180
187
  instance_method_addresses: code_object.meths(included: false, scope: :class).map(&:path),
181
- mixin_addresses: code_object.class_mixins.map { |mixin| mixin.path },
188
+ mixin_addresses: code_object.class_mixins.map { |mixin| path_to_store(mixin) },
182
189
  )
183
190
 
184
191
  [class_object, meta_class_object]
@@ -220,7 +227,7 @@ module Yoda
220
227
  # @return [Array<Objects::ModuleObject>]
221
228
  def create_proxy_module(code_object)
222
229
  module_object = Objects::ModuleObject.new(
223
- path: code_object.path,
230
+ path: path_to_store(code_object),
224
231
  document: '',
225
232
  tag_list: [],
226
233
  sources: [],
@@ -231,7 +238,7 @@ module Yoda
231
238
  )
232
239
 
233
240
  meta_class_object = Objects::MetaClassObject.new(
234
- path: code_object.path,
241
+ path: path_to_store(code_object),
235
242
  sources: [],
236
243
  primary_source: nil,
237
244
  instance_method_addresses: [],
@@ -244,9 +251,9 @@ module Yoda
244
251
  # @param code_object [::YARD::CodeObjects::Base]
245
252
  # @return [vaid]
246
253
  def register_to_parent_proxy(code_object)
247
- proxy_module = patch.find(code_object.parent.path)
248
- proxy_module.instance_method_addresses.push(code_object.path) if code_object.type == :method
249
- proxy_module.constant_addresses.push(code_object.path) if [:class, :module, :proxy].include?(code_object.type)
254
+ proxy_module = patch.find(path_to_store(code_object.parent))
255
+ proxy_module.instance_method_addresses.push(path_to_store(code_object)) if code_object.type == :method
256
+ proxy_module.constant_addresses.push(path_to_store(code_object)) if [:class, :module, :proxy].include?(code_object.type)
250
257
  end
251
258
 
252
259
  # @param source [(String, Integer)]
@@ -255,6 +262,29 @@ module Yoda
255
262
  file, line = source
256
263
  [root_path ? File.expand_path(file, root_path) : file, line, 0]
257
264
  end
265
+
266
+ # @param code_object [::YARD::CodeObjects::Base]
267
+ # @return [String]
268
+ def path_to_store(object)
269
+ @paths_to_store ||= {}
270
+ @paths_to_store[[object.type, object.path]] ||= calc_path_to_store(object)
271
+ end
272
+
273
+ # @param code_object [::YARD::CodeObjects::Base]
274
+ # @return [String]
275
+ def calc_path_to_store(object)
276
+ return 'Object' if object.root?
277
+ parent_path = path_to_store(object.parent)
278
+
279
+ if object.type == :proxy || object.is_a?(YARD::CodeObjects::Proxy)
280
+ # For now, we suppose the proxy object exists directly under its lexical namespace.
281
+ [path_to_store(object.parent), object.name].join('::')
282
+ elsif object.parent.path == path_to_store(object.parent)
283
+ object.path
284
+ else
285
+ [path_to_store(object.parent), object.name].join(object.sep)
286
+ end.gsub(/^Object::/, '')
287
+ end
258
288
  end
259
289
  end
260
290
  end