language_server 0.3.1 → 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 +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
|