yoda-language-server 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,39 @@
1
+ {
2
+ "name": "yoda",
3
+ "displayName": "yoda",
4
+ "description": "Static analytics tool for Ruby",
5
+ "version": "0.0.1",
6
+ "publisher": "nemunemu",
7
+ "engines": {
8
+ "vscode": "^1.19.0"
9
+ },
10
+ "categories": [
11
+ "Other"
12
+ ],
13
+ "activationEvents": [
14
+ "onLanguage:ruby"
15
+ ],
16
+ "main": "./out/extension",
17
+ "contributes": {
18
+ "commands": [
19
+ {
20
+ "command": "extension.sayHello",
21
+ "title": "Hello World"
22
+ }
23
+ ]
24
+ },
25
+ "scripts": {
26
+ "vscode:prepublish": "npm run compile",
27
+ "compile": "tsc -p ./",
28
+ "watch": "tsc -watch -p ./",
29
+ "postinstall": "node ./node_modules/vscode/bin/install",
30
+ "test": "npm run compile && node ./node_modules/vscode/bin/test"
31
+ },
32
+ "devDependencies": {
33
+ "typescript": "^2.6.1",
34
+ "vscode": "^1.1.6",
35
+ "vscode-languageclient": "^3.5.0",
36
+ "@types/node": "^7.0.43",
37
+ "@types/mocha": "^2.2.42"
38
+ }
39
+ }
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ import * as path from 'path';
4
+
5
+ // The module 'vscode' contains the VS Code extensibility API
6
+ // Import the module and reference it with the alias vscode in your code below
7
+ import * as vscode from 'vscode';
8
+ import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient';
9
+ import { worker } from 'cluster';
10
+ import { workspace } from 'vscode';
11
+
12
+ // this method is called when your extension is activated
13
+ // your extension is activated the very first time the command is executed
14
+ export function activate(context: vscode.ExtensionContext) {
15
+ // Use the console to output diagnostic information (console.log) and errors (console.error)
16
+ // This line of code will only be executed once when your extension is activated
17
+ console.log('Congratulations, your extension "yoda" is now active!');
18
+
19
+ let execOptions = {
20
+ command: path.resolve(__dirname, '../../../exe/yoda'),
21
+ args: ['server'],
22
+ }
23
+
24
+ let serverOptions : ServerOptions = {
25
+ run: execOptions,
26
+ debug: execOptions,
27
+ }
28
+
29
+ let clientOptions : LanguageClientOptions = {
30
+ documentSelector: [{ scheme: 'file', language: 'ruby' }],
31
+ synchronize: {
32
+ configurationSection: 'yoda',
33
+ fileEvents: workspace.createFileSystemWatcher('**/.rb'),
34
+ }
35
+ }
36
+
37
+ let disposable = new LanguageClient('yoda', 'Yoda Language Server', serverOptions, clientOptions).start();
38
+ }
39
+
40
+ // this method is called when your extension is deactivated
41
+ export function deactivate() {
42
+ }
@@ -0,0 +1,22 @@
1
+ //
2
+ // Note: This example test is leveraging the Mocha test framework.
3
+ // Please refer to their documentation on https://mochajs.org/ for help.
4
+ //
5
+
6
+ // The module 'assert' provides assertion methods from node
7
+ import * as assert from 'assert';
8
+
9
+ // You can import and use all API from the 'vscode' module
10
+ // as well as import your extension to test it
11
+ import * as vscode from 'vscode';
12
+ import * as myExtension from '../extension';
13
+
14
+ // Defines a Mocha test suite to group tests of similar kind together
15
+ suite("Extension Tests", () => {
16
+
17
+ // Defines a Mocha unit test
18
+ test("Something 1", () => {
19
+ assert.equal(-1, [1, 2, 3].indexOf(5));
20
+ assert.equal(-1, [1, 2, 3].indexOf(0));
21
+ });
22
+ });
@@ -0,0 +1,22 @@
1
+ //
2
+ // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
3
+ //
4
+ // This file is providing the test runner to use when running extension tests.
5
+ // By default the test runner in use is Mocha based.
6
+ //
7
+ // You can provide your own test runner if you want to override it by exporting
8
+ // a function run(testRoot: string, clb: (error:Error) => void) that the extension
9
+ // host can call to run the tests. The test runner is expected to use console.log
10
+ // to report the results back to the caller. When the tests are finished, return
11
+ // a possible error to the callback or null if none.
12
+
13
+ import * as testRunner from 'vscode/lib/testrunner';
14
+
15
+ // You can directly control Mocha options by uncommenting the following lines
16
+ // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
17
+ testRunner.configure({
18
+ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
19
+ useColors: true // colored output from test results
20
+ });
21
+
22
+ module.exports = testRunner;
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "target": "es6",
5
+ "outDir": "out",
6
+ "lib": [
7
+ "es6"
8
+ ],
9
+ "sourceMap": true,
10
+ "rootDir": "src"
11
+ },
12
+ "exclude": [
13
+ "node_modules",
14
+ ".vscode-test"
15
+ ]
16
+ }
@@ -0,0 +1,33 @@
1
+ # Welcome to your VS Code Extension
2
+
3
+ ## What's in the folder
4
+ * This folder contains all of the files necessary for your extension.
5
+ * `package.json` - this is the manifest file in which you declare your extension and command.
6
+ The sample plugin registers a command and defines its title and command name. With this information
7
+ VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
8
+ * `src/extension.ts` - this is the main file where you will provide the implementation of your command.
9
+ The file exports one function, `activate`, which is called the very first time your extension is
10
+ activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
11
+ We pass the function containing the implementation of the command as the second parameter to
12
+ `registerCommand`.
13
+
14
+ ## Get up and running straight away
15
+ * Press `F5` to open a new window with your extension loaded.
16
+ * Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
17
+ * Set breakpoints in your code inside `src/extension.ts` to debug your extension.
18
+ * Find output from your extension in the debug console.
19
+
20
+ ## Make changes
21
+ * You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
22
+ * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
23
+
24
+ ## Explore the API
25
+ * You can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts`.
26
+
27
+ ## Run tests
28
+ * Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests`.
29
+ * Press `F5` to run the tests in a new window with your extension loaded.
30
+ * See the output of the test result in the debug console.
31
+ * Make changes to `test/extension.test.ts` or create new test files inside the `test` folder.
32
+ * By convention, the test runner will only consider files matching the name pattern `**.test.ts`.
33
+ * You can create folders inside the `test` folder to structure your tests any way you want.
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if Dir.exist?(File.join(__dir__, "..", ".git"))
4
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
5
+ end
6
+
7
+ require 'yoda'
8
+ require 'optparse'
9
+
10
+ opt = OptionParser.new("Usage: yoda [options]") do |opt|
11
+ end
12
+
13
+ argv = opt.parse(ARGV)
14
+
15
+ case argv.first
16
+ when 'setup'
17
+ require 'pry'
18
+ Pry::rescue { Yoda::Runner::Setup.run(argv[1]) }
19
+ when 'infer'
20
+ require 'pry'
21
+ Pry::rescue { Yoda::Runner::Infer.run(argv[1]) }
22
+ when 'server'
23
+ Yoda::Server.new.run
24
+ else
25
+ puts opt.help
26
+ exit 1
27
+ end
@@ -0,0 +1,11 @@
1
+ module Yoda
2
+ require "yoda/version"
3
+ require "yoda/evaluation"
4
+ require "yoda/model"
5
+ require "yoda/store"
6
+ require "yoda/server"
7
+ require "yoda/parsing"
8
+ require "yoda/typing"
9
+ require "yoda/runner"
10
+ require "yoda/yard_extensions"
11
+ end
@@ -0,0 +1,9 @@
1
+ module Yoda
2
+ module Evaluation
3
+ require 'yoda/evaluation/evaluator'
4
+ require 'yoda/evaluation/current_node_explain'
5
+ require 'yoda/evaluation/comment_completion'
6
+ require 'yoda/evaluation/code_completion'
7
+ require 'yoda/evaluation/signature_discovery'
8
+ end
9
+ end
@@ -0,0 +1,65 @@
1
+ module Yoda
2
+ module Evaluation
3
+ class CodeCompletion
4
+ require 'yoda/evaluation/code_completion/base_provider'
5
+ require 'yoda/evaluation/code_completion/method_provider'
6
+ require 'yoda/evaluation/code_completion/variable_provider'
7
+ require 'yoda/evaluation/code_completion/const_provider'
8
+
9
+ # @return [Store::Registry]
10
+ attr_reader :registry
11
+
12
+ # @return [String]
13
+ attr_reader :source
14
+
15
+ # @return [Parsing::Location]
16
+ attr_reader :location
17
+
18
+ # @param registry [Store::Registry]
19
+ # @param source [String]
20
+ # @param location [Parsing::Location]
21
+ def initialize(registry, source, location)
22
+ @registry = registry
23
+ @source = source
24
+ @location = location
25
+ end
26
+
27
+ # @return [true, false]
28
+ def valid?
29
+ providers.any?(&:providable?)
30
+ end
31
+
32
+ # @return [Array<Model::CompletionItem>]
33
+ def candidates
34
+ providers.select(&:providable?).map(&:candidates).flatten
35
+ end
36
+
37
+ private
38
+
39
+ # @return [Array<CodeCompletion::BaseProvider>]
40
+ def providers
41
+ [method_provider, variable_provider, const_provider]
42
+ end
43
+
44
+ # @return [Parsing::SourceAnalyzer]
45
+ def source_analyzer
46
+ @source_analyzer ||= Parsing::SourceAnalyzer.from_source(source, location)
47
+ end
48
+
49
+ # @return [MethodProvider]
50
+ def method_provider
51
+ @method_provider ||= MethodProvider.new(registry, source_analyzer)
52
+ end
53
+
54
+ # @return [VariableProvider]
55
+ def variable_provider
56
+ @variable_provider ||= VariableProvider.new(registry, source_analyzer)
57
+ end
58
+
59
+ # @return [ConstantProvider]
60
+ def const_provider
61
+ @constant_provider ||= ConstProvider.new(registry, source_analyzer)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,57 @@
1
+ module Yoda
2
+ module Evaluation
3
+ class CodeCompletion
4
+ # @abstract
5
+ # Base class of completion candidates providers for code completion.
6
+ # This class bridges analysis features such as syntastic analysis {#analyzer} and symbolic execiton {#evaluator}.
7
+ class BaseProvider
8
+ # @return [Store::Registry]
9
+ attr_reader :registry
10
+
11
+ # @return [Parsing::SourceAnalyzer]
12
+ attr_reader :source_analyzer
13
+
14
+ # @param registry [Store::Registry]
15
+ # @param source_analyzer [Parsing::SourceAnalyzer]
16
+ def initialize(registry, source_analyzer)
17
+ @registry = registry
18
+ @source_analyzer = source_analyzer
19
+ end
20
+
21
+ # @abstract
22
+ # @return [true, false]
23
+ def providable?
24
+ fail NotImplementedError
25
+ end
26
+
27
+ # @abstract
28
+ # @return [Array<Model::CompletionItem>]
29
+ def candidates
30
+ fail NotImplementedError
31
+ end
32
+
33
+ private
34
+
35
+ # @return [SourceAnalyzer]
36
+ def analyzer
37
+ @analyzer ||= Parsing::SourceAnalyzer.from_source(source, location)
38
+ end
39
+
40
+ # @return [Evaluator]
41
+ def evaluator
42
+ @evaluator ||= Evaluator.from_ast(registry, source_analyzer.ast, location)
43
+ end
44
+
45
+ # @return [::Parser::AST::Node]
46
+ def ast
47
+ source_analyzer.ast
48
+ end
49
+
50
+ # @return [Parsing::Location]
51
+ def location
52
+ source_analyzer.location
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,90 @@
1
+ module Yoda
2
+ module Evaluation
3
+ class CodeCompletion
4
+ class ConstProvider < BaseProvider
5
+ # @return [true, false]
6
+ def providable?
7
+ !!current_ancestor_const_node
8
+ end
9
+
10
+ # Returns constant candidates by using the current lexical scope.
11
+ # @return [Array<Model::CompletionItem>] constant candidates.
12
+ def candidates
13
+ const_candidates.map do |const_candidate|
14
+ Model::CompletionItem.new(
15
+ description: Model::Descriptions::ValueDescription.new(const_candidate),
16
+ range: substitution_range,
17
+ kind: complete_item_kind(const_candidate),
18
+ prefix: just_after_separator? ? '::' : '',
19
+ )
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ # @param object [Store::Objects::Base]
26
+ # @return [Symbol]
27
+ def complete_item_kind(object)
28
+ case object.kind
29
+ when :class
30
+ :class
31
+ when :module
32
+ :module
33
+ else
34
+ :constant
35
+ end
36
+ end
37
+
38
+ # @return [Range, nil]
39
+ def substitution_range
40
+ return nil unless providable?
41
+ @substitution_range ||=
42
+ if just_after_separator?
43
+ Parsing::Range.of_ast_location(current_ancestor_const_node.node.location.double_colon)
44
+ else
45
+ Parsing::Range.of_ast_location(current_ancestor_const_node.node.location.name)
46
+ end
47
+ end
48
+
49
+ # @return [Parsing::NodeObjects::ConstNode, nil]
50
+ def current_ancestor_const_node
51
+ @current_ancestor_const_node ||= begin
52
+ node = source_analyzer.nodes_to_current_location_from_root.reverse.take_while { |el| [:const, :cbase].include?(el.type) }.last
53
+ return nil if !node || node.type != :const
54
+ Parsing::NodeObjects::ConstNode.new(node)
55
+ end
56
+ end
57
+
58
+ # @return [Array<Store::Objects::Base>]
59
+ def const_candidates
60
+ return [] unless providable?
61
+ return [] if const_parent_paths.empty?
62
+
63
+ base_path = current_ancestor_const_node.to_path
64
+ path = just_after_separator? ? Model::Path.from_names([base_path.spacename, '']) : base_path
65
+ scoped_path = Model::ScopedPath.new(const_parent_paths, path)
66
+ Store::Query::FindConstant.new(registry).select_with_prefix(scoped_path)
67
+ end
68
+
69
+ # @return [true, false]
70
+ def just_after_separator?
71
+ return @is_just_after_separator if instance_variable_defined?(:@is_just_after_separator)
72
+ @is_just_after_separator = current_ancestor_const_node.just_after_separator?(source_analyzer.location)
73
+ end
74
+
75
+ # @return [Array<Store::Objects::Base>]
76
+ def const_parent_paths
77
+ @const_parent_paths ||= begin
78
+ lexical_scope(source_analyzer.current_namespace)
79
+ end
80
+ end
81
+
82
+ # @param namespace [Parsing::NodeObjects::Namespace]
83
+ # @return [Array<Path>]
84
+ def lexical_scope(namespace)
85
+ namespace.paths_from_root.reverse.map { |name| Model::Path.build(name.empty? ? 'Object' : name.gsub(/\A::/, '')) }
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end