language_server 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.vscode/settings.json +4 -1
- data/CODE_OF_CONDUCT.md +1 -1
- data/Dockerfile.development +3 -2
- data/LICENSE.txt +1 -1
- data/README.md +5 -3
- data/bin/generate_files +2 -0
- data/bin/m +17 -0
- data/circle.yml +31 -0
- data/docker-compose.yml +17 -5
- data/docker-sync.yml +3 -1
- data/language_server.gemspec +2 -0
- data/lib/language_server.rb +24 -14
- data/lib/language_server/completion_provider/ad_hoc.rb +45 -0
- data/lib/language_server/completion_provider/rcodetools.rb +10 -17
- data/lib/language_server/file_store.rb +99 -0
- data/lib/language_server/project.rb +67 -0
- data/lib/language_server/project/node.rb +96 -0
- data/lib/language_server/project/parser.rb +80 -0
- data/lib/language_server/version.rb +1 -1
- metadata +37 -3
- data/lib/language_server/uri_store.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7afd0094b57b216afc44723653a9bd07aa1f7910
|
4
|
+
data.tar.gz: 1c440fef75c1492a7c4048e31f70123cdcd5c049
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9826d5f5402567b90eac9a722d4e4baa0cea4b9d388df2b263641a9a28190759f86cb739b56f355365302b1640c6c1f90890ae8abe91b1c08b99af8443c5864
|
7
|
+
data.tar.gz: f1eb2ab1791cdd3d1440500603e00498e886f68ba1e71f59e615e2cac6a1d59cc0fce0dbdfd0586d70369c32dc1b403b29d9bbc61fe60756cd82db3a6f0f1b01
|
data/.vscode/settings.json
CHANGED
data/CODE_OF_CONDUCT.md
CHANGED
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
|
|
55
55
|
## Enforcement
|
56
56
|
|
57
57
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
-
reported by contacting the project team at
|
58
|
+
reported by contacting the project team at mtsmfm@gmail.com. All
|
59
59
|
complaints will be reviewed and investigated and will result in a response that
|
60
60
|
is deemed necessary and appropriate to the circumstances. The project team is
|
61
61
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
data/Dockerfile.development
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
-
|
1
|
+
ARG RUBY_VERSION=2.4.1
|
2
|
+
FROM ruby:$RUBY_VERSION
|
2
3
|
|
3
4
|
RUN apt-get update && apt-get install less -y
|
4
5
|
RUN groupadd --gid 1000 ruby && useradd --uid 1000 --gid ruby --shell /bin/bash --create-home ruby
|
5
6
|
RUN mkdir /app && chown ruby:ruby /app
|
6
7
|
|
7
8
|
ENV LANG=C.UTF-8 \
|
8
|
-
BUNDLE_PATH=/app/vendor/bundle \
|
9
|
+
BUNDLE_PATH=/app/vendor/bundle/$RUBY_VERSION \
|
9
10
|
BUNDLE_JOBS=4
|
10
11
|
|
11
12
|
USER ruby
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -4,7 +4,7 @@ A Ruby Language Server implementation.
|
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
If you are using vscode,
|
7
|
+
If you are using vscode, install [ruby-lsc](https://marketplace.visualstudio.com/items?itemName=mtsmfm.ruby-lsc) extension.
|
8
8
|
|
9
9
|
### Docker
|
10
10
|
|
@@ -61,11 +61,13 @@ https://github.com/EugenMayer/docker-sync/wiki/1.-Installation
|
|
61
61
|
|
62
62
|
### Run test
|
63
63
|
|
64
|
-
$ docker-compose run app
|
64
|
+
$ docker-compose run app bin/m
|
65
|
+
$ docker-compose run ruby-2-3 bin/m
|
66
|
+
$ docker-compose run ruby-2-2 bin/m
|
65
67
|
|
66
68
|
## Contributing
|
67
69
|
|
68
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
70
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/mtsmfm/language_server-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
69
71
|
|
70
72
|
|
71
73
|
## License
|
data/bin/generate_files
CHANGED
data/bin/m
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'm' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("m", "m")
|
data/circle.yml
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
version: 2
|
2
|
+
jobs:
|
3
|
+
build:
|
4
|
+
docker:
|
5
|
+
- image: tmaier/docker-compose
|
6
|
+
working_directory: /project
|
7
|
+
steps:
|
8
|
+
- checkout
|
9
|
+
- setup_remote_docker:
|
10
|
+
version: 17.05.0-ce
|
11
|
+
- run:
|
12
|
+
name: setup
|
13
|
+
command: |
|
14
|
+
set -x
|
15
|
+
docker info
|
16
|
+
docker volume create mtsmfm-language-server-sync
|
17
|
+
docker create -v mtsmfm-language-server-sync:/app --name mtsmfm-language-server-sync busybox chown -R 1000:1000 /app
|
18
|
+
docker cp . mtsmfm-language-server-sync:/app
|
19
|
+
docker start mtsmfm-language-server-sync
|
20
|
+
docker-compose build
|
21
|
+
docker-compose run app bin/setup
|
22
|
+
docker-compose run ruby-2-3 bin/setup
|
23
|
+
docker-compose run ruby-2-2 bin/setup
|
24
|
+
- run:
|
25
|
+
name: test
|
26
|
+
command: |
|
27
|
+
set -x
|
28
|
+
docker-compose run node bin/generate_files
|
29
|
+
docker-compose run app bin/m
|
30
|
+
docker-compose run ruby-2-3 bin/m
|
31
|
+
docker-compose run ruby-2-2 bin/m
|
data/docker-compose.yml
CHANGED
@@ -1,23 +1,35 @@
|
|
1
1
|
version: '3.0'
|
2
2
|
services:
|
3
|
-
app:
|
4
|
-
build:
|
3
|
+
app: &app
|
4
|
+
build: &app-build
|
5
5
|
context: .
|
6
6
|
dockerfile: Dockerfile.development
|
7
7
|
volumes:
|
8
|
-
-
|
8
|
+
- mtsmfm-language-server-sync:/app:nocopy
|
9
9
|
- home:/home/ruby
|
10
10
|
- $HOME/.gitconfig:/home/ruby/.gitconfig:ro
|
11
11
|
- $HOME/.ssh:/home/ruby/.ssh:ro
|
12
12
|
- $HOME/.gem:/home/ruby/.gem
|
13
|
+
ruby-2-3:
|
14
|
+
<<: *app
|
15
|
+
build:
|
16
|
+
<<: *app-build
|
17
|
+
args:
|
18
|
+
RUBY_VERSION: 2.3
|
19
|
+
ruby-2-2:
|
20
|
+
<<: *app
|
21
|
+
build:
|
22
|
+
<<: *app-build
|
23
|
+
args:
|
24
|
+
RUBY_VERSION: 2.2
|
13
25
|
node:
|
14
26
|
build:
|
15
27
|
context: .
|
16
28
|
dockerfile: Dockerfile-node.development
|
17
29
|
volumes:
|
18
|
-
-
|
30
|
+
- mtsmfm-language-server-sync:/app:nocopy
|
19
31
|
- home:/home/node
|
20
32
|
volumes:
|
21
|
-
|
33
|
+
mtsmfm-language-server-sync:
|
22
34
|
external: true
|
23
35
|
home:
|
data/docker-sync.yml
CHANGED
data/language_server.gemspec
CHANGED
@@ -32,4 +32,6 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_development_dependency "minitest", "~> 5.0"
|
33
33
|
spec.add_development_dependency "pry-byebug"
|
34
34
|
spec.add_development_dependency "minitest-power_assert"
|
35
|
+
spec.add_development_dependency "m"
|
36
|
+
spec.add_development_dependency "awesome_print"
|
35
37
|
end
|
data/lib/language_server.rb
CHANGED
@@ -4,7 +4,9 @@ require "language_server/protocol/constants"
|
|
4
4
|
require "language_server/protocol/stdio"
|
5
5
|
require "language_server/linter/ruby_wc"
|
6
6
|
require "language_server/completion_provider/rcodetools"
|
7
|
-
require "language_server/
|
7
|
+
require "language_server/completion_provider/ad_hoc"
|
8
|
+
require "language_server/file_store"
|
9
|
+
require "language_server/project"
|
8
10
|
|
9
11
|
require "json"
|
10
12
|
require "logger"
|
@@ -18,6 +20,7 @@ module LanguageServer
|
|
18
20
|
def run
|
19
21
|
writer = Protocol::Stdio::Writer.new
|
20
22
|
reader = Protocol::Stdio::Reader.new
|
23
|
+
variables = {}
|
21
24
|
|
22
25
|
reader.read do |request|
|
23
26
|
method = request[:method].to_sym
|
@@ -29,7 +32,12 @@ module LanguageServer
|
|
29
32
|
}
|
30
33
|
|
31
34
|
if subscriber
|
32
|
-
|
35
|
+
keys = subscriber.parameters.map(&:last)
|
36
|
+
result = subscriber.call(
|
37
|
+
{
|
38
|
+
request: request, notifier: writer.method(:notify), variables: variables
|
39
|
+
}.merge(variables).select {|k, _| keys.include?(k) }
|
40
|
+
)
|
33
41
|
|
34
42
|
if request[:id]
|
35
43
|
writer.respond(id: request[:id], result: result)
|
@@ -49,7 +57,10 @@ module LanguageServer
|
|
49
57
|
end
|
50
58
|
end
|
51
59
|
|
52
|
-
on :initialize do
|
60
|
+
on :initialize do |request:, variables:|
|
61
|
+
variables[:file_store] = FileStore.new(load_paths: $LOAD_PATH, remote_root: request[:params][:rootPath], local_root: Dir.getwd)
|
62
|
+
variables[:project] = Project.new(variables[:file_store])
|
63
|
+
|
53
64
|
Protocol::Interfaces::InitializeResult.new(
|
54
65
|
capabilities: Protocol::Interfaces::ServerCapabilities.new(
|
55
66
|
text_document_sync: Protocol::Interfaces::TextDocumentSyncOptions.new(
|
@@ -67,10 +78,11 @@ module LanguageServer
|
|
67
78
|
exit
|
68
79
|
end
|
69
80
|
|
70
|
-
on :"textDocument/didChange" do |request
|
81
|
+
on :"textDocument/didChange" do |request:, notifier:, file_store:, project:|
|
71
82
|
uri = request[:params][:textDocument][:uri]
|
72
83
|
text = request[:params][:contentChanges][0][:text]
|
73
|
-
|
84
|
+
file_store.cache(uri, text)
|
85
|
+
project.recalculate_result(uri)
|
74
86
|
|
75
87
|
diagnostics = Linter::RubyWC.new(text).call.map do |error|
|
76
88
|
Protocol::Interfaces::Diagnostic.new(
|
@@ -98,15 +110,13 @@ module LanguageServer
|
|
98
110
|
)
|
99
111
|
end
|
100
112
|
|
101
|
-
on :"textDocument/completion" do |request
|
113
|
+
on :"textDocument/completion" do |request:, file_store:, project:|
|
102
114
|
uri = request[:params][:textDocument][:uri]
|
103
|
-
line, character = request[:params][:position].fetch_values(:line, :character)
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
)
|
110
|
-
end
|
115
|
+
line, character = request[:params][:position].fetch_values(:line, :character).map(&:to_i)
|
116
|
+
|
117
|
+
[
|
118
|
+
CompletionProvider::AdHoc.new(uri: uri, line: line, character: character, project: project),
|
119
|
+
CompletionProvider::Rcodetools.new(uri: uri, line: line, character: character, file_store: file_store)
|
120
|
+
].flat_map(&:call)
|
111
121
|
end
|
112
122
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "language_server/project"
|
2
|
+
|
3
|
+
module LanguageServer
|
4
|
+
module CompletionProvider
|
5
|
+
class AdHoc
|
6
|
+
def initialize(uri:, line:, character:, project:)
|
7
|
+
@uri = uri
|
8
|
+
@line = line
|
9
|
+
@character = character
|
10
|
+
@project = project
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
(project.constants(uri: uri, line: line, character: character).map {|c|
|
15
|
+
Protocol::Interfaces::CompletionItem.new(
|
16
|
+
label: c.name,
|
17
|
+
detail: c.full_name,
|
18
|
+
documentation: "#{c.remote_path}##{c.lineno}",
|
19
|
+
kind: Protocol::Constants::CompletionItemKind::ENUM
|
20
|
+
)
|
21
|
+
} +
|
22
|
+
project.classes(uri: uri, line: line, character: character).map {|c|
|
23
|
+
Protocol::Interfaces::CompletionItem.new(
|
24
|
+
label: c.name,
|
25
|
+
detail: c.full_name,
|
26
|
+
documentation: "#{c.remote_path}##{c.lineno}",
|
27
|
+
kind: Protocol::Constants::CompletionItemKind::CLASS
|
28
|
+
)
|
29
|
+
} +
|
30
|
+
project.modules(uri: uri, line: line, character: character).map {|m|
|
31
|
+
Protocol::Interfaces::CompletionItem.new(
|
32
|
+
label: m.name,
|
33
|
+
detail: m.full_name,
|
34
|
+
documentation: "#{m.remote_path}##{m.lineno}",
|
35
|
+
kind: Protocol::Constants::CompletionItemKind::MODULE
|
36
|
+
)
|
37
|
+
}).uniq(&:label)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_reader :uri, :line, :character, :project
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -1,22 +1,10 @@
|
|
1
|
-
require "language_server/
|
1
|
+
require "language_server/protocol/interfaces/completion_item"
|
2
|
+
require "language_server/protocol/constants/completion_item_kind"
|
2
3
|
|
3
4
|
require "rcodetools/completion"
|
4
5
|
|
5
6
|
module LanguageServer
|
6
7
|
module CompletionProvider
|
7
|
-
class Candidate
|
8
|
-
attr_reader :method_name, :description
|
9
|
-
|
10
|
-
def initialize(method_name:, description:)
|
11
|
-
@method_name = method_name
|
12
|
-
@description = description
|
13
|
-
end
|
14
|
-
|
15
|
-
def ==(other)
|
16
|
-
method_name == other.method_name && description == other.description
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
8
|
class Filter < ::Rcodetools::XMPCompletionFilter
|
21
9
|
@candidates_with_description_flag = true
|
22
10
|
|
@@ -28,24 +16,29 @@ module LanguageServer
|
|
28
16
|
end
|
29
17
|
|
30
18
|
class Rcodetools
|
31
|
-
def initialize(uri
|
19
|
+
def initialize(uri:, line:, character:, file_store:)
|
32
20
|
@uri = uri
|
33
21
|
@line = line
|
34
22
|
@character = character
|
23
|
+
@file_store = file_store
|
35
24
|
end
|
36
25
|
|
37
26
|
def call
|
38
27
|
_, candidates = Filter.run(source, lineno: @line + 1, column: @character)
|
39
28
|
candidates.map do |candidate|
|
40
29
|
method_name, description = candidate.split(/\0/, 2)
|
41
|
-
|
30
|
+
Protocol::Interfaces::CompletionItem.new(
|
31
|
+
label: method_name,
|
32
|
+
detail: description,
|
33
|
+
kind: Protocol::Constants::CompletionItemKind::METHOD
|
34
|
+
)
|
42
35
|
end
|
43
36
|
end
|
44
37
|
|
45
38
|
private
|
46
39
|
|
47
40
|
def source
|
48
|
-
|
41
|
+
@file_store.read_remote_uri(@uri)
|
49
42
|
end
|
50
43
|
end
|
51
44
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require "uri"
|
2
|
+
|
3
|
+
module LanguageServer
|
4
|
+
class FileStore
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
class FilePath
|
8
|
+
class << self
|
9
|
+
def from_remote_uri(remote_root:, local_root:, remote_uri:)
|
10
|
+
new(remote_root: remote_root, local_root: local_root, local_path: URI(remote_uri).path.sub(remote_root, local_root))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :local_root, :remote_root, :local_path
|
15
|
+
|
16
|
+
def initialize(remote_root:, local_root:, local_path:)
|
17
|
+
@remote_root = remote_root
|
18
|
+
@local_root = local_root
|
19
|
+
@local_path = local_path
|
20
|
+
end
|
21
|
+
|
22
|
+
def remote_path
|
23
|
+
@remote_path ||= local_path.sub(local_root, remote_root)
|
24
|
+
end
|
25
|
+
|
26
|
+
def eql?(other)
|
27
|
+
self.class == other.class && remote_path == other.remote_path
|
28
|
+
end
|
29
|
+
|
30
|
+
def ==(other)
|
31
|
+
eql?(other)
|
32
|
+
end
|
33
|
+
|
34
|
+
def hash
|
35
|
+
self.remote_path.hash
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(load_paths: [], remote_root: Dir.getwd, local_root: Dir.getwd)
|
40
|
+
@load_paths = load_paths
|
41
|
+
@remote_root = remote_root
|
42
|
+
@local_root = local_root
|
43
|
+
end
|
44
|
+
|
45
|
+
def cache(remote_uri, content)
|
46
|
+
cache_store[path_from_remote_uri(remote_uri)] = content
|
47
|
+
end
|
48
|
+
|
49
|
+
def path_from_remote_uri(remote_uri)
|
50
|
+
FilePath.from_remote_uri(local_root: local_root, remote_root: remote_root, remote_uri: remote_uri)
|
51
|
+
end
|
52
|
+
|
53
|
+
def read(path)
|
54
|
+
if exists_on_cache?(path)
|
55
|
+
read_from_cache(path)
|
56
|
+
else
|
57
|
+
read_from_local(path)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def read_remote_uri(remote_uri)
|
62
|
+
read(path_from_remote_uri(remote_uri))
|
63
|
+
end
|
64
|
+
|
65
|
+
def each(&block)
|
66
|
+
all_paths.each do |path|
|
67
|
+
block.call(read(path), path)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
attr_reader :load_paths, :remote_root, :local_root
|
74
|
+
|
75
|
+
def all_paths
|
76
|
+
(cache_store.keys + load_paths.flat_map {|path|
|
77
|
+
Dir.glob(File.join(path, "**", "*.rb"))
|
78
|
+
}.map {|path|
|
79
|
+
FilePath.new(local_root: local_root, remote_root: remote_root, local_path: path)
|
80
|
+
}).uniq
|
81
|
+
end
|
82
|
+
|
83
|
+
def exists_on_cache?(path)
|
84
|
+
cache_store.key?(path)
|
85
|
+
end
|
86
|
+
|
87
|
+
def read_from_cache(path)
|
88
|
+
cache_store[path]
|
89
|
+
end
|
90
|
+
|
91
|
+
def read_from_local(path)
|
92
|
+
File.read(path.local_path)
|
93
|
+
end
|
94
|
+
|
95
|
+
def cache_store
|
96
|
+
@cache_store ||= {}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'language_server/project/parser'
|
2
|
+
|
3
|
+
module LanguageServer
|
4
|
+
class Project
|
5
|
+
def initialize(file_store)
|
6
|
+
@file_store = file_store
|
7
|
+
@result_store = {}
|
8
|
+
|
9
|
+
fetch_result
|
10
|
+
end
|
11
|
+
|
12
|
+
def recalculate_result(uri)
|
13
|
+
path = file_store.path_from_remote_uri(uri)
|
14
|
+
result_store[path] = calculate(file_store.read(path), path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def constants(uri: nil, line: nil, character: nil)
|
18
|
+
node = find_nearest_node(uri: uri, line: line, character: character) if uri && line && character
|
19
|
+
|
20
|
+
lazy_constants.select {|n| n.names[0..-2] == Array(node && node.names).first(n.names.size - 1) }.force
|
21
|
+
end
|
22
|
+
|
23
|
+
def modules(uri: nil, line: nil, character: nil)
|
24
|
+
node = find_nearest_node(uri: uri, line: line, character: character) if uri && line && character
|
25
|
+
|
26
|
+
lazy_modules.select {|n| n.names[0..-2] == Array(node && node.names).first(n.names.size - 1) }.force
|
27
|
+
end
|
28
|
+
|
29
|
+
def classes(uri: nil, line: nil, character: nil)
|
30
|
+
node = find_nearest_node(uri: uri, line: line, character: character) if uri && line && character
|
31
|
+
|
32
|
+
lazy_classes.select {|n| n.names[0..-2] == Array(node && node.names).first(n.names.size - 1) }.force
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_reader :file_store, :result_store
|
38
|
+
|
39
|
+
def lazy_constants
|
40
|
+
result_store.each_value.lazy.flat_map(&:constants)
|
41
|
+
end
|
42
|
+
|
43
|
+
def lazy_modules
|
44
|
+
result_store.each_value.lazy.flat_map(&:modules)
|
45
|
+
end
|
46
|
+
|
47
|
+
def lazy_classes
|
48
|
+
result_store.each_value.lazy.flat_map(&:classes)
|
49
|
+
end
|
50
|
+
|
51
|
+
def fetch_result
|
52
|
+
file_store.each {|content, path|
|
53
|
+
result_store[path] = calculate(content, path)
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def find_nearest_node(uri:, line:, character:)
|
58
|
+
result = result_store[file_store.path_from_remote_uri(uri)]
|
59
|
+
|
60
|
+
(result.modules + result.classes).select {|node| node.lines.include?(line) }.min_by {|node| node.lines.size }
|
61
|
+
end
|
62
|
+
|
63
|
+
def calculate(content, path)
|
64
|
+
Parser.parse(content, path)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module LanguageServer
|
2
|
+
class Project
|
3
|
+
class Node
|
4
|
+
class << self
|
5
|
+
def attributes(*attrs)
|
6
|
+
attr_accessor(*attrs)
|
7
|
+
attribute_names.concat(attrs)
|
8
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
9
|
+
def initialize(#{attribute_names.map {|n| "#{n}:" }.join(',')})
|
10
|
+
#{
|
11
|
+
attribute_names.map {|n|
|
12
|
+
"@#{n} = #{n}"
|
13
|
+
}.join("\n")
|
14
|
+
}
|
15
|
+
end
|
16
|
+
RUBY
|
17
|
+
end
|
18
|
+
|
19
|
+
def attribute_names
|
20
|
+
@attribute_names ||= superclass.respond_to?(:attribute_names) ? superclass.attribute_names.dup : []
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
attributes :lineno, :path
|
25
|
+
|
26
|
+
def remote_path; path.remote_path; end
|
27
|
+
def local_path; path.local_path; end
|
28
|
+
|
29
|
+
def eql?(other)
|
30
|
+
other.instance_of?(self.class) && attributes == other.attributes
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(other)
|
34
|
+
eql?(other)
|
35
|
+
end
|
36
|
+
|
37
|
+
def hash
|
38
|
+
self.attributes.hash
|
39
|
+
end
|
40
|
+
|
41
|
+
def attributes
|
42
|
+
self.class.attribute_names.map {|a|
|
43
|
+
[a, public_send(a)]
|
44
|
+
}.to_h
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Constant < Node
|
49
|
+
attributes :namespaces, :name, :value
|
50
|
+
|
51
|
+
def unshift_namespace(class_or_module)
|
52
|
+
namespaces.unshift(class_or_module)
|
53
|
+
end
|
54
|
+
|
55
|
+
def names
|
56
|
+
namespaces.flat_map(&:names) + [name]
|
57
|
+
end
|
58
|
+
|
59
|
+
def full_name
|
60
|
+
names.join('::')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class LiteralValue < Node
|
65
|
+
attributes :value
|
66
|
+
end
|
67
|
+
|
68
|
+
class Module < Node
|
69
|
+
attributes :constant, :children
|
70
|
+
|
71
|
+
%i(name namespaces full_name names).each do |m|
|
72
|
+
define_method(m) { constant.__send__(m) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def unshift_namespace(class_or_module)
|
76
|
+
constant.unshift_namespace(class_or_module)
|
77
|
+
end
|
78
|
+
|
79
|
+
def lines
|
80
|
+
constant.lineno..lineno
|
81
|
+
end
|
82
|
+
|
83
|
+
def inspect
|
84
|
+
"<Module #{full_name}#L#{lines.begin}-#{lines.end}>"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Class < Module
|
89
|
+
attributes :superclass
|
90
|
+
|
91
|
+
def inspect
|
92
|
+
"<Class #{full_name}#L#{lines.begin}-#{lines.end}>"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'language_server/project/node'
|
2
|
+
require 'ripper'
|
3
|
+
|
4
|
+
module LanguageServer
|
5
|
+
class Project
|
6
|
+
class Parser < Ripper
|
7
|
+
class Result
|
8
|
+
attr_reader :constants, :classes, :modules
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@constants = []
|
12
|
+
@classes = []
|
13
|
+
@modules = []
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def parse(src, path = nil)
|
19
|
+
new(src, path).tap(&:parse).result
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :result
|
24
|
+
|
25
|
+
def initialize(src, path)
|
26
|
+
super(src, path && path.remote_path)
|
27
|
+
|
28
|
+
@path = path
|
29
|
+
@result = Result.new
|
30
|
+
@current_constants = []
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :path
|
36
|
+
|
37
|
+
def lineno
|
38
|
+
# Language Server Protocol's lineno is zero origin
|
39
|
+
super - 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_const(name)
|
43
|
+
Constant.new(namespaces: [], name: name, value: nil, lineno: lineno, path: path).tap do |c|
|
44
|
+
@current_constants.push(c)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def on_int(value)
|
49
|
+
LiteralValue.new(value: value.to_i, lineno: lineno, path: path)
|
50
|
+
end
|
51
|
+
|
52
|
+
def on_stmts_add(*args)
|
53
|
+
args.flatten.compact
|
54
|
+
end
|
55
|
+
|
56
|
+
def on_assign(left, right)
|
57
|
+
result.constants << left if Constant === left
|
58
|
+
|
59
|
+
left.value = right if left.respond_to?(:value) # TODO: remove this condition
|
60
|
+
left
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_module(constant, children)
|
64
|
+
cn = children.select {|child| child.instance_of?(Constant) || child.instance_of?(Module) || child.instance_of?(Class)}
|
65
|
+
Module.new(constant: constant, lineno: lineno, path: path, children: cn).tap do |m|
|
66
|
+
result.modules << m
|
67
|
+
cn.each {|child| child.unshift_namespace(m) }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def on_class(constant, superclass, children)
|
72
|
+
cn = children.select {|child| child.instance_of?(Constant) || child.instance_of?(Module) || child.instance_of?(Class)}
|
73
|
+
Class.new(constant: constant, superclass: superclass, lineno: lineno, path: path, children: cn).tap do |c|
|
74
|
+
result.classes << c
|
75
|
+
cn.each {|child| child.unshift_namespace(c) }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: language_server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fumiaki MATSUSHIMA
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rcodetools
|
@@ -94,6 +94,34 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: m
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: awesome_print
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
97
125
|
description: A Ruby Language Server implementation
|
98
126
|
email:
|
99
127
|
- mtsmfm@gmail.com
|
@@ -114,14 +142,21 @@ files:
|
|
114
142
|
- Rakefile
|
115
143
|
- bin/console
|
116
144
|
- bin/generate_files
|
145
|
+
- bin/m
|
117
146
|
- bin/setup
|
147
|
+
- circle.yml
|
118
148
|
- docker-compose.yml
|
119
149
|
- docker-sync.yml
|
120
150
|
- exe/language_server
|
121
151
|
- language_server.gemspec
|
122
152
|
- lib/language_server.rb
|
153
|
+
- lib/language_server/completion_provider/ad_hoc.rb
|
123
154
|
- lib/language_server/completion_provider/rcodetools.rb
|
155
|
+
- lib/language_server/file_store.rb
|
124
156
|
- lib/language_server/linter/ruby_wc.rb
|
157
|
+
- lib/language_server/project.rb
|
158
|
+
- lib/language_server/project/node.rb
|
159
|
+
- lib/language_server/project/parser.rb
|
125
160
|
- lib/language_server/protocol/constants.rb
|
126
161
|
- lib/language_server/protocol/constants/completion_item_kind.rb
|
127
162
|
- lib/language_server/protocol/constants/diagnostic_severity.rb
|
@@ -225,7 +260,6 @@ files:
|
|
225
260
|
- lib/language_server/protocol/stdio.rb
|
226
261
|
- lib/language_server/protocol/stdio/reader.rb
|
227
262
|
- lib/language_server/protocol/stdio/writer.rb
|
228
|
-
- lib/language_server/uri_store.rb
|
229
263
|
- lib/language_server/version.rb
|
230
264
|
- package.json
|
231
265
|
- scripts/generateFiles.ts
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module LanguageServer
|
2
|
-
class UriStore
|
3
|
-
class << self
|
4
|
-
def []=(uri, content)
|
5
|
-
store[uri] = content
|
6
|
-
end
|
7
|
-
|
8
|
-
def [](uri)
|
9
|
-
store[uri]
|
10
|
-
end
|
11
|
-
|
12
|
-
def clear
|
13
|
-
store.clear
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def store
|
19
|
-
@store ||= {}
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|