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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +3 -0
- data/.travis.yml +8 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +78 -0
- data/LICENSE.txt +21 -0
- data/README.md +85 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/client/atom/main.js +27 -0
- data/client/vscode/.gitignore +4 -0
- data/client/vscode/.vscode/launch.json +28 -0
- data/client/vscode/.vscode/settings.json +9 -0
- data/client/vscode/.vscode/tasks.json +20 -0
- data/client/vscode/.vscodeignore +8 -0
- data/client/vscode/CHANGELOG.md +7 -0
- data/client/vscode/README.md +65 -0
- data/client/vscode/package-lock.json +2688 -0
- data/client/vscode/package.json +39 -0
- data/client/vscode/src/extension.ts +42 -0
- data/client/vscode/src/test/extension.test.ts +22 -0
- data/client/vscode/src/test/index.ts +22 -0
- data/client/vscode/tsconfig.json +16 -0
- data/client/vscode/vsc-extension-quickstart.md +33 -0
- data/exe/yoda +27 -0
- data/lib/yoda.rb +11 -0
- data/lib/yoda/evaluation.rb +9 -0
- data/lib/yoda/evaluation/code_completion.rb +65 -0
- data/lib/yoda/evaluation/code_completion/base_provider.rb +57 -0
- data/lib/yoda/evaluation/code_completion/const_provider.rb +90 -0
- data/lib/yoda/evaluation/code_completion/method_provider.rb +82 -0
- data/lib/yoda/evaluation/code_completion/variable_provider.rb +18 -0
- data/lib/yoda/evaluation/comment_completion.rb +70 -0
- data/lib/yoda/evaluation/comment_completion/base_provider.rb +64 -0
- data/lib/yoda/evaluation/comment_completion/param_provider.rb +18 -0
- data/lib/yoda/evaluation/comment_completion/tag_provider.rb +41 -0
- data/lib/yoda/evaluation/comment_completion/type_provider.rb +58 -0
- data/lib/yoda/evaluation/current_node_explain.rb +70 -0
- data/lib/yoda/evaluation/evaluator.rb +103 -0
- data/lib/yoda/evaluation/signature_discovery.rb +83 -0
- data/lib/yoda/model.rb +12 -0
- data/lib/yoda/model/completion_item.rb +56 -0
- data/lib/yoda/model/descriptions.rb +10 -0
- data/lib/yoda/model/descriptions/base.rb +26 -0
- data/lib/yoda/model/descriptions/function_description.rb +40 -0
- data/lib/yoda/model/descriptions/value_description.rb +33 -0
- data/lib/yoda/model/descriptions/word_description.rb +32 -0
- data/lib/yoda/model/function_signatures.rb +13 -0
- data/lib/yoda/model/function_signatures/base.rb +68 -0
- data/lib/yoda/model/function_signatures/constructor.rb +70 -0
- data/lib/yoda/model/function_signatures/formatter.rb +82 -0
- data/lib/yoda/model/function_signatures/method.rb +67 -0
- data/lib/yoda/model/function_signatures/overload.rb +79 -0
- data/lib/yoda/model/function_signatures/parameter_list.rb +108 -0
- data/lib/yoda/model/function_signatures/type_builder.rb +101 -0
- data/lib/yoda/model/node_signature.rb +28 -0
- data/lib/yoda/model/path.rb +96 -0
- data/lib/yoda/model/scoped_path.rb +44 -0
- data/lib/yoda/model/types.rb +84 -0
- data/lib/yoda/model/types/any_type.rb +32 -0
- data/lib/yoda/model/types/base.rb +37 -0
- data/lib/yoda/model/types/duck_type.rb +41 -0
- data/lib/yoda/model/types/function_type.rb +174 -0
- data/lib/yoda/model/types/generic_type.rb +66 -0
- data/lib/yoda/model/types/instance_type.rb +42 -0
- data/lib/yoda/model/types/module_type.rb +42 -0
- data/lib/yoda/model/types/sequence_type.rb +53 -0
- data/lib/yoda/model/types/union_type.rb +56 -0
- data/lib/yoda/model/types/unknown_type.rb +40 -0
- data/lib/yoda/model/types/value_type.rb +58 -0
- data/lib/yoda/model/values.rb +9 -0
- data/lib/yoda/model/values/base.rb +32 -0
- data/lib/yoda/model/values/instance_value.rb +65 -0
- data/lib/yoda/model/values/module_value.rb +72 -0
- data/lib/yoda/parsing.rb +15 -0
- data/lib/yoda/parsing/ast_traversable.rb +18 -0
- data/lib/yoda/parsing/comment_tokenizer.rb +59 -0
- data/lib/yoda/parsing/location.rb +101 -0
- data/lib/yoda/parsing/node_objects.rb +10 -0
- data/lib/yoda/parsing/node_objects/const_node.rb +52 -0
- data/lib/yoda/parsing/node_objects/method_definition.rb +46 -0
- data/lib/yoda/parsing/node_objects/namespace.rb +104 -0
- data/lib/yoda/parsing/node_objects/send_node.rb +72 -0
- data/lib/yoda/parsing/parser.rb +27 -0
- data/lib/yoda/parsing/query.rb +11 -0
- data/lib/yoda/parsing/query/current_comment_query.rb +80 -0
- data/lib/yoda/parsing/query/current_comment_token_query.rb +153 -0
- data/lib/yoda/parsing/query/current_commenting_node_query.rb +68 -0
- data/lib/yoda/parsing/query/current_location_node_query.rb +51 -0
- data/lib/yoda/parsing/query/current_node_comment_query.rb +40 -0
- data/lib/yoda/parsing/range.rb +41 -0
- data/lib/yoda/parsing/scopes.rb +15 -0
- data/lib/yoda/parsing/scopes/base.rb +78 -0
- data/lib/yoda/parsing/scopes/builder.rb +60 -0
- data/lib/yoda/parsing/scopes/class_definition.rb +47 -0
- data/lib/yoda/parsing/scopes/meta_class_definition.rb +44 -0
- data/lib/yoda/parsing/scopes/meta_method_definition.rb +70 -0
- data/lib/yoda/parsing/scopes/method_definition.rb +69 -0
- data/lib/yoda/parsing/scopes/module_definition.rb +36 -0
- data/lib/yoda/parsing/scopes/root.rb +25 -0
- data/lib/yoda/parsing/source_analyzer.rb +59 -0
- data/lib/yoda/parsing/source_cutter.rb +231 -0
- data/lib/yoda/parsing/type_parser.rb +141 -0
- data/lib/yoda/runner.rb +6 -0
- data/lib/yoda/runner/infer.rb +50 -0
- data/lib/yoda/runner/setup.rb +26 -0
- data/lib/yoda/server.rb +191 -0
- data/lib/yoda/server/client_info.rb +98 -0
- data/lib/yoda/server/completion_provider.rb +78 -0
- data/lib/yoda/server/definition_provider.rb +36 -0
- data/lib/yoda/server/deserializer.rb +27 -0
- data/lib/yoda/server/hover_provider.rb +38 -0
- data/lib/yoda/server/signature_provider.rb +46 -0
- data/lib/yoda/store.rb +13 -0
- data/lib/yoda/store/actions.rb +10 -0
- data/lib/yoda/store/actions/import_core_library.rb +30 -0
- data/lib/yoda/store/actions/import_gems.rb +91 -0
- data/lib/yoda/store/actions/read_file.rb +36 -0
- data/lib/yoda/store/actions/read_project_files.rb +29 -0
- data/lib/yoda/store/adapters.rb +14 -0
- data/lib/yoda/store/adapters/base.rb +58 -0
- data/lib/yoda/store/adapters/leveldb_adapter.rb +80 -0
- data/lib/yoda/store/adapters/lmdb_adapter.rb +113 -0
- data/lib/yoda/store/objects.rb +46 -0
- data/lib/yoda/store/objects/addressable.rb +25 -0
- data/lib/yoda/store/objects/base.rb +116 -0
- data/lib/yoda/store/objects/class_object.rb +51 -0
- data/lib/yoda/store/objects/merger.rb +94 -0
- data/lib/yoda/store/objects/meta_class_object.rb +41 -0
- data/lib/yoda/store/objects/method_object.rb +94 -0
- data/lib/yoda/store/objects/module_object.rb +11 -0
- data/lib/yoda/store/objects/namespace_object.rb +67 -0
- data/lib/yoda/store/objects/overload.rb +51 -0
- data/lib/yoda/store/objects/patch.rb +46 -0
- data/lib/yoda/store/objects/patch_set.rb +80 -0
- data/lib/yoda/store/objects/tag.rb +62 -0
- data/lib/yoda/store/objects/value_object.rb +45 -0
- data/lib/yoda/store/project.rb +159 -0
- data/lib/yoda/store/query.rb +12 -0
- data/lib/yoda/store/query/associators.rb +10 -0
- data/lib/yoda/store/query/associators/associate_ancestors.rb +103 -0
- data/lib/yoda/store/query/associators/associate_methods.rb +38 -0
- data/lib/yoda/store/query/base.rb +16 -0
- data/lib/yoda/store/query/find_constant.rb +150 -0
- data/lib/yoda/store/query/find_meta_class.rb +18 -0
- data/lib/yoda/store/query/find_method.rb +74 -0
- data/lib/yoda/store/query/find_signature.rb +43 -0
- data/lib/yoda/store/registry.rb +67 -0
- data/lib/yoda/store/yard_importer.rb +260 -0
- data/lib/yoda/typing.rb +10 -0
- data/lib/yoda/typing/context.rb +96 -0
- data/lib/yoda/typing/environment.rb +35 -0
- data/lib/yoda/typing/evaluator.rb +256 -0
- data/lib/yoda/typing/lexical_scope.rb +26 -0
- data/lib/yoda/typing/relation.rb +15 -0
- data/lib/yoda/typing/traces.rb +9 -0
- data/lib/yoda/typing/traces/base.rb +26 -0
- data/lib/yoda/typing/traces/normal.rb +22 -0
- data/lib/yoda/typing/traces/send.rb +26 -0
- data/lib/yoda/version.rb +3 -0
- data/lib/yoda/yard_extensions.rb +11 -0
- data/lib/yoda/yard_extensions/sig_directive.rb +40 -0
- data/lib/yoda/yard_extensions/type_tag.rb +10 -0
- data/package.json +76 -0
- data/scripts/benchmark.rb +6 -0
- data/scripts/build_core_index.sh +16 -0
- data/yarn.lock +13 -0
- data/yoda-language-server.gemspec +40 -0
- 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,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.
|
data/exe/yoda
ADDED
|
@@ -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
|
data/lib/yoda.rb
ADDED
|
@@ -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
|