yoda-language-server 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (389) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +23 -0
  3. data/.gitignore +1 -1
  4. data/.rspec +1 -0
  5. data/Gemfile +2 -0
  6. data/README.md +23 -11
  7. data/Rakefile +32 -0
  8. data/bin/bench +12 -0
  9. data/bin/local +7 -0
  10. data/client/atom/.gitignore +1 -0
  11. data/{package.json → client/atom/package.json} +1 -1
  12. data/{yarn.lock → client/atom/yarn.lock} +0 -0
  13. data/client/vscode/.gitignore +1 -1
  14. data/client/vscode/.vscode/launch.json +0 -11
  15. data/client/vscode/.vscode/settings.json +5 -1
  16. data/client/vscode/LICENSE.txt +21 -0
  17. data/client/vscode/README.md +4 -55
  18. data/client/vscode/package-lock.json +4195 -1360
  19. data/client/vscode/package.json +30 -15
  20. data/client/vscode/src/config.ts +13 -0
  21. data/client/vscode/src/extension.ts +11 -27
  22. data/client/vscode/src/install-tools.ts +69 -0
  23. data/client/vscode/src/language-server.ts +54 -0
  24. data/client/vscode/src/status.ts +3 -0
  25. data/client/vscode/src/test/runTest.ts +30 -0
  26. data/client/vscode/src/test/{completion.test.ts → suite/completion.test.ts} +2 -1
  27. data/client/vscode/src/test/suite/hover.test.ts +43 -0
  28. data/client/vscode/src/test/suite/index.ts +27 -0
  29. data/client/vscode/tsconfig.json +4 -1
  30. data/exe/yoda +1 -1
  31. data/lib/yoda/ast/array_node.rb +10 -0
  32. data/lib/yoda/ast/assignment_node.rb +15 -0
  33. data/lib/yoda/ast/block_call_node.rb +20 -0
  34. data/lib/yoda/ast/block_node.rb +10 -0
  35. data/lib/yoda/ast/case_node.rb +20 -0
  36. data/lib/yoda/ast/center_operator_node.rb +20 -0
  37. data/lib/yoda/ast/class_node.rb +22 -0
  38. data/lib/yoda/ast/comment_block/base_part.rb +12 -0
  39. data/lib/yoda/ast/comment_block/range_calculation.rb +58 -0
  40. data/lib/yoda/ast/comment_block/tag_part.rb +88 -0
  41. data/lib/yoda/ast/comment_block/tag_text_name_part.rb +35 -0
  42. data/lib/yoda/ast/comment_block/tag_text_type_part.rb +52 -0
  43. data/lib/yoda/ast/comment_block/text_part.rb +28 -0
  44. data/lib/yoda/ast/comment_block/token.rb +44 -0
  45. data/lib/yoda/ast/comment_block.rb +119 -0
  46. data/lib/yoda/ast/conditional_loop_node.rb +15 -0
  47. data/lib/yoda/ast/constant_assignment_node.rb +20 -0
  48. data/lib/yoda/ast/constant_base_node.rb +42 -0
  49. data/lib/yoda/ast/constant_node.rb +53 -0
  50. data/lib/yoda/ast/def_node.rb +27 -0
  51. data/lib/yoda/ast/def_singleton_node.rb +32 -0
  52. data/lib/yoda/ast/empty_vnode.rb +24 -0
  53. data/lib/yoda/ast/ensure_node.rb +15 -0
  54. data/lib/yoda/ast/for_node.rb +20 -0
  55. data/lib/yoda/ast/hash_node.rb +10 -0
  56. data/lib/yoda/ast/if_node.rb +20 -0
  57. data/lib/yoda/ast/interpolation_text_node.rb +10 -0
  58. data/lib/yoda/ast/kwsplat_node.rb +10 -0
  59. data/lib/yoda/ast/left_operator_node.rb +15 -0
  60. data/lib/yoda/ast/literal_node.rb +18 -0
  61. data/lib/yoda/ast/method_traversable.rb +22 -0
  62. data/lib/yoda/ast/module_node.rb +17 -0
  63. data/lib/yoda/ast/multiple_left_hand_side_node.rb +31 -0
  64. data/lib/yoda/ast/name_vnode.rb +28 -0
  65. data/lib/yoda/ast/namespace.rb +36 -0
  66. data/lib/yoda/ast/namespace_traversable.rb +39 -0
  67. data/lib/yoda/ast/node.rb +52 -0
  68. data/lib/yoda/ast/optional_parameter_node.rb +20 -0
  69. data/lib/yoda/ast/pair_node.rb +15 -0
  70. data/lib/yoda/ast/parameter_node.rb +25 -0
  71. data/lib/yoda/ast/parameters_node.rb +62 -0
  72. data/lib/yoda/ast/rescue_clause_node.rb +20 -0
  73. data/lib/yoda/ast/rescue_node.rb +20 -0
  74. data/lib/yoda/ast/root_vnode.rb +48 -0
  75. data/lib/yoda/ast/send_node.rb +100 -0
  76. data/lib/yoda/ast/singleton_class_node.rb +17 -0
  77. data/lib/yoda/ast/special_call_node.rb +10 -0
  78. data/lib/yoda/ast/traversable.rb +34 -0
  79. data/lib/yoda/ast/value_vnode.rb +28 -0
  80. data/lib/yoda/ast/variable_node.rb +15 -0
  81. data/lib/yoda/ast/vnode.rb +170 -0
  82. data/lib/yoda/ast/when_node.rb +15 -0
  83. data/lib/yoda/ast.rb +140 -0
  84. data/lib/yoda/cli/analyze_deps.rb +102 -0
  85. data/lib/yoda/{commands → cli}/base.rb +1 -1
  86. data/lib/yoda/{commands → cli}/complete.rb +12 -9
  87. data/lib/yoda/cli/console.rb +17 -0
  88. data/lib/yoda/{commands → cli}/file_cursor_parsable.rb +1 -1
  89. data/lib/yoda/{commands → cli}/infer.rb +4 -4
  90. data/lib/yoda/cli.rb +117 -0
  91. data/lib/yoda/has_services.rb +40 -0
  92. data/lib/yoda/instrument.rb +14 -5
  93. data/lib/yoda/logger.rb +4 -2
  94. data/lib/yoda/missing_delegatable.rb +34 -0
  95. data/lib/yoda/model/completion_item.rb +43 -11
  96. data/lib/yoda/model/descriptions/base.rb +11 -0
  97. data/lib/yoda/model/descriptions/comment_token_description.rb +46 -0
  98. data/lib/yoda/model/descriptions/function_description.rb +19 -2
  99. data/lib/yoda/model/descriptions/node_description.rb +19 -8
  100. data/lib/yoda/model/descriptions/variable_description.rb +41 -0
  101. data/lib/yoda/model/descriptions.rb +2 -0
  102. data/lib/yoda/model/environment/accessor_interface.rb +43 -0
  103. data/lib/yoda/model/environment/instance_accessor.rb +121 -0
  104. data/lib/yoda/model/environment/namespace_members.rb +104 -0
  105. data/lib/yoda/model/environment/singleton_accessor.rb +66 -0
  106. data/lib/yoda/model/environment/value_factory.rb +151 -0
  107. data/lib/yoda/model/environment/value_resolve_context.rb +62 -0
  108. data/lib/yoda/model/environment/with_cache.rb +14 -0
  109. data/lib/yoda/model/environment.rb +122 -0
  110. data/lib/yoda/model/function_signatures/base.rb +22 -2
  111. data/lib/yoda/model/function_signatures/constructor.rb +17 -1
  112. data/lib/yoda/model/function_signatures/method.rb +18 -2
  113. data/lib/yoda/model/function_signatures/overload.rb +17 -1
  114. data/lib/yoda/model/function_signatures/parameter_list.rb +17 -0
  115. data/lib/yoda/model/function_signatures/rbs_method.rb +91 -0
  116. data/lib/yoda/model/function_signatures/type_builder.rb +63 -31
  117. data/lib/yoda/model/function_signatures/wrapper.rb +46 -0
  118. data/lib/yoda/model/function_signatures.rb +2 -0
  119. data/lib/yoda/model/lexical_context.rb +40 -0
  120. data/lib/yoda/model/node_signatures/base.rb +42 -0
  121. data/lib/yoda/model/node_signatures/const.rb +20 -0
  122. data/lib/yoda/model/node_signatures/method_definition.rb +20 -0
  123. data/lib/yoda/model/node_signatures/node.rb +11 -0
  124. data/lib/yoda/model/node_signatures/send.rb +20 -0
  125. data/lib/yoda/model/node_signatures.rb +35 -0
  126. data/lib/yoda/model/parameters/base.rb +14 -0
  127. data/lib/yoda/model/parameters/binder.rb +148 -0
  128. data/lib/yoda/model/parameters/multiple.rb +36 -0
  129. data/lib/yoda/model/parameters/named.rb +20 -0
  130. data/lib/yoda/model/parameters/unnamed.rb +12 -0
  131. data/lib/yoda/model/parameters.rb +11 -0
  132. data/lib/yoda/model/path.rb +16 -0
  133. data/lib/yoda/model/primary_source_inferencer.rb +34 -0
  134. data/lib/yoda/model/scoped_path.rb +13 -2
  135. data/lib/yoda/model/{types → type_expressions}/any_type.rb +7 -3
  136. data/lib/yoda/model/{types → type_expressions}/base.rb +10 -3
  137. data/lib/yoda/model/{types → type_expressions}/duck_type.rb +8 -2
  138. data/lib/yoda/model/type_expressions/function_type/parameter.rb +95 -0
  139. data/lib/yoda/model/{types → type_expressions}/function_type.rb +61 -70
  140. data/lib/yoda/model/type_expressions/generator.rb +28 -0
  141. data/lib/yoda/model/{types → type_expressions}/generic_type.rb +17 -2
  142. data/lib/yoda/model/{types → type_expressions}/instance_type.rb +10 -2
  143. data/lib/yoda/model/{types → type_expressions}/module_type.rb +7 -1
  144. data/lib/yoda/model/type_expressions/self_type.rb +38 -0
  145. data/lib/yoda/model/{types → type_expressions}/sequence_type.rb +6 -1
  146. data/lib/yoda/model/{types → type_expressions}/union_type.rb +7 -3
  147. data/lib/yoda/model/{types → type_expressions}/unknown_type.rb +6 -1
  148. data/lib/yoda/model/{types → type_expressions}/value_type.rb +17 -1
  149. data/lib/yoda/model/type_expressions/void_type.rb +37 -0
  150. data/lib/yoda/model/type_expressions.rb +40 -0
  151. data/lib/yoda/model/values/base.rb +23 -8
  152. data/lib/yoda/model/values/empty_value.rb +39 -0
  153. data/lib/yoda/model/values/instance_value.rb +24 -43
  154. data/lib/yoda/model/values/intersection_value.rb +48 -0
  155. data/lib/yoda/model/values/literal_value.rb +23 -0
  156. data/lib/yoda/model/values/union_value.rb +50 -0
  157. data/lib/yoda/model/values.rb +5 -1
  158. data/lib/yoda/model/yard_type_parser.rb +35 -0
  159. data/lib/yoda/model.rb +7 -2
  160. data/lib/yoda/parsing/comment_tokenizer.rb +21 -4
  161. data/lib/yoda/parsing/location.rb +1 -1
  162. data/lib/yoda/parsing/node_objects/args_node.rb +85 -0
  163. data/lib/yoda/parsing/node_objects/const_node.rb +3 -3
  164. data/lib/yoda/parsing/node_objects/mlhs_node.rb +31 -0
  165. data/lib/yoda/parsing/node_objects.rb +2 -0
  166. data/lib/yoda/parsing/parser.rb +44 -5
  167. data/lib/yoda/parsing/query/current_comment_token_query.rb +4 -3
  168. data/lib/yoda/parsing/query/current_commenting_node_query.rb +7 -7
  169. data/lib/yoda/parsing/scopes/base.rb +1 -1
  170. data/lib/yoda/parsing/scopes/builder.rb +1 -1
  171. data/lib/yoda/parsing/source_cutter.rb +6 -2
  172. data/lib/yoda/parsing/traverser/matcher.rb +65 -0
  173. data/lib/yoda/parsing/traverser/query_interface.rb +68 -0
  174. data/lib/yoda/parsing/traverser/result_set.rb +37 -0
  175. data/lib/yoda/parsing/traverser.rb +29 -0
  176. data/lib/yoda/parsing/type_parser.rb +24 -20
  177. data/lib/yoda/parsing.rb +27 -1
  178. data/lib/yoda/presentation/code_completion/constant.rb +27 -0
  179. data/lib/yoda/server/lifecycle_handler.rb +81 -4
  180. data/lib/yoda/server/notifier.rb +131 -0
  181. data/lib/yoda/server/providers/completion.rb +31 -17
  182. data/lib/yoda/server/providers/definition.rb +38 -10
  183. data/lib/yoda/server/providers/hover.rb +26 -10
  184. data/lib/yoda/server/providers/reportable_progress.rb +75 -0
  185. data/lib/yoda/server/providers/signature.rb +20 -7
  186. data/lib/yoda/server/providers/text_document_did_change.rb +1 -1
  187. data/lib/yoda/server/providers/text_document_did_open.rb +1 -1
  188. data/lib/yoda/server/providers/with_timeout.rb +36 -0
  189. data/lib/yoda/server/providers/workspace_did_change_workspace_folders.rb +33 -0
  190. data/lib/yoda/server/providers/workspace_did_create_files.rb +21 -0
  191. data/lib/yoda/server/providers/workspace_did_delete_files.rb +21 -0
  192. data/lib/yoda/server/providers/workspace_did_rename_files.rb +22 -0
  193. data/lib/yoda/server/providers/workspace_symbol.rb +82 -0
  194. data/lib/yoda/server/providers.rb +12 -0
  195. data/lib/yoda/server/root_handler.rb +27 -23
  196. data/lib/yoda/server/rootless_workspace.rb +62 -0
  197. data/lib/yoda/server/scheduler.rb +50 -0
  198. data/lib/yoda/server/session.rb +71 -25
  199. data/lib/yoda/server/uri_decoder.rb +16 -0
  200. data/lib/yoda/server/workspace.rb +117 -0
  201. data/lib/yoda/server.rb +9 -2
  202. data/lib/yoda/services/code_completion/base_provider.rb +52 -0
  203. data/lib/yoda/{evaluation → services}/code_completion/const_provider.rb +35 -25
  204. data/lib/yoda/services/code_completion/keyword_provider.rb +67 -0
  205. data/lib/yoda/services/code_completion/local_variable_provider.rb +51 -0
  206. data/lib/yoda/{evaluation → services}/code_completion/method_provider.rb +14 -32
  207. data/lib/yoda/services/code_completion.rb +74 -0
  208. data/lib/yoda/{evaluation → services}/comment_completion/base_provider.rb +11 -6
  209. data/lib/yoda/{evaluation → services}/comment_completion/param_provider.rb +1 -1
  210. data/lib/yoda/{evaluation → services}/comment_completion/tag_provider.rb +1 -1
  211. data/lib/yoda/{evaluation → services}/comment_completion/type_provider.rb +6 -5
  212. data/lib/yoda/{evaluation → services}/comment_completion.rb +13 -13
  213. data/lib/yoda/services/current_node_explain/comment_signature.rb +77 -0
  214. data/lib/yoda/services/current_node_explain.rb +72 -0
  215. data/lib/yoda/services/evaluator.rb +44 -0
  216. data/lib/yoda/services/signature_discovery.rb +74 -0
  217. data/lib/yoda/services.rb +9 -0
  218. data/lib/yoda/store/actions/build_core_index.rb +38 -9
  219. data/lib/yoda/store/actions/import_core_library.rb +14 -19
  220. data/lib/yoda/store/actions/import_gem.rb +65 -33
  221. data/lib/yoda/store/actions/import_project_dependencies.rb +47 -0
  222. data/lib/yoda/store/actions/import_std_library.rb +13 -18
  223. data/lib/yoda/store/actions/read_file.rb +21 -6
  224. data/lib/yoda/store/actions/read_project_files.rb +7 -1
  225. data/lib/yoda/store/actions.rb +1 -0
  226. data/lib/yoda/store/adapters/base.rb +39 -0
  227. data/lib/yoda/store/adapters/gdbm_adapter/namespace_accessor.rb +152 -0
  228. data/lib/yoda/store/adapters/gdbm_adapter.rb +56 -0
  229. data/lib/yoda/store/adapters/lazy_adapter.rb +34 -0
  230. data/lib/yoda/store/adapters/memory_adapter.rb +21 -0
  231. data/lib/yoda/store/adapters.rb +12 -4
  232. data/lib/yoda/store/config.rb +43 -0
  233. data/lib/yoda/store/file_tree.rb +131 -0
  234. data/lib/yoda/store/objects/base.rb +73 -4
  235. data/lib/yoda/store/objects/class_object.rb +16 -7
  236. data/lib/yoda/store/objects/connected_delegation.rb +25 -0
  237. data/lib/yoda/store/objects/libraries_status.rb +67 -0
  238. data/lib/yoda/store/objects/library/core.rb +72 -0
  239. data/lib/yoda/store/objects/library/gem.rb +141 -0
  240. data/lib/yoda/store/objects/library/std.rb +72 -0
  241. data/lib/yoda/store/objects/library/with_registry.rb +28 -0
  242. data/lib/yoda/store/objects/library.rb +24 -0
  243. data/lib/yoda/store/objects/map.rb +61 -0
  244. data/lib/yoda/store/objects/merger.rb +96 -12
  245. data/lib/yoda/store/objects/meta_class_object.rb +28 -1
  246. data/lib/yoda/store/objects/method_object.rb +39 -6
  247. data/lib/yoda/store/objects/module_object.rb +3 -0
  248. data/lib/yoda/store/objects/namespace_object.rb +38 -1
  249. data/lib/yoda/store/objects/overload.rb +1 -1
  250. data/lib/yoda/store/objects/patch.rb +56 -12
  251. data/lib/yoda/store/objects/patch_set.rb +58 -26
  252. data/lib/yoda/store/objects/serializable.rb +17 -2
  253. data/lib/yoda/store/objects/serializable_set.rb +23 -0
  254. data/lib/yoda/store/objects/tag.rb +1 -1
  255. data/lib/yoda/store/objects/value_object.rb +5 -1
  256. data/lib/yoda/store/objects.rb +5 -1
  257. data/lib/yoda/store/project/dependency.rb +83 -0
  258. data/lib/yoda/store/project/file_finder.rb +101 -0
  259. data/lib/yoda/store/project/rbs_loader.rb +41 -0
  260. data/lib/yoda/store/project/setuper.rb +68 -0
  261. data/lib/yoda/store/project.rb +99 -41
  262. data/lib/yoda/store/query/ancestor_tree.rb +126 -0
  263. data/lib/yoda/store/query/associators/associate_ancestors.rb +5 -78
  264. data/lib/yoda/store/query/associators/associate_methods.rb +11 -8
  265. data/lib/yoda/store/query/constant_member_set.rb +33 -0
  266. data/lib/yoda/store/query/find_constant.rb +23 -6
  267. data/lib/yoda/store/query/find_meta_class.rb +1 -1
  268. data/lib/yoda/store/query/find_method.rb +12 -25
  269. data/lib/yoda/store/query/find_signature.rb +11 -3
  270. data/lib/yoda/store/query/find_workspace_objects.rb +36 -0
  271. data/lib/yoda/store/query/method_member_set.rb +50 -0
  272. data/lib/yoda/store/query.rb +4 -0
  273. data/lib/yoda/store/registry/cache.rb +65 -0
  274. data/lib/yoda/store/registry/composer.rb +47 -0
  275. data/lib/yoda/store/registry/index.rb +116 -0
  276. data/lib/yoda/store/registry/library_registry.rb +69 -0
  277. data/lib/yoda/store/registry/library_registry_set.rb +133 -0
  278. data/lib/yoda/store/registry/local_store.rb +38 -0
  279. data/lib/yoda/store/registry/project_registry.rb +72 -0
  280. data/lib/yoda/store/registry.rb +25 -89
  281. data/lib/yoda/store/transformers/core_visibility.rb +61 -0
  282. data/lib/yoda/store/transformers.rb +7 -0
  283. data/lib/yoda/store/version_store.rb +71 -0
  284. data/lib/yoda/store/yard_importer.rb +22 -20
  285. data/lib/yoda/store.rb +15 -1
  286. data/lib/yoda/typing/constant_resolver/cbase_query.rb +14 -0
  287. data/lib/yoda/typing/constant_resolver/member_query.rb +27 -0
  288. data/lib/yoda/typing/constant_resolver/node_tracer.rb +36 -0
  289. data/lib/yoda/typing/constant_resolver/query.rb +59 -0
  290. data/lib/yoda/typing/constant_resolver/relative_base_query.rb +14 -0
  291. data/lib/yoda/typing/constant_resolver.rb +105 -0
  292. data/lib/yoda/typing/contexts/base_context.rb +83 -0
  293. data/lib/yoda/typing/contexts/block_context.rb +14 -0
  294. data/lib/yoda/typing/contexts/context_derivation.rb +59 -0
  295. data/lib/yoda/typing/contexts/method_context.rb +14 -0
  296. data/lib/yoda/typing/contexts/namespace_block_context.rb +29 -0
  297. data/lib/yoda/typing/contexts/namespace_context.rb +14 -0
  298. data/lib/yoda/typing/contexts.rb +25 -0
  299. data/lib/yoda/typing/environment.rb +1 -1
  300. data/lib/yoda/typing/inferencer/arguments.rb +38 -0
  301. data/lib/yoda/typing/inferencer/arguments_binder.rb +24 -0
  302. data/lib/yoda/typing/inferencer/ast_traverser.rb +408 -0
  303. data/lib/yoda/typing/inferencer/method_resolver.rb +81 -0
  304. data/lib/yoda/typing/inferencer/object_resolver.rb +55 -0
  305. data/lib/yoda/typing/inferencer/parameter_binder.rb +176 -0
  306. data/lib/yoda/typing/inferencer/tracer.rb +177 -0
  307. data/lib/yoda/typing/inferencer/type_binding.rb +46 -0
  308. data/lib/yoda/typing/inferencer.rb +52 -0
  309. data/lib/yoda/typing/node_info.rb +78 -0
  310. data/lib/yoda/typing/relation.rb +2 -2
  311. data/lib/yoda/typing/traces/base.rb +2 -2
  312. data/lib/yoda/typing/traces/normal.rb +3 -3
  313. data/lib/yoda/typing/traces/send.rb +4 -4
  314. data/lib/yoda/typing/tree/base.rb +46 -0
  315. data/lib/yoda/typing/tree/begin.rb +15 -0
  316. data/lib/yoda/typing/tree/block.rb +12 -0
  317. data/lib/yoda/typing/tree/case.rb +29 -0
  318. data/lib/yoda/typing/tree/class_tree.rb +31 -0
  319. data/lib/yoda/typing/tree/const.rb +12 -0
  320. data/lib/yoda/typing/tree/constant_assignment.rb +12 -0
  321. data/lib/yoda/typing/tree/defined.rb +11 -0
  322. data/lib/yoda/typing/tree/escape.rb +12 -0
  323. data/lib/yoda/typing/tree/for.rb +8 -0
  324. data/lib/yoda/typing/tree/hash_body.rb +36 -0
  325. data/lib/yoda/typing/tree/if.rb +17 -0
  326. data/lib/yoda/typing/tree/literal.rb +46 -0
  327. data/lib/yoda/typing/tree/literal_with_interpolation.rb +21 -0
  328. data/lib/yoda/typing/tree/logical_assignment.rb +16 -0
  329. data/lib/yoda/typing/tree/logical_operator.rb +15 -0
  330. data/lib/yoda/typing/tree/method.rb +43 -0
  331. data/lib/yoda/typing/tree/module_tree.rb +31 -0
  332. data/lib/yoda/typing/tree/multiple_assignment.rb +20 -0
  333. data/lib/yoda/typing/tree/rescue_body.rb +12 -0
  334. data/lib/yoda/typing/tree/self.rb +11 -0
  335. data/lib/yoda/typing/tree/send.rb +71 -0
  336. data/lib/yoda/typing/tree/singleton_method.rb +47 -0
  337. data/lib/yoda/typing/tree/super.rb +12 -0
  338. data/lib/yoda/typing/tree/variable.rb +20 -0
  339. data/lib/yoda/typing/tree/variable_assignment.rb +20 -0
  340. data/lib/yoda/typing/tree/while.rb +12 -0
  341. data/lib/yoda/typing/tree/yield.rb +12 -0
  342. data/lib/yoda/typing/tree.rb +96 -0
  343. data/lib/yoda/typing/types/any.rb +15 -0
  344. data/lib/yoda/typing/types/associative_array.rb +28 -0
  345. data/lib/yoda/typing/types/base.rb +25 -0
  346. data/lib/yoda/typing/types/converter.rb +67 -0
  347. data/lib/yoda/typing/types/function.rb +68 -0
  348. data/lib/yoda/typing/types/generator.rb +306 -0
  349. data/lib/yoda/typing/types/generic.rb +29 -0
  350. data/lib/yoda/typing/types/instance.rb +30 -0
  351. data/lib/yoda/typing/types/instance_type.rb +74 -0
  352. data/lib/yoda/typing/types/method.rb +39 -0
  353. data/lib/yoda/typing/types/rbs_type_wrapper_interface.rb +44 -0
  354. data/lib/yoda/typing/types/singleton_type.rb +75 -0
  355. data/lib/yoda/typing/types/tuple.rb +24 -0
  356. data/lib/yoda/typing/types/type.rb +60 -0
  357. data/lib/yoda/typing/types/union.rb +38 -0
  358. data/lib/yoda/typing/types/var.rb +38 -0
  359. data/lib/yoda/typing/types.rb +24 -0
  360. data/lib/yoda/typing.rb +5 -2
  361. data/lib/yoda/version.rb +1 -1
  362. data/lib/yoda.rb +6 -2
  363. data/scripts/benchmark.rb +1 -1
  364. data/yoda-language-server.gemspec +16 -15
  365. metadata +334 -110
  366. data/.travis.yml +0 -8
  367. data/client/vscode/src/test/index.ts +0 -24
  368. data/lib/yoda/commands/setup.rb +0 -51
  369. data/lib/yoda/commands.rb +0 -69
  370. data/lib/yoda/evaluation/code_completion/base_provider.rb +0 -57
  371. data/lib/yoda/evaluation/code_completion/variable_provider.rb +0 -18
  372. data/lib/yoda/evaluation/code_completion.rb +0 -65
  373. data/lib/yoda/evaluation/current_node_explain.rb +0 -71
  374. data/lib/yoda/evaluation/evaluator.rb +0 -105
  375. data/lib/yoda/evaluation/signature_discovery.rb +0 -85
  376. data/lib/yoda/evaluation.rb +0 -9
  377. data/lib/yoda/model/node_signature.rb +0 -43
  378. data/lib/yoda/model/types.rb +0 -84
  379. data/lib/yoda/model/values/module_value.rb +0 -72
  380. data/lib/yoda/parsing/source_analyzer.rb +0 -59
  381. data/lib/yoda/server/file_store.rb +0 -57
  382. data/lib/yoda/store/adapters/leveldb_adapter.rb +0 -80
  383. data/lib/yoda/store/adapters/lmdb_adapter.rb +0 -113
  384. data/lib/yoda/store/objects/project_status.rb +0 -169
  385. data/lib/yoda/store/project/cache.rb +0 -78
  386. data/lib/yoda/store/project/library_doc_loader.rb +0 -114
  387. data/lib/yoda/typing/context.rb +0 -96
  388. data/lib/yoda/typing/evaluator.rb +0 -259
  389. data/scripts/build_core_index.sh +0 -16
@@ -0,0 +1,170 @@
1
+ module Yoda
2
+ module AST
3
+ # @abstract
4
+ class Vnode
5
+ include NamespaceTraversable
6
+ include MethodTraversable
7
+ include Traversable
8
+
9
+ # @return [Vnode, nil]
10
+ attr_reader :parent
11
+
12
+ # @return [Hash{Parser::AST::Node => Array<Parser::Source::Comment>}]
13
+ attr_reader :comments_by_node
14
+
15
+ # @param parent [Vnode, nil]
16
+ # @param comment_by_node [Hash{Parser::AST::Node => Array<Parser::Source::Comment>}]
17
+ def initialize(parent: nil, comments_by_node: {})
18
+ @parent = parent
19
+ @comments_by_node = comments_by_node
20
+ end
21
+
22
+ # @return [Array<Vnode>] all nodes between root and self.
23
+ def nesting
24
+ @nesting ||= (parent&.nesting || []) + [self]
25
+ end
26
+
27
+ # @param [Parser::AST::Node]
28
+ # @return [Vnode]
29
+ def wrap_child(child_node)
30
+ AST.wrap(child_node, parent: self, comments_by_node: comments_by_node)
31
+ end
32
+
33
+ # @return [Array<Vnode>]
34
+ def children
35
+ fail NotImplementedError
36
+ end
37
+
38
+ # @return [boolean]
39
+ def empty?
40
+ false
41
+ end
42
+
43
+ # @return [boolean]
44
+ def present?
45
+ !empty?
46
+ end
47
+
48
+ def constant?
49
+ false
50
+ end
51
+
52
+ # @return [Symbol]
53
+ def type
54
+ fail NotImplementedError
55
+ end
56
+
57
+ # @return [String]
58
+ def identifier
59
+ "#{type}:#{object_id}"
60
+ end
61
+
62
+ # @return [String]
63
+ def inspect
64
+ "(#{[identifier, inspect_content].join(' ')})"
65
+ end
66
+
67
+ # @return [String]
68
+ def inspect_content
69
+ children.map(&:inspect).join(' ')
70
+ end
71
+
72
+ def try(method_name, *args)
73
+ respond_to?(method_name) ? send(method_name, *args) : nil
74
+ end
75
+
76
+ module CommentAssociation
77
+ # @param comments [Array<::Parser::Source::Comment>]
78
+ # @return [Hash{Vnode => Array<Parser::Source::Comment>}]
79
+ def associate_comments(comments)
80
+ if node = try(:node)
81
+ Parser::Source::Comment.associate(node, comments).map do |ast_key_node, comments|
82
+ [all_nodes_lazy.find { |node| node.wrapping?(ast_key_node) }, comments]
83
+ end.to_h
84
+ else
85
+ {}
86
+ end
87
+ end
88
+
89
+ # @param node [::AST::Node]
90
+ # @return [boolean]
91
+ def wrapping?(node)
92
+ try(:node) && try(:node) == node
93
+ end
94
+
95
+ # @return [Enumerable<Vnode>]
96
+ def all_nodes_lazy
97
+ [self, *children].lazy.flat_map { |el| self == el ? self : el.all_nodes_lazy }
98
+ end
99
+
100
+ # @param comment_by_node [Array<Parser::Source::Comment>]
101
+ def comments
102
+ @comments ||= comments_by_node[node] || []
103
+ end
104
+ end
105
+ include CommentAssociation
106
+
107
+ module Positional
108
+ # @return [Parsing::Location, nil]
109
+ def location
110
+ source_map && Parsing::Location.of_ast_location(source_map)
111
+ end
112
+
113
+ # @return [Range, nil]
114
+ def range
115
+ source_map && Parsing::Range.of_ast_location(source_map)
116
+ end
117
+
118
+ # @return [Parser::Source::Map, nil]
119
+ def source_map
120
+ nil
121
+ end
122
+
123
+ # @param target [Parsing::Location, Parsing::Range, nil]
124
+ # @return [boolean]
125
+ def positionally_include?(target)
126
+ range && range.include?(target)
127
+ end
128
+
129
+ # @param target [Location, Range, nil]
130
+ # @return [Vnode]
131
+ def positionally_nearest_child(target)
132
+ if positionally_include?(target)
133
+ children.find { |child| child.positionally_include?(target) }&.positionally_nearest_child(target) || self
134
+ else
135
+ nil
136
+ end
137
+ end
138
+ end
139
+ include Positional
140
+
141
+ module CommentPositional
142
+ # @return [Hash{Vnode => CommentBlock}]
143
+ def comments_by_vnode
144
+ @comments_by_vnode ||= begin
145
+ node_map = all_nodes_lazy.map { |vnode| vnode.respond_to?(:node) ? [vnode.node, vnode] : nil }.to_a.compact.to_h
146
+ comments_by_node.map do |node, comments|
147
+ vnode = node_map[node]
148
+ vnode ? [vnode, CommentBlock.new(comments, node: vnode)] : nil
149
+ end.compact.to_h
150
+ end
151
+ end
152
+
153
+ # @param location [Location]
154
+ # @return [CommentBlock, nil]
155
+ def positionally_nearest_comment(location)
156
+ comments_by_vnode.find do |(node, comment_block)|
157
+ comment_block.range.include?(location)
158
+ end&.last
159
+ end
160
+
161
+ # @param location [Location]
162
+ # @return [Vnode, nil]
163
+ def positionally_nearest_commenting_node(location)
164
+ positionally_nearest_comment(location)&.node
165
+ end
166
+ end
167
+ include CommentPositional
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,15 @@
1
+ module Yoda
2
+ module AST
3
+ class WhenNode < Node
4
+ # @return [Array<Node>]
5
+ def matchers
6
+ children.slice(0..-2) || []
7
+ end
8
+
9
+ # @return [Node]
10
+ def body
11
+ children.last
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/yoda/ast.rb ADDED
@@ -0,0 +1,140 @@
1
+ module Yoda
2
+ module AST
3
+ require 'yoda/ast/traversable'
4
+ require 'yoda/ast/method_traversable'
5
+ require 'yoda/ast/namespace_traversable'
6
+ require 'yoda/ast/namespace'
7
+ require 'yoda/ast/vnode'
8
+ require 'yoda/ast/node'
9
+ require 'yoda/ast/comment_block'
10
+
11
+ require 'yoda/ast/array_node'
12
+ require 'yoda/ast/assignment_node'
13
+ require 'yoda/ast/block_call_node'
14
+ require 'yoda/ast/block_node'
15
+ require 'yoda/ast/case_node'
16
+ require 'yoda/ast/center_operator_node'
17
+ require 'yoda/ast/class_node'
18
+ require 'yoda/ast/conditional_loop_node'
19
+ require 'yoda/ast/constant_assignment_node'
20
+ require 'yoda/ast/constant_node'
21
+ require 'yoda/ast/def_node'
22
+ require 'yoda/ast/def_singleton_node'
23
+ require 'yoda/ast/empty_vnode'
24
+ require 'yoda/ast/ensure_node'
25
+ require 'yoda/ast/for_node'
26
+ require 'yoda/ast/hash_node'
27
+ require 'yoda/ast/if_node'
28
+ require 'yoda/ast/interpolation_text_node'
29
+ require 'yoda/ast/kwsplat_node'
30
+ require 'yoda/ast/left_operator_node'
31
+ require 'yoda/ast/literal_node'
32
+ require 'yoda/ast/module_node'
33
+ require 'yoda/ast/multiple_left_hand_side_node'
34
+ require 'yoda/ast/name_vnode'
35
+ require 'yoda/ast/optional_parameter_node'
36
+ require 'yoda/ast/pair_node'
37
+ require 'yoda/ast/parameter_node'
38
+ require 'yoda/ast/parameters_node'
39
+ require 'yoda/ast/rescue_node'
40
+ require 'yoda/ast/rescue_clause_node'
41
+ require 'yoda/ast/root_vnode'
42
+ require 'yoda/ast/send_node'
43
+ require 'yoda/ast/singleton_class_node'
44
+ require 'yoda/ast/special_call_node'
45
+ require 'yoda/ast/value_vnode'
46
+ require 'yoda/ast/variable_node'
47
+ require 'yoda/ast/when_node'
48
+
49
+ class << self
50
+ # @param node [::AST::Node]
51
+ # @param parent [Vnode]
52
+ # @param comment_by_node [Hash{Parser::AST::Node => Array<Parser::Source::Comment>}]
53
+ # @return [Yoda::AST::Node]
54
+ def wrap(node, parent: nil, comments_by_node: {})
55
+ if parent
56
+ wrapper_class_for(node).new(node, parent: parent, comments_by_node: comments_by_node)
57
+ else
58
+ RootVnode.new(node, comments_by_node: comments_by_node).content
59
+ end
60
+ end
61
+
62
+ # @param node [::AST::Node]
63
+ def wrapper_class_for(node)
64
+ return EmptyVnode unless node
65
+ return NameVnode if node.is_a?(Symbol)
66
+ return ValueVnode unless node.respond_to?(:type)
67
+ case node.type
68
+ when :lvasgn, :ivasgn, :cvasgn, :gvasgn, :masgn, :op_asgn, :or_asgn, :and_asgn
69
+ AssignmentNode
70
+ when :casgn
71
+ ConstantAssignmentNode
72
+ when :not
73
+ LeftOperatorNode
74
+ when :and, :or
75
+ CenterOperatorNode
76
+ when :if
77
+ IfNode
78
+ when :case
79
+ CaseNode
80
+ when :const
81
+ ConstantNode
82
+ when :while, :until, :while_post, :until_post
83
+ ConditionalLoopNode
84
+ when :for
85
+ ForNode
86
+ when :super, :zsuper, :yield, :return, :break, :next, :self, :defined?, :redo, :undef
87
+ SpecialCallNode
88
+ when :ensure
89
+ EnsureNode
90
+ when :rescue
91
+ RescueNode
92
+ when :resbody
93
+ RescueClauseNode
94
+ when :csend, :send
95
+ SendNode
96
+ when :block
97
+ BlockCallNode
98
+ when :const
99
+ ConstantNode
100
+ when :lvar, :cvar, :ivar, :gvar
101
+ VariableNode
102
+ when :begin, :kwbegin, :block
103
+ BlockNode
104
+ when :dstr, :dsym, :xstr
105
+ InterpolationTextNode
106
+ when :args
107
+ ParametersNode
108
+ when :arg, :shadowarg, :restarg, :blockarg, :kwarg, :kwrestarg
109
+ ParameterNode
110
+ when :optarg, :kwoptarg
111
+ OptionalParameterNode
112
+ when :mlhs
113
+ MultipleLeftHandSideNode
114
+ when :def
115
+ DefNode
116
+ when :defs
117
+ DefSingletonNode
118
+ when :hash
119
+ HashNode
120
+ when :array
121
+ ArrayNode
122
+ when :pair
123
+ PairNode
124
+ when :module
125
+ ModuleNode
126
+ when :class
127
+ ClassNode
128
+ when :sclass
129
+ SingletonClassNode
130
+ when :kwsplat
131
+ KwsplatNode
132
+ when :int, :float, :complex, :rational, :str, :string, :sym
133
+ LiteralNode
134
+ else
135
+ Node
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,102 @@
1
+ module Yoda
2
+ module Cli
3
+ class AnalyzeDeps
4
+ # @param root_path [String]
5
+ def self.run(root_path)
6
+ require 'json'
7
+ require 'bundler'
8
+
9
+ builder = Builder.new(root_path)
10
+ puts JSON.dump(builder)
11
+ end
12
+
13
+ class Builder
14
+ # @return [String]
15
+ attr_reader :root_path
16
+
17
+ # @param root_path [String]
18
+ def initialize(root_path)
19
+ @root_path = root_path
20
+ end
21
+
22
+ # @return [Array<Yoda::Store::Objects::Library::Gem>]
23
+ def gems
24
+ @gems ||= dependency_specs.map { |spec| Yoda::Store::Objects::Library::Gem.from_gem_spec(spec) }
25
+ end
26
+
27
+ def to_h
28
+ {
29
+ path: root_path,
30
+ dependencies: gems.map(&:to_h),
31
+ }
32
+ end
33
+
34
+ def to_json(*)
35
+ to_h.to_json
36
+ end
37
+
38
+ private
39
+
40
+ # @return [Array<Bundler::StubSpecification>]
41
+ def dependency_specs
42
+ @dependency_specs ||= begin
43
+ (gem_specs || [])
44
+ .reject { |spec| self_spec?(spec) || metadata?(spec) }
45
+ end
46
+ end
47
+
48
+ # @return [Array<Bundler::LazySpecification>, nil]
49
+ def gem_specs
50
+ @gem_specs ||= begin
51
+ Dir.chdir(root_path) do
52
+ with_unbundled_env do
53
+ # Suppress bundler outputs to stdout.
54
+ Bundler.ui = Bundler::UI::Silent.new
55
+
56
+ Bundler.reset!
57
+
58
+ if File.exists?("Gemfile")
59
+ # Resolve dependencies of uninstalled gems and ensure remote sources are available.
60
+ Bundler.definition.resolve_remotely!
61
+ spec_set = Bundler.definition.resolve
62
+
63
+ if Gem::Version.new(Bundler::VERSION) >= Gem::Version.new('2.2.25')
64
+ deps = Bundler.definition.requested_dependencies
65
+ spec_set.materialize(deps).to_a
66
+ else
67
+ # For backward compatibility (Ref: https://github.com/rubygems/rubygems/pull/4788)
68
+ deps = Bundler.definition.send(:requested_dependencies)
69
+ missing = []
70
+ materialized = spec_set.materialize(deps, missing)
71
+ materialized.to_a + missing
72
+ end
73
+ else
74
+ []
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ # @param [Gem::Specification]
82
+ def metadata?(spec)
83
+ spec.source.is_a?(Bundler::Source::Metadata)
84
+ end
85
+
86
+ # @param [Gem::Specification]
87
+ def self_spec?(spec)
88
+ spec.source.is_a?(Bundler::Source::Path) && (File.expand_path(spec.source.path) == File.expand_path(root_path))
89
+ end
90
+
91
+ def with_unbundled_env(&block)
92
+ if Bundler.respond_to?(:with_unbundled_env)
93
+ Bundler.with_unbundled_env(&block)
94
+ else
95
+ # For backward compatibility (This method is introduced at bundler 2.1.0: https://github.com/rubygems/bundler/pull/6843)
96
+ Bundler.with_clean_env(&block)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -1,5 +1,5 @@
1
1
  module Yoda
2
- module Commands
2
+ module Cli
3
3
  # @abstract
4
4
  class Base
5
5
  def self.run(*args)
@@ -1,5 +1,5 @@
1
1
  module Yoda
2
- module Commands
2
+ module Cli
3
3
  class Complete < Base
4
4
  include FileCursorParsable
5
5
 
@@ -11,25 +11,28 @@ module Yoda
11
11
  end
12
12
 
13
13
  def run
14
- project.build_cache
15
- puts create_signature_help(worker.current_node_signature)
14
+ project.setup
15
+ puts create_completions(worker.candidates)
16
16
  end
17
17
 
18
18
  private
19
19
 
20
- # @param signature [Model::NodeSignature, nil]
20
+ # @param completion_item [Model::CompletionItem]
21
21
  # @return [String, nil]
22
- def create_signature_help(signature)
23
- return nil unless signature
24
- signature.descriptions.map(&:title).join("\n")
22
+ def create_completions(completion_item)
23
+ completion_item.join("\n")
25
24
  end
26
25
 
27
26
  def worker
28
- @worker ||= Evaluation::CurrentNodeExplain.new(project.registry, File.read(filename), position)
27
+ @worker ||= Services::CodeCompletion.new(project.environment, source, position)
29
28
  end
30
29
 
31
30
  def project
32
- @project ||= Store::Project.new(Dir.pwd)
31
+ @project ||= Store::Project.for_path(Dir.pwd)
32
+ end
33
+
34
+ def source
35
+ Parsing.fix_parse_error(source: File.read(filename), location: position)
33
36
  end
34
37
  end
35
38
  end
@@ -0,0 +1,17 @@
1
+ module Yoda
2
+ module Cli
3
+ class Console < Base
4
+ def run
5
+ require "pry"
6
+ project.setup
7
+ project.pry
8
+ end
9
+
10
+ private
11
+
12
+ def project
13
+ @project ||= Store::Project.for_path(Dir.pwd)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,5 +1,5 @@
1
1
  module Yoda
2
- module Commands
2
+ module Cli
3
3
  # Provide parsing methods of positon representation with the format `path/to/file:line_num:character_num`
4
4
  module FileCursorParsable
5
5
  private
@@ -1,5 +1,5 @@
1
1
  module Yoda
2
- module Commands
2
+ module Cli
3
3
  class Infer < Base
4
4
  attr_reader :filename_with_position
5
5
 
@@ -9,7 +9,7 @@ module Yoda
9
9
  end
10
10
 
11
11
  def run
12
- project.build_cache
12
+ project.setup
13
13
  puts create_signature_help(worker.current_node_signature)
14
14
  end
15
15
 
@@ -23,11 +23,11 @@ module Yoda
23
23
  end
24
24
 
25
25
  def worker
26
- @worker ||= Evaluation::CurrentNodeExplain.new(project.registry, File.read(filename), position)
26
+ @worker ||= Services::CurrentNodeExplain.from_source(registry: project.registry, source: File.read(filename), location: position)
27
27
  end
28
28
 
29
29
  def project
30
- @project ||= Store::Project.new(Dir.pwd)
30
+ @project ||= Store::Project.for_path(Dir.pwd)
31
31
  end
32
32
 
33
33
  def filename
data/lib/yoda/cli.rb ADDED
@@ -0,0 +1,117 @@
1
+ require 'thor'
2
+
3
+ module Yoda
4
+ # Cli module has handler for each cli command.
5
+ module Cli
6
+ require 'yoda/cli/analyze_deps'
7
+ require 'yoda/cli/base'
8
+ require 'yoda/cli/file_cursor_parsable'
9
+ require 'yoda/cli/infer'
10
+ require 'yoda/cli/complete'
11
+ require 'yoda/cli/console'
12
+
13
+ class << self
14
+ # @return [String]
15
+ def exe_dir
16
+ File.expand_path('../../exe', __dir__)
17
+ end
18
+
19
+ # @return [String]
20
+ def yoda_exe
21
+ File.expand_path('./yoda', exe_dir)
22
+ end
23
+ end
24
+
25
+ class Top < Thor
26
+ class_option :log_level, type: :string, desc: 'Set log level (debug info warn error fatal)'
27
+ class_option :profile, type: :boolean, desc: 'Set log level (debug info warn error fatal)'
28
+ class_option :help, type: :boolean, desc: 'Show help', aliases: :h, hide: true
29
+
30
+ desc 'setup', 'Setup indexes for current Ruby version and project gems'
31
+ option :force_build, type: :boolean, desc: "If enabled, (re)build current project's index forcibly"
32
+ def setup
33
+ process_class_options
34
+ return self.class.command_help(shell, __method__) if options[:help]
35
+ Instrument.instance.hear(initialization_progress: method(:on_progress), registry_dump: method(:on_progress)) do
36
+ Store.setup(dir: Dir.pwd, force_build: options[:force_build])
37
+ end
38
+ end
39
+
40
+ desc 'infer POSITION', 'Infer the type of value at the specified position'
41
+ def infer(position)
42
+ process_class_options
43
+ return self.class.command_help(shell, __method__) if options[:help]
44
+ Cli::Infer.run(position)
45
+ end
46
+
47
+ desc 'complete POSITION', 'Provide completion candidates for the specified position'
48
+ def complete(position)
49
+ process_class_options
50
+ return self.class.command_help(shell, __method__) if options[:help]
51
+ Cli::Complete.run(position)
52
+ end
53
+
54
+ desc 'console', 'Launch debug console'
55
+ def console
56
+ process_class_options
57
+ return self.class.command_help(shell, __method__) if options[:help]
58
+ Cli::Console.run
59
+ end
60
+
61
+ desc 'server', 'Start Language Server'
62
+ def server
63
+ process_class_options
64
+ return self.class.command_help(shell, __method__) if options[:help]
65
+ Server.new.run
66
+ end
67
+
68
+ desc 'analyze-deps PATH', 'Analyze dependencies of the given path'
69
+ def analyze_deps(path)
70
+ process_class_options
71
+ return self.class.command_help(shell, __method__) if options[:help]
72
+ Cli::AnalyzeDeps.run(path)
73
+ end
74
+
75
+ desc 'version', 'show current version'
76
+ def version
77
+ process_class_options
78
+ return self.class.command_help(shell, __method__) if options[:help]
79
+ say "#{Yoda::VERSION}"
80
+ end
81
+
82
+ private
83
+
84
+ def process_class_options
85
+ set_log_level
86
+ use_profiler_if_enabled
87
+ end
88
+
89
+ def set_log_level
90
+ Yoda::Logger.log_level = options[:log_level].to_sym if options[:log_level]
91
+ end
92
+
93
+ def use_profiler_if_enabled
94
+ if options[:profile]
95
+ require 'stackprof'
96
+ require 'securerandom'
97
+ Logger.info('Enabled profiler')
98
+ StackProf.start(mode: :wall, raw: true)
99
+
100
+ at_exit do
101
+ StackProf.stop
102
+ tmpfile_path = File.expand_path(SecureRandom.hex(12), Dir.tmpdir)
103
+ StackProf.results(tmpfile_path)
104
+ Logger.fatal("Dumped file to: #{tmpfile_path}")
105
+ end
106
+ end
107
+ end
108
+
109
+ def on_progress(phase: :save_keys, index: nil, length: nil, **params)
110
+ return unless index
111
+ @bars ||= {}
112
+ @bar = @bars[phase] ||= ProgressBar.create(format: "%t: %c/%C |%w>%i| %e ", title: phase.to_s.gsub('_', ' '), starting_at: index, total: length)
113
+ @bar.progress = index if index <= @bar.total
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,40 @@
1
+ module Yoda
2
+ module HasServices
3
+ module ClassMethods
4
+ # Register a service with the specified name
5
+ # @params name [String]
6
+ def service(name, &block)
7
+ service_hub_class.register_service(name, &block)
8
+ define_method(:"#{name}_service") { services.public_send(name) }
9
+ end
10
+
11
+ # @return [Class<ServiceHub>]
12
+ def service_hub_class
13
+ @service_hub_class ||= Class.new(ServiceHub)
14
+ end
15
+ end
16
+
17
+ class ServiceHub
18
+ attr_reader :instance
19
+
20
+ def initialize(instance)
21
+ @instance = instance
22
+ end
23
+
24
+ def self.register_service(name, &block)
25
+ define_method(name) do
26
+ instance.instance_exec(instance, &block)
27
+ end
28
+ end
29
+ end
30
+
31
+ # @return [ServiceHub]
32
+ def services
33
+ @services ||= self.class.service_hub_class.new(self)
34
+ end
35
+
36
+ def self.included(klass)
37
+ klass.extend ClassMethods
38
+ end
39
+ end
40
+ end