yoda-language-server 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +5 -3
- data/README.md +50 -48
- data/client/atom/main.js +13 -3
- data/client/vscode/.vscode/launch.json +7 -4
- data/client/vscode/package-lock.json +585 -1454
- data/client/vscode/package.json +10 -7
- data/client/vscode/src/extension.ts +3 -3
- data/client/vscode/src/test/completion.test.ts +39 -0
- data/client/vscode/src/test/helper.ts +38 -0
- data/client/vscode/src/test/index.ts +5 -3
- data/client/vscode/testFixture/completion.rb +1 -0
- data/exe/yoda +1 -20
- data/lib/yoda.rb +2 -1
- data/lib/yoda/commands.rb +34 -0
- data/lib/yoda/commands/base.rb +10 -0
- data/lib/yoda/commands/complete.rb +36 -0
- data/lib/yoda/commands/file_cursor_parsable.rb +29 -0
- data/lib/yoda/{runner → commands}/infer.rb +4 -9
- data/lib/yoda/commands/setup.rb +37 -0
- data/lib/yoda/errors.rb +34 -0
- data/lib/yoda/evaluation/evaluator.rb +2 -0
- data/lib/yoda/server.rb +60 -15
- data/lib/yoda/server/completion_provider.rb +8 -8
- data/lib/yoda/server/definition_provider.rb +8 -8
- data/lib/yoda/server/hover_provider.rb +6 -6
- data/lib/yoda/server/initialization_provider.rb +85 -0
- data/lib/yoda/server/{client_info.rb → session.rb} +6 -2
- data/lib/yoda/server/signature_provider.rb +6 -6
- data/lib/yoda/store/actions.rb +3 -1
- data/lib/yoda/store/actions/build_core_index.rb +44 -0
- data/lib/yoda/store/actions/import_core_library.rb +14 -9
- data/lib/yoda/store/actions/import_gem.rb +98 -0
- data/lib/yoda/store/actions/import_std_library.rb +35 -0
- data/lib/yoda/store/adapters.rb +1 -0
- data/lib/yoda/store/adapters/memory_adapter.rb +81 -0
- data/lib/yoda/store/objects.rb +4 -0
- data/lib/yoda/store/objects/addressable.rb +0 -12
- data/lib/yoda/store/objects/base.rb +4 -14
- data/lib/yoda/store/objects/merger.rb +4 -4
- data/lib/yoda/store/objects/patchable.rb +19 -0
- data/lib/yoda/store/objects/project_status.rb +169 -0
- data/lib/yoda/store/objects/serializable.rb +39 -0
- data/lib/yoda/store/project.rb +28 -114
- data/lib/yoda/store/project/cache.rb +79 -0
- data/lib/yoda/store/project/library_doc_loader.rb +102 -0
- data/lib/yoda/store/query/find_constant.rb +2 -1
- data/lib/yoda/store/registry.rb +15 -0
- data/lib/yoda/store/yard_importer.rb +58 -28
- data/lib/yoda/typing/evaluator.rb +8 -5
- data/lib/yoda/version.rb +1 -1
- data/package.json +32 -11
- data/scripts/benchmark.rb +1 -1
- data/yoda-language-server.gemspec +1 -0
- metadata +37 -7
- data/client/vscode/src/test/extension.test.ts +0 -22
- data/lib/yoda/runner/setup.rb +0 -26
- data/lib/yoda/store/actions/import_gems.rb +0 -91
data/client/vscode/package.json
CHANGED
@@ -3,9 +3,9 @@
|
|
3
3
|
"displayName": "yoda",
|
4
4
|
"description": "Static analytics tool for Ruby",
|
5
5
|
"version": "0.0.1",
|
6
|
-
"publisher": "
|
6
|
+
"publisher": "tomoasleep",
|
7
7
|
"engines": {
|
8
|
-
"vscode": "^1.
|
8
|
+
"vscode": "^1.23.0"
|
9
9
|
},
|
10
10
|
"categories": [
|
11
11
|
"Other"
|
@@ -27,13 +27,16 @@
|
|
27
27
|
"compile": "tsc -p ./",
|
28
28
|
"watch": "tsc -watch -p ./",
|
29
29
|
"postinstall": "node ./node_modules/vscode/bin/install",
|
30
|
+
"update-vscode": "node ./node_modules/vscode/bin/install",
|
30
31
|
"test": "npm run compile && node ./node_modules/vscode/bin/test"
|
31
32
|
},
|
33
|
+
"dependencies": {
|
34
|
+
"vscode": "^1.1.18",
|
35
|
+
"vscode-languageclient": "^4.1.4"
|
36
|
+
},
|
32
37
|
"devDependencies": {
|
33
|
-
"
|
34
|
-
"
|
35
|
-
"
|
36
|
-
"@types/node": "^7.0.43",
|
37
|
-
"@types/mocha": "^2.2.42"
|
38
|
+
"@types/mocha": "^5.2.0",
|
39
|
+
"@types/node": "^8.0.0",
|
40
|
+
"typescript": "^2.9.2"
|
38
41
|
}
|
39
42
|
}
|
@@ -17,7 +17,7 @@ export function activate(context: vscode.ExtensionContext) {
|
|
17
17
|
console.log('Congratulations, your extension "yoda" is now active!');
|
18
18
|
|
19
19
|
let execOptions = {
|
20
|
-
command:
|
20
|
+
command: 'yoda',
|
21
21
|
args: ['server'],
|
22
22
|
}
|
23
23
|
|
@@ -34,9 +34,9 @@ export function activate(context: vscode.ExtensionContext) {
|
|
34
34
|
}
|
35
35
|
}
|
36
36
|
|
37
|
-
let disposable = new LanguageClient('yoda', 'Yoda
|
37
|
+
let disposable = new LanguageClient('yoda', 'Yoda', serverOptions, clientOptions).start();
|
38
38
|
}
|
39
39
|
|
40
40
|
// this method is called when your extension is deactivated
|
41
41
|
export function deactivate() {
|
42
|
-
}
|
42
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import * as vscode from 'vscode';
|
2
|
+
import * as assert from 'assert';
|
3
|
+
import { getDocUri, activate } from './helper';
|
4
|
+
|
5
|
+
describe('Should do completion', () => {
|
6
|
+
const docUri = getDocUri('completion.rb');
|
7
|
+
|
8
|
+
it('Completes', async () => {
|
9
|
+
await testCompletion(docUri, new vscode.Position(0, 2), {
|
10
|
+
items: [
|
11
|
+
{ label: 'Object', kind: vscode.CompletionItemKind.Class },
|
12
|
+
]
|
13
|
+
});
|
14
|
+
})
|
15
|
+
|
16
|
+
|
17
|
+
});
|
18
|
+
|
19
|
+
async function testCompletion(
|
20
|
+
docUri: vscode.Uri,
|
21
|
+
position: vscode.Position,
|
22
|
+
expectedCompletionList: vscode.CompletionList
|
23
|
+
) {
|
24
|
+
await activate(docUri);
|
25
|
+
|
26
|
+
// Executing the command `vscode.executeCompletionItemProvider` to simulate triggering completion
|
27
|
+
const actualCompletionList = (await vscode.commands.executeCommand(
|
28
|
+
'vscode.executeCompletionItemProvider',
|
29
|
+
docUri,
|
30
|
+
position
|
31
|
+
)) as vscode.CompletionList;
|
32
|
+
|
33
|
+
assert.equal(actualCompletionList.items.length, expectedCompletionList.items.length);
|
34
|
+
expectedCompletionList.items.forEach((expectedItem, i) => {
|
35
|
+
const actualItem = actualCompletionList.items[i];
|
36
|
+
assert.equal(actualItem.label, expectedItem.label);
|
37
|
+
assert.equal(actualItem.kind, expectedItem.kind);
|
38
|
+
});
|
39
|
+
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import * as vscode from 'vscode';
|
2
|
+
import * as path from 'path';
|
3
|
+
|
4
|
+
export let doc: vscode.TextDocument;
|
5
|
+
export let editor: vscode.TextEditor;
|
6
|
+
export let documentEol: string;
|
7
|
+
export let platformEol: string;
|
8
|
+
|
9
|
+
export async function activate(docUri: vscode.Uri) {
|
10
|
+
const ext = vscode.extensions.getExtension('tomoasleep.yoda');
|
11
|
+
await ext.activate();
|
12
|
+
try {
|
13
|
+
doc = await vscode.workspace.openTextDocument(docUri);
|
14
|
+
editor = await vscode.window.showTextDocument(doc);
|
15
|
+
await sleep(20000); // Wait for server activation
|
16
|
+
} catch (e) {
|
17
|
+
console.error(e);
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
async function sleep(ms: number) {
|
22
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
23
|
+
}
|
24
|
+
|
25
|
+
export const getDocPath = (p: string) => {
|
26
|
+
return path.resolve(__dirname, '../../testFixture', p);
|
27
|
+
};
|
28
|
+
export const getDocUri = (p: string) => {
|
29
|
+
return vscode.Uri.file(getDocPath(p));
|
30
|
+
};
|
31
|
+
|
32
|
+
export async function setTestContent(content: string): Promise<boolean> {
|
33
|
+
const all = new vscode.Range(
|
34
|
+
doc.positionAt(0),
|
35
|
+
doc.positionAt(doc.getText().length)
|
36
|
+
);
|
37
|
+
return editor.edit(eb => eb.replace(all, content));
|
38
|
+
}
|
@@ -15,8 +15,10 @@ import * as testRunner from 'vscode/lib/testrunner';
|
|
15
15
|
// You can directly control Mocha options by uncommenting the following lines
|
16
16
|
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
|
17
17
|
testRunner.configure({
|
18
|
-
ui: '
|
19
|
-
useColors: true // colored output from test results
|
18
|
+
ui: 'bdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
|
19
|
+
useColors: true, // colored output from test results
|
20
|
+
timeout: 600000,
|
21
|
+
|
20
22
|
});
|
21
23
|
|
22
|
-
module.exports = testRunner;
|
24
|
+
module.exports = testRunner;
|
@@ -0,0 +1 @@
|
|
1
|
+
Object
|
data/exe/yoda
CHANGED
@@ -5,23 +5,4 @@ if Dir.exist?(File.join(__dir__, "..", ".git"))
|
|
5
5
|
end
|
6
6
|
|
7
7
|
require 'yoda'
|
8
|
-
|
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
|
8
|
+
Yoda::Commands::Top.start(ARGV)
|
data/lib/yoda.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
module Yoda
|
2
2
|
require "yoda/version"
|
3
|
+
require "yoda/commands"
|
4
|
+
require "yoda/errors"
|
3
5
|
require "yoda/evaluation"
|
4
6
|
require "yoda/model"
|
5
7
|
require "yoda/store"
|
6
8
|
require "yoda/server"
|
7
9
|
require "yoda/parsing"
|
8
10
|
require "yoda/typing"
|
9
|
-
require "yoda/runner"
|
10
11
|
require "yoda/yard_extensions"
|
11
12
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Yoda
|
4
|
+
# Commands module has handler for each cli command.
|
5
|
+
module Commands
|
6
|
+
require 'yoda/commands/base'
|
7
|
+
require 'yoda/commands/file_cursor_parsable'
|
8
|
+
require 'yoda/commands/setup'
|
9
|
+
require 'yoda/commands/infer'
|
10
|
+
require 'yoda/commands/complete'
|
11
|
+
|
12
|
+
class Top < Thor
|
13
|
+
desc 'setup', 'Setup indexes for current Ruby version and project gems'
|
14
|
+
def setup
|
15
|
+
Commands::Setup.run
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'infer POSITION', 'Infer the type of value at the specified position'
|
19
|
+
def infer(position)
|
20
|
+
Commands::Infer.run(position)
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'complete POSITION', 'Provide completion candidates for the specified position'
|
24
|
+
def complete(position)
|
25
|
+
Commands::Complete.run(position)
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'server', 'Start Language Server'
|
29
|
+
def server
|
30
|
+
Server.new.run
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Yoda
|
2
|
+
module Commands
|
3
|
+
class Complete < Base
|
4
|
+
include FileCursorParsable
|
5
|
+
|
6
|
+
attr_reader :filename_with_position
|
7
|
+
|
8
|
+
# @param filename_with_position [String] position representation with the format `path/to/file:line_num:character_num`
|
9
|
+
def initialize(filename_with_position)
|
10
|
+
@filename_with_position = filename_with_position
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
project.build_cache
|
15
|
+
puts create_signature_help(worker.current_node_signature)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# @param signature [Model::NodeSignature, nil]
|
21
|
+
# @return [String, nil]
|
22
|
+
def create_signature_help(signature)
|
23
|
+
return nil unless signature
|
24
|
+
signature.descriptions.map(&:title).join("\n")
|
25
|
+
end
|
26
|
+
|
27
|
+
def worker
|
28
|
+
@worker ||= Evaluation::CurrentNodeExplain.new(project.registry, File.read(filename), position)
|
29
|
+
end
|
30
|
+
|
31
|
+
def project
|
32
|
+
@project ||= Store::Project.new(Dir.pwd)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Yoda
|
2
|
+
module Commands
|
3
|
+
# Provide parsing methods of positon representation with the format `path/to/file:line_num:character_num`
|
4
|
+
module FileCursorParsable
|
5
|
+
private
|
6
|
+
|
7
|
+
# Returns a cursor literal to parse.
|
8
|
+
# @abstract
|
9
|
+
# @return [String]
|
10
|
+
def filename_with_position
|
11
|
+
fail NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String, nil] represents the filename part.
|
15
|
+
def filename
|
16
|
+
@filename ||= filename_with_position.split(':').first
|
17
|
+
end
|
18
|
+
|
19
|
+
# Parse location part of cursor literal and returns the parsed location.
|
20
|
+
# @return [Parsing::Location]
|
21
|
+
def position
|
22
|
+
@position ||= begin
|
23
|
+
row, column = filename_with_position.split(':').slice(1..2)
|
24
|
+
Parsing::Location.new(row: row.to_i, column: column.to_i)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,20 +1,15 @@
|
|
1
1
|
module Yoda
|
2
|
-
module
|
3
|
-
class Infer
|
2
|
+
module Commands
|
3
|
+
class Infer < Base
|
4
4
|
attr_reader :filename_with_position
|
5
5
|
|
6
|
-
# @param filename_with_position [String]
|
7
|
-
def self.run(filename_with_position)
|
8
|
-
new(filename_with_position).run
|
9
|
-
end
|
10
|
-
|
11
|
-
# @param filename_with_position [String]
|
6
|
+
# @param filename_with_position [String] position representation with the format `path/to/file:line_num:character_num`
|
12
7
|
def initialize(filename_with_position)
|
13
8
|
@filename_with_position = filename_with_position
|
14
9
|
end
|
15
10
|
|
16
11
|
def run
|
17
|
-
project.
|
12
|
+
project.build_cache
|
18
13
|
puts create_signature_help(worker.current_node_signature)
|
19
14
|
end
|
20
15
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Yoda
|
2
|
+
module Commands
|
3
|
+
class Setup < Base
|
4
|
+
# @return [String]
|
5
|
+
attr_reader :dir
|
6
|
+
|
7
|
+
# @return [true, false]
|
8
|
+
attr_reader :force_build
|
9
|
+
|
10
|
+
# @param dir [String]
|
11
|
+
def initialize(dir: nil, force_build: false)
|
12
|
+
@dir = dir || Dir.pwd
|
13
|
+
@force_build = force_build
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
build_core_index
|
18
|
+
if File.exist?(File.expand_path('Gemfile.lock', dir)) || force_build
|
19
|
+
STDERR.puts 'Building index for the current project...'
|
20
|
+
force_build ? project.rebuild_cache(progress: true) : project.build_cache(progress: true)
|
21
|
+
else
|
22
|
+
STDERR.puts 'Skipped building project index because Gemfile.lock is not exist for the current dir'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def project
|
27
|
+
@project ||= Store::Project.new(dir)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def build_core_index
|
33
|
+
Store::Actions::BuildCoreIndex.run unless Store::Actions::BuildCoreIndex.exists?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/yoda/errors.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Yoda
|
2
|
+
# @abstract
|
3
|
+
class BaseError < ::StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class GemImportError < BaseError
|
7
|
+
# @return [String]
|
8
|
+
attr_reader :name, :version
|
9
|
+
|
10
|
+
def initialize(name:, version:)
|
11
|
+
@name = name
|
12
|
+
@version = version
|
13
|
+
super(msg)
|
14
|
+
end
|
15
|
+
|
16
|
+
def msg
|
17
|
+
"Failed to import #{name} #{version}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class CoreImportError < BaseError
|
22
|
+
# @return [String]
|
23
|
+
attr_reader :name
|
24
|
+
|
25
|
+
def initialize(name)
|
26
|
+
@name = name
|
27
|
+
super(msg)
|
28
|
+
end
|
29
|
+
|
30
|
+
def msg
|
31
|
+
"Failed to import Ruby #{name} Library"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -76,6 +76,7 @@ module Yoda
|
|
76
76
|
def evaluation_context
|
77
77
|
@evaluation_context ||= begin
|
78
78
|
fail RuntimeError, "The namespace #{scope.scope_name} (#{scope}) is not registered" unless scope_constant
|
79
|
+
fail RuntimeError, "The namespace for #{scope} (#{scope.scope_name}) is not registered" unless receiver
|
79
80
|
lexical_scope = Typing::LexicalScope.new(scope_constant, scope.ancestor_scopes)
|
80
81
|
context = Typing::Context.new(registry: registry, caller_object: receiver, lexical_scope: lexical_scope)
|
81
82
|
context.env.bind_method_parameters(current_method_signature) if current_method_signature
|
@@ -89,6 +90,7 @@ module Yoda
|
|
89
90
|
@current_method_signature ||= Store::Query::FindSignature.new(registry).select(scope_constant, scope.name.to_s)&.first
|
90
91
|
end
|
91
92
|
|
93
|
+
# @return [Store::Objects::Base, nil]
|
92
94
|
def receiver
|
93
95
|
@receiver ||= begin
|
94
96
|
if scope.kind == :method
|
data/lib/yoda/server.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'language_server-protocol'
|
2
|
+
require 'securerandom'
|
2
3
|
|
3
4
|
module Yoda
|
4
5
|
class Server
|
@@ -6,8 +7,9 @@ module Yoda
|
|
6
7
|
require 'yoda/server/signature_provider'
|
7
8
|
require 'yoda/server/hover_provider'
|
8
9
|
require 'yoda/server/definition_provider'
|
10
|
+
require 'yoda/server/initialization_provider'
|
9
11
|
require 'yoda/server/deserializer'
|
10
|
-
require 'yoda/server/
|
12
|
+
require 'yoda/server/session'
|
11
13
|
|
12
14
|
LSP = ::LanguageServer::Protocol
|
13
15
|
|
@@ -21,8 +23,8 @@ module Yoda
|
|
21
23
|
# @type ::LanguageServer::Protocol::Transport::Stdio::Writer
|
22
24
|
attr_reader :writer
|
23
25
|
|
24
|
-
# @
|
25
|
-
attr_reader :
|
26
|
+
# @return [Responser]
|
27
|
+
attr_reader :session
|
26
28
|
|
27
29
|
# @type CompletionProvider
|
28
30
|
attr_reader :completion_provider
|
@@ -36,17 +38,26 @@ module Yoda
|
|
36
38
|
# @type DefinitionProvider
|
37
39
|
attr_reader :definition_provider
|
38
40
|
|
41
|
+
# @return [Array<Hash>]
|
42
|
+
attr_reader :after_notifications
|
43
|
+
|
39
44
|
def initialize
|
40
45
|
@reader = LSP::Transport::Stdio::Reader.new
|
41
46
|
@writer = LSP::Transport::Stdio::Writer.new
|
47
|
+
@after_notifications = []
|
42
48
|
end
|
43
49
|
|
44
50
|
def run
|
45
51
|
reader.read do |request|
|
46
52
|
begin
|
47
53
|
if result = callback(request)
|
48
|
-
|
54
|
+
if result.is_a?(LanguageServer::Protocol::Interface::ResponseError)
|
55
|
+
send_error(id: request[:id], error: result)
|
56
|
+
else
|
57
|
+
send_response(id: request[:id], result: result)
|
58
|
+
end
|
49
59
|
end
|
60
|
+
process_after_notifications if session&.client_initialized
|
50
61
|
rescue StandardError => ex
|
51
62
|
STDERR.puts ex
|
52
63
|
STDERR.puts ex.backtrace
|
@@ -54,6 +65,30 @@ module Yoda
|
|
54
65
|
end
|
55
66
|
end
|
56
67
|
|
68
|
+
def process_after_notifications
|
69
|
+
while notification = after_notifications.pop
|
70
|
+
send_notification(**notification)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param method [String]
|
75
|
+
# @param params [Object]
|
76
|
+
def send_notification(method:, params:)
|
77
|
+
writer.write(method: method, params: params)
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param id [String]
|
81
|
+
# @param result [Object]
|
82
|
+
def send_response(id:, result:)
|
83
|
+
writer.write(id: id, result: result)
|
84
|
+
end
|
85
|
+
|
86
|
+
# @param method [String]
|
87
|
+
# @param error [Object]
|
88
|
+
def send_error(id:, error:)
|
89
|
+
writer.write(id: id, error: error)
|
90
|
+
end
|
91
|
+
|
57
92
|
def callback(request)
|
58
93
|
if method_name = resolve(request_registrations, request[:method])
|
59
94
|
send(method_name, self.class.deserialize(request[:params] || {}))
|
@@ -65,10 +100,12 @@ module Yoda
|
|
65
100
|
|
66
101
|
# @param hash [Hash]
|
67
102
|
# @param key [String, Symbol]
|
103
|
+
# @return [Symbol, nil]
|
68
104
|
def resolve(hash, key)
|
69
|
-
key.to_s.split('/').reduce(hash) do |scope, key|
|
105
|
+
resolved = key.to_s.split('/').reduce(hash) do |scope, key|
|
70
106
|
(scope || {})[key.to_sym]
|
71
107
|
end
|
108
|
+
resolved.is_a?(Symbol) && resolved
|
72
109
|
end
|
73
110
|
|
74
111
|
def request_registrations
|
@@ -97,12 +134,13 @@ module Yoda
|
|
97
134
|
end
|
98
135
|
|
99
136
|
def handle_initialize(params)
|
100
|
-
@
|
101
|
-
@completion_provider = CompletionProvider.new(@
|
102
|
-
@hover_provider = HoverProvider.new(@
|
103
|
-
@signature_provider = SignatureProvider.new(@
|
104
|
-
@definition_provider = DefinitionProvider.new(@
|
105
|
-
|
137
|
+
@session = Session.new(params[:root_uri])
|
138
|
+
@completion_provider = CompletionProvider.new(@session)
|
139
|
+
@hover_provider = HoverProvider.new(@session)
|
140
|
+
@signature_provider = SignatureProvider.new(@session)
|
141
|
+
@definition_provider = DefinitionProvider.new(@session)
|
142
|
+
|
143
|
+
(InitializationProvider.new(@session).provide || []).each { |notification| after_notifications.push(notification) }
|
106
144
|
|
107
145
|
LSP::Interface::InitializeResult.new(
|
108
146
|
capabilities: LSP::Interface::ServerCapabilities.new(
|
@@ -113,7 +151,7 @@ module Yoda
|
|
113
151
|
),
|
114
152
|
),
|
115
153
|
completion_provider: LSP::Interface::CompletionOptions.new(
|
116
|
-
resolve_provider:
|
154
|
+
resolve_provider: false,
|
117
155
|
trigger_characters: ['.', '@', '[', ':', '!', '<'],
|
118
156
|
),
|
119
157
|
hover_provider: true,
|
@@ -123,9 +161,16 @@ module Yoda
|
|
123
161
|
),
|
124
162
|
),
|
125
163
|
)
|
164
|
+
rescue => e
|
165
|
+
LanguageServer::Protocol::Interface::ResponseError.new(
|
166
|
+
message: "Failed to initialize yoda: #{e.class} #{e.message}",
|
167
|
+
code: LanguageServer::Protocol::Constant::InitializeError::UNKNOWN_PROTOCOL_VERSION,
|
168
|
+
data: LanguageServer::Protocol::Interface::InitializeError.new(retry: false),
|
169
|
+
)
|
126
170
|
end
|
127
171
|
|
128
172
|
def handle_initialized(_params)
|
173
|
+
session.client_initialized = true
|
129
174
|
end
|
130
175
|
|
131
176
|
def handle_shutdown(_params)
|
@@ -143,19 +188,19 @@ module Yoda
|
|
143
188
|
def handle_text_document_did_open(params)
|
144
189
|
uri = params[:text_document][:uri]
|
145
190
|
text = params[:text_document][:text]
|
146
|
-
|
191
|
+
session.file_store.store(uri, text)
|
147
192
|
end
|
148
193
|
|
149
194
|
def handle_text_document_did_save(params)
|
150
195
|
uri = params[:text_document][:uri]
|
151
196
|
|
152
|
-
|
197
|
+
session.reparse_doc(uri)
|
153
198
|
end
|
154
199
|
|
155
200
|
def handle_text_document_did_change(params)
|
156
201
|
uri = params[:text_document][:uri]
|
157
202
|
text = params[:content_changes].first[:text]
|
158
|
-
|
203
|
+
session.file_store.store(uri, text)
|
159
204
|
end
|
160
205
|
|
161
206
|
def handle_text_document_completion(params)
|