yoda-language-server 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (171) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +8 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +78 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +85 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/client/atom/main.js +27 -0
  13. data/client/vscode/.gitignore +4 -0
  14. data/client/vscode/.vscode/launch.json +28 -0
  15. data/client/vscode/.vscode/settings.json +9 -0
  16. data/client/vscode/.vscode/tasks.json +20 -0
  17. data/client/vscode/.vscodeignore +8 -0
  18. data/client/vscode/CHANGELOG.md +7 -0
  19. data/client/vscode/README.md +65 -0
  20. data/client/vscode/package-lock.json +2688 -0
  21. data/client/vscode/package.json +39 -0
  22. data/client/vscode/src/extension.ts +42 -0
  23. data/client/vscode/src/test/extension.test.ts +22 -0
  24. data/client/vscode/src/test/index.ts +22 -0
  25. data/client/vscode/tsconfig.json +16 -0
  26. data/client/vscode/vsc-extension-quickstart.md +33 -0
  27. data/exe/yoda +27 -0
  28. data/lib/yoda.rb +11 -0
  29. data/lib/yoda/evaluation.rb +9 -0
  30. data/lib/yoda/evaluation/code_completion.rb +65 -0
  31. data/lib/yoda/evaluation/code_completion/base_provider.rb +57 -0
  32. data/lib/yoda/evaluation/code_completion/const_provider.rb +90 -0
  33. data/lib/yoda/evaluation/code_completion/method_provider.rb +82 -0
  34. data/lib/yoda/evaluation/code_completion/variable_provider.rb +18 -0
  35. data/lib/yoda/evaluation/comment_completion.rb +70 -0
  36. data/lib/yoda/evaluation/comment_completion/base_provider.rb +64 -0
  37. data/lib/yoda/evaluation/comment_completion/param_provider.rb +18 -0
  38. data/lib/yoda/evaluation/comment_completion/tag_provider.rb +41 -0
  39. data/lib/yoda/evaluation/comment_completion/type_provider.rb +58 -0
  40. data/lib/yoda/evaluation/current_node_explain.rb +70 -0
  41. data/lib/yoda/evaluation/evaluator.rb +103 -0
  42. data/lib/yoda/evaluation/signature_discovery.rb +83 -0
  43. data/lib/yoda/model.rb +12 -0
  44. data/lib/yoda/model/completion_item.rb +56 -0
  45. data/lib/yoda/model/descriptions.rb +10 -0
  46. data/lib/yoda/model/descriptions/base.rb +26 -0
  47. data/lib/yoda/model/descriptions/function_description.rb +40 -0
  48. data/lib/yoda/model/descriptions/value_description.rb +33 -0
  49. data/lib/yoda/model/descriptions/word_description.rb +32 -0
  50. data/lib/yoda/model/function_signatures.rb +13 -0
  51. data/lib/yoda/model/function_signatures/base.rb +68 -0
  52. data/lib/yoda/model/function_signatures/constructor.rb +70 -0
  53. data/lib/yoda/model/function_signatures/formatter.rb +82 -0
  54. data/lib/yoda/model/function_signatures/method.rb +67 -0
  55. data/lib/yoda/model/function_signatures/overload.rb +79 -0
  56. data/lib/yoda/model/function_signatures/parameter_list.rb +108 -0
  57. data/lib/yoda/model/function_signatures/type_builder.rb +101 -0
  58. data/lib/yoda/model/node_signature.rb +28 -0
  59. data/lib/yoda/model/path.rb +96 -0
  60. data/lib/yoda/model/scoped_path.rb +44 -0
  61. data/lib/yoda/model/types.rb +84 -0
  62. data/lib/yoda/model/types/any_type.rb +32 -0
  63. data/lib/yoda/model/types/base.rb +37 -0
  64. data/lib/yoda/model/types/duck_type.rb +41 -0
  65. data/lib/yoda/model/types/function_type.rb +174 -0
  66. data/lib/yoda/model/types/generic_type.rb +66 -0
  67. data/lib/yoda/model/types/instance_type.rb +42 -0
  68. data/lib/yoda/model/types/module_type.rb +42 -0
  69. data/lib/yoda/model/types/sequence_type.rb +53 -0
  70. data/lib/yoda/model/types/union_type.rb +56 -0
  71. data/lib/yoda/model/types/unknown_type.rb +40 -0
  72. data/lib/yoda/model/types/value_type.rb +58 -0
  73. data/lib/yoda/model/values.rb +9 -0
  74. data/lib/yoda/model/values/base.rb +32 -0
  75. data/lib/yoda/model/values/instance_value.rb +65 -0
  76. data/lib/yoda/model/values/module_value.rb +72 -0
  77. data/lib/yoda/parsing.rb +15 -0
  78. data/lib/yoda/parsing/ast_traversable.rb +18 -0
  79. data/lib/yoda/parsing/comment_tokenizer.rb +59 -0
  80. data/lib/yoda/parsing/location.rb +101 -0
  81. data/lib/yoda/parsing/node_objects.rb +10 -0
  82. data/lib/yoda/parsing/node_objects/const_node.rb +52 -0
  83. data/lib/yoda/parsing/node_objects/method_definition.rb +46 -0
  84. data/lib/yoda/parsing/node_objects/namespace.rb +104 -0
  85. data/lib/yoda/parsing/node_objects/send_node.rb +72 -0
  86. data/lib/yoda/parsing/parser.rb +27 -0
  87. data/lib/yoda/parsing/query.rb +11 -0
  88. data/lib/yoda/parsing/query/current_comment_query.rb +80 -0
  89. data/lib/yoda/parsing/query/current_comment_token_query.rb +153 -0
  90. data/lib/yoda/parsing/query/current_commenting_node_query.rb +68 -0
  91. data/lib/yoda/parsing/query/current_location_node_query.rb +51 -0
  92. data/lib/yoda/parsing/query/current_node_comment_query.rb +40 -0
  93. data/lib/yoda/parsing/range.rb +41 -0
  94. data/lib/yoda/parsing/scopes.rb +15 -0
  95. data/lib/yoda/parsing/scopes/base.rb +78 -0
  96. data/lib/yoda/parsing/scopes/builder.rb +60 -0
  97. data/lib/yoda/parsing/scopes/class_definition.rb +47 -0
  98. data/lib/yoda/parsing/scopes/meta_class_definition.rb +44 -0
  99. data/lib/yoda/parsing/scopes/meta_method_definition.rb +70 -0
  100. data/lib/yoda/parsing/scopes/method_definition.rb +69 -0
  101. data/lib/yoda/parsing/scopes/module_definition.rb +36 -0
  102. data/lib/yoda/parsing/scopes/root.rb +25 -0
  103. data/lib/yoda/parsing/source_analyzer.rb +59 -0
  104. data/lib/yoda/parsing/source_cutter.rb +231 -0
  105. data/lib/yoda/parsing/type_parser.rb +141 -0
  106. data/lib/yoda/runner.rb +6 -0
  107. data/lib/yoda/runner/infer.rb +50 -0
  108. data/lib/yoda/runner/setup.rb +26 -0
  109. data/lib/yoda/server.rb +191 -0
  110. data/lib/yoda/server/client_info.rb +98 -0
  111. data/lib/yoda/server/completion_provider.rb +78 -0
  112. data/lib/yoda/server/definition_provider.rb +36 -0
  113. data/lib/yoda/server/deserializer.rb +27 -0
  114. data/lib/yoda/server/hover_provider.rb +38 -0
  115. data/lib/yoda/server/signature_provider.rb +46 -0
  116. data/lib/yoda/store.rb +13 -0
  117. data/lib/yoda/store/actions.rb +10 -0
  118. data/lib/yoda/store/actions/import_core_library.rb +30 -0
  119. data/lib/yoda/store/actions/import_gems.rb +91 -0
  120. data/lib/yoda/store/actions/read_file.rb +36 -0
  121. data/lib/yoda/store/actions/read_project_files.rb +29 -0
  122. data/lib/yoda/store/adapters.rb +14 -0
  123. data/lib/yoda/store/adapters/base.rb +58 -0
  124. data/lib/yoda/store/adapters/leveldb_adapter.rb +80 -0
  125. data/lib/yoda/store/adapters/lmdb_adapter.rb +113 -0
  126. data/lib/yoda/store/objects.rb +46 -0
  127. data/lib/yoda/store/objects/addressable.rb +25 -0
  128. data/lib/yoda/store/objects/base.rb +116 -0
  129. data/lib/yoda/store/objects/class_object.rb +51 -0
  130. data/lib/yoda/store/objects/merger.rb +94 -0
  131. data/lib/yoda/store/objects/meta_class_object.rb +41 -0
  132. data/lib/yoda/store/objects/method_object.rb +94 -0
  133. data/lib/yoda/store/objects/module_object.rb +11 -0
  134. data/lib/yoda/store/objects/namespace_object.rb +67 -0
  135. data/lib/yoda/store/objects/overload.rb +51 -0
  136. data/lib/yoda/store/objects/patch.rb +46 -0
  137. data/lib/yoda/store/objects/patch_set.rb +80 -0
  138. data/lib/yoda/store/objects/tag.rb +62 -0
  139. data/lib/yoda/store/objects/value_object.rb +45 -0
  140. data/lib/yoda/store/project.rb +159 -0
  141. data/lib/yoda/store/query.rb +12 -0
  142. data/lib/yoda/store/query/associators.rb +10 -0
  143. data/lib/yoda/store/query/associators/associate_ancestors.rb +103 -0
  144. data/lib/yoda/store/query/associators/associate_methods.rb +38 -0
  145. data/lib/yoda/store/query/base.rb +16 -0
  146. data/lib/yoda/store/query/find_constant.rb +150 -0
  147. data/lib/yoda/store/query/find_meta_class.rb +18 -0
  148. data/lib/yoda/store/query/find_method.rb +74 -0
  149. data/lib/yoda/store/query/find_signature.rb +43 -0
  150. data/lib/yoda/store/registry.rb +67 -0
  151. data/lib/yoda/store/yard_importer.rb +260 -0
  152. data/lib/yoda/typing.rb +10 -0
  153. data/lib/yoda/typing/context.rb +96 -0
  154. data/lib/yoda/typing/environment.rb +35 -0
  155. data/lib/yoda/typing/evaluator.rb +256 -0
  156. data/lib/yoda/typing/lexical_scope.rb +26 -0
  157. data/lib/yoda/typing/relation.rb +15 -0
  158. data/lib/yoda/typing/traces.rb +9 -0
  159. data/lib/yoda/typing/traces/base.rb +26 -0
  160. data/lib/yoda/typing/traces/normal.rb +22 -0
  161. data/lib/yoda/typing/traces/send.rb +26 -0
  162. data/lib/yoda/version.rb +3 -0
  163. data/lib/yoda/yard_extensions.rb +11 -0
  164. data/lib/yoda/yard_extensions/sig_directive.rb +40 -0
  165. data/lib/yoda/yard_extensions/type_tag.rb +10 -0
  166. data/package.json +76 -0
  167. data/scripts/benchmark.rb +6 -0
  168. data/scripts/build_core_index.sh +16 -0
  169. data/yarn.lock +13 -0
  170. data/yoda-language-server.gemspec +40 -0
  171. metadata +424 -0
@@ -0,0 +1,45 @@
1
+ module Yoda
2
+ module Store
3
+ module Objects
4
+ class ValueObject < Base
5
+ # @return [String]
6
+ attr_reader :value
7
+
8
+ # @return [Array<Symbol>]
9
+ def self.attr_names
10
+ super + %i(value)
11
+ end
12
+
13
+ # @param path [String]
14
+ # @param value [String]
15
+ def initialize(value: nil, **kwargs)
16
+ super(kwargs)
17
+ @value = value
18
+ end
19
+
20
+ # @return [String]
21
+ def name
22
+ @name ||= path.match(MODULE_TAIL_PATTERN) { |md| md[1] || md[2] }
23
+ end
24
+
25
+ def kind
26
+ :value
27
+ end
28
+
29
+ def to_h
30
+ super.merge(value: value)
31
+ end
32
+
33
+ private
34
+
35
+ # @param another [self]
36
+ # @return [Hash]
37
+ def merge_attributes(another)
38
+ super.merge(
39
+ value: another.value || self.value,
40
+ )
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,159 @@
1
+ require 'bundler'
2
+ require 'tmpdir'
3
+ require 'fileutils'
4
+ require 'digest'
5
+
6
+ module Yoda
7
+ module Store
8
+ class Project
9
+ # @type String
10
+ attr_reader :root_path
11
+
12
+ # @type Registry
13
+ attr_reader :registry
14
+
15
+ # @param root_path [String]
16
+ def initialize(root_path)
17
+ fail ArgumentError, root_path unless root_path.is_a?(String)
18
+
19
+ @root_path = File.absolute_path(root_path)
20
+ @registry = Registry.new
21
+ end
22
+
23
+ def clean
24
+ end
25
+
26
+ def setup
27
+ YARD::Logger.instance(STDERR)
28
+ make_dir
29
+ cache.setup
30
+ load_project_files
31
+ self
32
+ end
33
+
34
+ def rebuild_cache(progress: false)
35
+ make_dir
36
+ cache.build(progress: progress)
37
+ end
38
+
39
+ # @param source_path [String]
40
+ def read_source(source_path)
41
+ Actions::ReadFile.run(registry, source_path)
42
+ end
43
+
44
+ def yoda_dir
45
+ File.expand_path('.yoda', root_path)
46
+ end
47
+
48
+ private
49
+
50
+ def make_dir
51
+ File.exist?(yoda_dir) || FileUtils.mkdir(yoda_dir)
52
+ end
53
+
54
+ def load_project_files
55
+ Actions::ReadProjectFiles.new(registry, root_path).run
56
+ end
57
+
58
+ def cache
59
+ @cache ||= Cache.new(self)
60
+ end
61
+
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
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,12 @@
1
+ module Yoda
2
+ module Store
3
+ module Query
4
+ require 'yoda/store/query/base'
5
+ require 'yoda/store/query/find_constant'
6
+ require 'yoda/store/query/find_meta_class'
7
+ require 'yoda/store/query/find_method'
8
+ require 'yoda/store/query/find_signature'
9
+ require 'yoda/store/query/associators'
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module Yoda
2
+ module Store
3
+ module Query
4
+ module Associators
5
+ require 'yoda/store/query/associators/associate_ancestors'
6
+ require 'yoda/store/query/associators/associate_methods'
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,103 @@
1
+ require 'set'
2
+
3
+ module Yoda
4
+ module Store
5
+ module Query
6
+ module Associators
7
+ class AssociateAncestors
8
+ class CircularReferenceError < StandardError; end
9
+
10
+ # @return [Registry]
11
+ attr_reader :registry
12
+
13
+ # @param registry [Registry]
14
+ def initialize(registry)
15
+ @registry = registry
16
+ end
17
+
18
+ # @param obj [Objects::Base]
19
+ def associate(obj)
20
+ if obj.is_a?(Objects::NamespaceObject)
21
+ obj.ancestors = Enumerator.new do |yielder|
22
+ Processor.new(registry).process(obj).each { |klass| yielder << klass }
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ class Processor
30
+ # @return [Registry]
31
+ attr_reader :registry
32
+
33
+ # @param registry [Registry]
34
+ def initialize(registry)
35
+ @registry = registry
36
+ end
37
+
38
+
39
+ # @return [Set<Objects::Base>]
40
+ def met
41
+ @met ||= Set.new
42
+ end
43
+
44
+ # @param scope [Objects::NamespaceObject]
45
+ # @return [Enumerator<Objects::NamespaceObject>]
46
+ def process(scope)
47
+ fail CircularReferenceError, scope if met.include?(scope)
48
+ met.add(scope)
49
+
50
+ Enumerator.new do |yielder|
51
+ if scope.is_a?(Objects::NamespaceObject)
52
+ yielder << scope
53
+ find_mixins(scope).each { |mixin| yielder << mixin }
54
+
55
+ if scope.is_a?(Objects::MetaClassObject)
56
+ find_metaclass_superclass_ancestors(scope).each { |obj| yielder << obj }
57
+ elsif scope.is_a?(Objects::ClassObject)
58
+ find_superclass_ancestors(scope).each { |obj| yielder << obj }
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # @param obj [Objects::NamespaceObject]
65
+ # @return [Enumerator<Objects::NamespaceObject>]
66
+ def find_mixins(obj)
67
+ Enumerator.new do |yielder|
68
+ obj.mixin_addresses.each do |address|
69
+ if el = registry.find(address.to_s)
70
+ yielder << el
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ # @param obj [Objects::ClassObject]
77
+ # @return [Enumerator<Objects::NamespaceObject>]
78
+ def find_superclass_ancestors(obj)
79
+ if obj.superclass_path && super_class = FindConstant.new(registry).find(obj.superclass_path)
80
+ process(super_class)
81
+ else
82
+ []
83
+ end
84
+ end
85
+
86
+ # @param obj [Objects::MetaClassObject]
87
+ # @return [Enumerator<Objects::NamespaceObject>]
88
+ def find_metaclass_superclass_ancestors(obj)
89
+ base_class = registry.find(obj.base_class_address)
90
+ if base_class && base_class.is_a?(Objects::ClassObject) && base_class.superclass_path
91
+ (meta_class = FindMetaClass.new(registry).find(base_class.superclass_path || 'Object')) ? process(meta_class) : []
92
+ elsif base_class
93
+ (class_object = registry.find('Class')) ? process(class_object) : []
94
+ else
95
+ []
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,38 @@
1
+ module Yoda
2
+ module Store
3
+ module Query
4
+ module Associators
5
+ class AssociateMethods
6
+ # @return [Registry]
7
+ attr_reader :registry
8
+
9
+ # @param registry [Registry]
10
+ def initialize(registry)
11
+ @registry = registry
12
+ end
13
+
14
+ # @param obj [Object::Base]
15
+ def associate(obj)
16
+ if obj.is_a?(Objects::NamespaceObject)
17
+ AssociateMethods.new(registry).associate(obj)
18
+ obj.methods = Enumerator.new do |yielder|
19
+ name_set = Set.new
20
+
21
+ obj.ancestors.each do |ancestor|
22
+ ancestor.instance_method_addresses.each do |method_address|
23
+ method_name = Objects::MethodObject.name_of_path(method_address)
24
+ next name_set.has_key?(method_name)
25
+ name_set.add(method_name)
26
+ if el = registry.find(method_address)
27
+ yielder << el
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,16 @@
1
+ module Yoda
2
+ module Store
3
+ module Query
4
+ # @abstract
5
+ class Base
6
+ # @return [Registry]
7
+ attr_reader :registry
8
+
9
+ # @param registry [Registry]
10
+ def initialize(registry)
11
+ @registry = registry
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,150 @@
1
+ module Yoda
2
+ module Store
3
+ module Query
4
+ class FindConstant < Base
5
+ # @param path [String, Model::Path, Model::ScopedPath]
6
+ # @return [Objects::Base, nil]
7
+ def find(path)
8
+ lexical_scope_paths = lexical_scopes_of(path)
9
+ base_name, *constant_names = path_of(path).split
10
+ base_namespace = select_base_namespace(base_name, lexical_scope_paths).first
11
+
12
+ find_constant(constant_names.join('::'), base_namespace)
13
+ end
14
+
15
+ # @param path [String, Model::Path, Model::ScopedPath]
16
+ # @return [Array<Objects::Base>]
17
+ def select_with_prefix(path)
18
+ lexical_scope_paths = lexical_scopes_of(path)
19
+ base_name, *constant_names, bottom_name = path_of(path).split
20
+
21
+ if constant_names.empty? && !bottom_name
22
+ # When the path does not contain separator (`::`)
23
+ select_base_namespace(/\A#{Regexp.escape(base_name || '')}/, lexical_scope_paths).to_a.uniq
24
+ else
25
+ base_namespace = select_base_namespace(base_name, lexical_scope_paths).first
26
+ scope = find_constant(constant_names.join('::'), base_namespace)
27
+ return [] unless scope
28
+ select_constants_from_ancestors(scope, /\A#{bottom_name}/).to_a
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ # Find namespaces which matches with the lexical scopes and the unnested constant name.
35
+ #
36
+ # @param base_name [String, Regexp] is an unnested constant name or a pattern of unnested constant name.
37
+ # @param lexical_scope_paths [Array<String>]
38
+ # @return [Enumerator<Objects::Base>]
39
+ def select_base_namespace(base_name, lexical_scope_paths)
40
+ Enumerator.new do |yielder|
41
+ lexical_scope_paths.each do |path|
42
+ scope = find_constant(path.to_s)
43
+ next if !scope || !scope.is_a?(Objects::NamespaceObject)
44
+ select_child_constants(scope, base_name).each do |obj|
45
+ yielder << obj
46
+ end
47
+ end
48
+
49
+ nearest_scope_path = lexical_scope_paths.first
50
+ if nearest_scope_path && nearest_scope = find_constant(nearest_scope_path) && nearest_scope.is_a?(Objects::NamespaceObject)
51
+ select_constants_from_ancestors(nearest_scope, base_name).each do |obj|
52
+ yielder << obj
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ # @param name [String, Model::Path]
59
+ # @param namespace [Objects::Base, nil]
60
+ # @return [Objects::Base, nil]
61
+ def find_constant(name, namespace = nil)
62
+ constant_names = path_of(name).split
63
+ namespace = registry.find('Object') unless namespace
64
+ constant_names.reduce(namespace) do |namespace, name|
65
+ if namespace
66
+ select_constants_from_ancestors(namespace, name).first
67
+ else
68
+ return nil
69
+ end
70
+ end
71
+ end
72
+
73
+ # @param scope [Objects::NamespaceObject]
74
+ # @param name [String, Regexp]
75
+ # @return [Enumerator<Objects::Base>]
76
+ def select_constants_from_ancestors(scope, name)
77
+ Enumerator.new do |yielder|
78
+ met = Set.new
79
+
80
+ Associators::AssociateAncestors.new(registry).associate(scope)
81
+ scope.ancestors.each do |ancestor|
82
+ select_child_constants(ancestor, name).each do |obj|
83
+ next if met.include?(obj.name)
84
+ met.add(obj.name)
85
+ yielder << obj
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ # @param scope [Objects::Base]
92
+ # @param name [String, Regexp]
93
+ # @return [Enumerator<Objects::Base>]
94
+ def select_child_constants(scope, name)
95
+ Enumerator.new do |yielder|
96
+ if scope.is_a?(Objects::NamespaceObject)
97
+ scope.constant_addresses.select { |address| match_name?(Model::Path.new(address).basename, name) }.each do |address|
98
+ obj = registry.find(address)
99
+ yielder << obj if obj
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ # @param path [String, Model::Path, Model::ScopedPath]
106
+ # @return [Array<String>]
107
+ def lexical_scopes_of(path)
108
+ case path
109
+ when Model::Path
110
+ ['Object']
111
+ when Model::ScopedPath
112
+ if path.path.absolute?
113
+ ['Object']
114
+ else
115
+ path.scopes.map { |scope| scope.to_s.gsub(/\A::/, '') }
116
+ end
117
+ else
118
+ ['Object']
119
+ end
120
+ end
121
+
122
+ # @param path [String, Model::Path, Model::ScopedPath]
123
+ # @return [Model::Path]
124
+ def path_of(path)
125
+ case path
126
+ when Model::Path
127
+ path
128
+ when Model::ScopedPath
129
+ path.path
130
+ when String
131
+ Model::Path.new(path == '::' ? '::' : path.gsub(/\A::/, ''))
132
+ else
133
+ fail ArgumentError, path
134
+ end
135
+ end
136
+
137
+ # @param name [String]
138
+ # @param expected_name_or_pattern [String, Regexp]
139
+ # @return [true, false]
140
+ def match_name?(name, expected_name_or_pattern)
141
+ if expected_name_or_pattern.is_a?(String)
142
+ name == expected_name_or_pattern
143
+ else
144
+ name.match?(expected_name_or_pattern)
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end