language_server 0.9.0 → 0.10.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 +5 -5
- data/.gitignore +1 -0
- data/.rubocop.yml +45 -0
- data/CHANGELOG.md +5 -0
- data/Dockerfile +2 -0
- data/Dockerfile.development +1 -1
- data/Gemfile +4 -1
- data/Guardfile +1 -1
- data/README.md +2 -0
- data/Rakefile +1 -1
- data/benchmark/rubocop +25 -0
- data/benchmark/ruby_wc +3 -3
- data/bin/ci +7 -0
- data/bin/rubocop +17 -0
- data/circle.yml +10 -2
- data/exe/language_server-ruby +23 -1
- data/language_server.gemspec +13 -14
- data/lib/language_server.rb +42 -25
- data/lib/language_server/completion_provider/ad_hoc.rb +7 -7
- data/lib/language_server/completion_provider/rcodetools.rb +4 -4
- data/lib/language_server/definition_provider/ad_hoc.rb +5 -5
- data/lib/language_server/file_store.rb +21 -21
- data/lib/language_server/linter/rubocop.rb +16 -18
- data/lib/language_server/linter/ruby_wc.rb +48 -37
- data/lib/language_server/logger.rb +12 -12
- data/lib/language_server/project.rb +34 -34
- data/lib/language_server/project/node.rb +15 -12
- data/lib/language_server/project/parser.rb +54 -54
- data/lib/language_server/version.rb +1 -1
- metadata +44 -54
@@ -11,35 +11,35 @@ module LanguageServer
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def call
|
14
|
-
(project.constants(uri: uri, line: line, character: character).map {|c|
|
14
|
+
(project.constants(uri: uri, line: line, character: character).map { |c|
|
15
15
|
Protocol::Interface::CompletionItem.new(
|
16
16
|
label: c.name,
|
17
17
|
detail: c.full_name,
|
18
18
|
documentation: "#{c.remote_path}##{c.lineno}",
|
19
|
-
kind: Protocol::Constant::CompletionItemKind::ENUM
|
19
|
+
kind: Protocol::Constant::CompletionItemKind::ENUM,
|
20
20
|
)
|
21
21
|
} +
|
22
|
-
project.classes(uri: uri, line: line, character: character).map {|c|
|
22
|
+
project.classes(uri: uri, line: line, character: character).map { |c|
|
23
23
|
Protocol::Interface::CompletionItem.new(
|
24
24
|
label: c.name,
|
25
25
|
detail: c.full_name,
|
26
26
|
documentation: "#{c.remote_path}##{c.lineno}",
|
27
|
-
kind: Protocol::Constant::CompletionItemKind::CLASS
|
27
|
+
kind: Protocol::Constant::CompletionItemKind::CLASS,
|
28
28
|
)
|
29
29
|
} +
|
30
|
-
project.modules(uri: uri, line: line, character: character).map {|m|
|
30
|
+
project.modules(uri: uri, line: line, character: character).map { |m|
|
31
31
|
Protocol::Interface::CompletionItem.new(
|
32
32
|
label: m.name,
|
33
33
|
detail: m.full_name,
|
34
34
|
documentation: "#{m.remote_path}##{m.lineno}",
|
35
|
-
kind: Protocol::Constant::CompletionItemKind::MODULE
|
35
|
+
kind: Protocol::Constant::CompletionItemKind::MODULE,
|
36
36
|
)
|
37
37
|
}).uniq(&:label)
|
38
38
|
end
|
39
39
|
|
40
40
|
private
|
41
41
|
|
42
|
-
|
42
|
+
attr_reader :uri, :line, :character, :project
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
@@ -28,16 +28,16 @@ module LanguageServer
|
|
28
28
|
Protocol::Interface::CompletionItem.new(
|
29
29
|
label: method_name,
|
30
30
|
detail: description,
|
31
|
-
kind: Protocol::Constant::CompletionItemKind::METHOD
|
31
|
+
kind: Protocol::Constant::CompletionItemKind::METHOD,
|
32
32
|
)
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
36
|
private
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
def source
|
39
|
+
@file_store.read_remote_uri(@uri)
|
40
|
+
end
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
@@ -15,20 +15,20 @@ module LanguageServer
|
|
15
15
|
range: Protocol::Interface::Range.new(
|
16
16
|
start: Protocol::Interface::Position.new(
|
17
17
|
line: n.lines.begin,
|
18
|
-
character: 0
|
18
|
+
character: 0,
|
19
19
|
),
|
20
20
|
end: Protocol::Interface::Position.new(
|
21
21
|
line: n.lines.end,
|
22
|
-
character: 0
|
23
|
-
)
|
24
|
-
)
|
22
|
+
character: 0,
|
23
|
+
),
|
24
|
+
),
|
25
25
|
)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
private
|
30
30
|
|
31
|
-
|
31
|
+
attr_reader :uri, :line, :character, :project
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
@@ -64,36 +64,36 @@ module LanguageServer
|
|
64
64
|
|
65
65
|
def each(&block)
|
66
66
|
all_paths.each do |path|
|
67
|
-
|
67
|
+
yield(read(path), path)
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
71
|
private
|
72
72
|
|
73
|
-
|
73
|
+
attr_reader :load_paths, :remote_root, :local_root
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
82
|
|
83
|
-
|
84
|
-
|
85
|
-
|
83
|
+
def exists_on_cache?(path)
|
84
|
+
cache_store.has_key?(path)
|
85
|
+
end
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
|
87
|
+
def read_from_cache(path)
|
88
|
+
cache_store[path]
|
89
|
+
end
|
90
90
|
|
91
|
-
|
92
|
-
|
93
|
-
|
91
|
+
def read_from_local(path)
|
92
|
+
File.read(path.local_path)
|
93
|
+
end
|
94
94
|
|
95
|
-
|
96
|
-
|
97
|
-
|
95
|
+
def cache_store
|
96
|
+
@cache_store ||= {}
|
97
|
+
end
|
98
98
|
end
|
99
99
|
end
|
@@ -1,22 +1,21 @@
|
|
1
1
|
begin
|
2
|
-
require
|
2
|
+
require "rubocop"
|
3
3
|
rescue LoadError
|
4
4
|
end
|
5
5
|
|
6
6
|
module LanguageServer
|
7
7
|
module Linter
|
8
8
|
class Rubocop
|
9
|
-
def initialize(source, config_path="")
|
9
|
+
def initialize(source, config_path = "")
|
10
10
|
@source = source
|
11
11
|
@config_path = config_path
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
14
|
def call
|
16
15
|
return [] unless defined? ::RuboCop
|
17
16
|
args = []
|
18
|
-
args += ["--config", @config_path] if @config_path !=
|
19
|
-
args += ["--format", "json","--stdin", "lsp_buffer.rb"]
|
17
|
+
args += ["--config", @config_path] if @config_path != ""
|
18
|
+
args += ["--format", "json", "--stdin", "lsp_buffer.rb"]
|
20
19
|
o = nil
|
21
20
|
begin
|
22
21
|
$stdin = StringIO.new(@source)
|
@@ -32,24 +31,23 @@ module LanguageServer
|
|
32
31
|
$stdout = STDOUT
|
33
32
|
end
|
34
33
|
return [] unless o
|
35
|
-
JSON
|
36
|
-
|
37
|
-
.
|
38
|
-
|
34
|
+
JSON.
|
35
|
+
parse(o)["files"].map { |v| v["offenses"] }.
|
36
|
+
flatten.
|
37
|
+
map { |v| Error.new(line_num: v["location"]["line"].to_i - 1, message: v["message"], type: convert_type(v["severity"])) }
|
39
38
|
end
|
40
39
|
|
41
40
|
private
|
42
41
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
42
|
+
def convert_type(type)
|
43
|
+
case type
|
44
|
+
when "refactor" then "warning"
|
45
|
+
when "convention" then "warning"
|
46
|
+
when "warning" then "warning"
|
47
|
+
when "error" then "error"
|
48
|
+
when "fatal" then "error"
|
49
|
+
end
|
50
50
|
end
|
51
|
-
end
|
52
|
-
|
53
51
|
end
|
54
52
|
end
|
55
53
|
end
|
@@ -3,10 +3,11 @@ require "stringio"
|
|
3
3
|
module LanguageServer
|
4
4
|
module Linter
|
5
5
|
class Error
|
6
|
-
attr_reader :line_num, :message, :type
|
6
|
+
attr_reader :line_num, :characters, :message, :type
|
7
7
|
|
8
|
-
def initialize(line_num:, message:, type:)
|
8
|
+
def initialize(line_num:, characters: 0..0, message:, type:)
|
9
9
|
@line_num = line_num
|
10
|
+
@characters = characters
|
10
11
|
@message = message
|
11
12
|
@type = type
|
12
13
|
end
|
@@ -16,7 +17,7 @@ module LanguageServer
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def ==(other)
|
19
|
-
line_num == other.line_num && message == other.message
|
20
|
+
line_num == other.line_num && characters == other.characters && message == other.message
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
@@ -26,55 +27,65 @@ module LanguageServer
|
|
26
27
|
end
|
27
28
|
|
28
29
|
def call
|
29
|
-
error_message.scan(/.+:(\d+):\s*(.+?)[,:]\s(.+)/).map do |line_num, type,
|
30
|
-
Error.new(line_num: line_num.to_i - 1, message: message, type: type)
|
30
|
+
error_message.scan(/.+:(\d+):\s*(.+?)[,:]\s(.+)/).map do |line_num, type, message|
|
31
|
+
Error.new(line_num: line_num.to_i - 1, characters: get_characters_from_error_message(error_message, line_num.to_i - 1), message: message, type: type)
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
34
35
|
private
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
37
|
+
# Since Ruby 2.4, syntax error information is outputted to Exception#message instead of stderr
|
38
|
+
if begin; stderr = $stderr; $stderr = StringIO.new; RubyVM::InstructionSequence.compile("="); rescue SyntaxError => e; e.message != "compile error"; ensure; $stderr = stderr; end
|
39
|
+
def error_message
|
40
|
+
with_verbose do
|
41
|
+
begin
|
42
|
+
capture_stderr { RubyVM::InstructionSequence.compile(@source) }
|
43
|
+
rescue SyntaxError => e
|
44
|
+
e.message
|
45
|
+
end
|
44
46
|
end
|
45
47
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
48
|
+
else
|
49
|
+
def error_message
|
50
|
+
with_verbose do
|
51
|
+
capture_stderr do
|
52
|
+
begin
|
53
|
+
RubyVM::InstructionSequence.compile(@source)
|
54
|
+
rescue SyntaxError
|
55
|
+
end
|
54
56
|
end
|
55
57
|
end
|
56
58
|
end
|
57
59
|
end
|
58
|
-
end
|
59
60
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
61
|
+
def with_verbose
|
62
|
+
origin = $VERBOSE
|
63
|
+
$VERBOSE = true
|
64
|
+
yield
|
65
|
+
ensure
|
66
|
+
$VERBOSE = origin
|
67
|
+
end
|
67
68
|
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
def capture_stderr
|
70
|
+
origin = $stderr
|
71
|
+
$stderr = StringIO.new
|
71
72
|
|
72
|
-
|
73
|
+
yield
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
$stderr.string
|
76
|
+
ensure
|
77
|
+
$stderr = origin
|
78
|
+
end
|
79
|
+
|
80
|
+
def get_characters_from_error_message(error_message, line_index)
|
81
|
+
error_mark_included_line = error_message.split("\n")[2]
|
82
|
+
|
83
|
+
if !error_mark_included_line.nil? && character_start = error_mark_included_line.index("^")
|
84
|
+
Range.new(character_start, character_start + 1)
|
85
|
+
else
|
86
|
+
Range.new(0, @source.split("\n")[line_index].length - 1)
|
87
|
+
end
|
88
|
+
end
|
78
89
|
end
|
79
90
|
end
|
80
91
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "logger"
|
2
2
|
|
3
3
|
module LanguageServer
|
4
4
|
class << self
|
@@ -8,16 +8,16 @@ module LanguageServer
|
|
8
8
|
end
|
9
9
|
|
10
10
|
class Formatter
|
11
|
-
RESET = "\e[0m"
|
12
|
-
RED = "\e[31m"
|
13
|
-
YELLOW = "\e[33m"
|
11
|
+
RESET = "\e[0m".freeze
|
12
|
+
RED = "\e[31m".freeze
|
13
|
+
YELLOW = "\e[33m".freeze
|
14
14
|
|
15
15
|
def call(severity, *rest)
|
16
16
|
msg = default_message(severity, *rest)
|
17
17
|
case severity
|
18
|
-
when
|
18
|
+
when "ERROR"
|
19
19
|
RED + msg + RESET
|
20
|
-
when
|
20
|
+
when "WARN"
|
21
21
|
YELLOW + msg + RESET
|
22
22
|
else
|
23
23
|
msg
|
@@ -26,12 +26,12 @@ module LanguageServer
|
|
26
26
|
|
27
27
|
private
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
def default_message(*args)
|
30
|
+
default_formatter.call(*args)
|
31
|
+
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
def default_formatter
|
34
|
+
@default_formatter ||= ::Logger::Formatter.new
|
35
|
+
end
|
36
36
|
end
|
37
37
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "language_server/project/parser"
|
2
2
|
|
3
3
|
module LanguageServer
|
4
4
|
class Project
|
@@ -12,11 +12,11 @@ module LanguageServer
|
|
12
12
|
def find_definitions(uri:, line:, character:)
|
13
13
|
result = result_store[file_store.path_from_remote_uri(uri)]
|
14
14
|
|
15
|
-
ref = result.refs.select {|node| node.lines.include?(line) && node.characters.include?(character) }.min_by {|node| node.characters.size }
|
15
|
+
ref = result.refs.select { |node| node.lines.include?(line) && node.characters.include?(character) }.min_by { |node| node.characters.size }
|
16
16
|
|
17
17
|
return [] unless ref
|
18
18
|
|
19
|
-
lazy_modules.select {|n| n.full_name == ref.full_name }.force + lazy_classes.select {|n| n.full_name == ref.full_name }.force
|
19
|
+
lazy_modules.select { |n| n.full_name == ref.full_name }.force + lazy_classes.select { |n| n.full_name == ref.full_name }.force
|
20
20
|
end
|
21
21
|
|
22
22
|
def recalculate_result(uri)
|
@@ -27,58 +27,58 @@ module LanguageServer
|
|
27
27
|
def constants(uri: nil, line: nil, character: nil)
|
28
28
|
node = find_nearest_node(uri: uri, line: line, character: character) if uri && line && character
|
29
29
|
|
30
|
-
lazy_constants.select {|n| n.names[0..-2] == Array(node && node.names).first(n.names.size - 1) }.force
|
30
|
+
lazy_constants.select { |n| n.names[0..-2] == Array(node && node.names).first(n.names.size - 1) }.force
|
31
31
|
end
|
32
32
|
|
33
33
|
def modules(uri: nil, line: nil, character: nil)
|
34
34
|
node = find_nearest_node(uri: uri, line: line, character: character) if uri && line && character
|
35
35
|
|
36
|
-
lazy_modules.select {|n| n.names[0..-2] == Array(node && node.names).first(n.names.size - 1) }.force
|
36
|
+
lazy_modules.select { |n| n.names[0..-2] == Array(node && node.names).first(n.names.size - 1) }.force
|
37
37
|
end
|
38
38
|
|
39
39
|
def classes(uri: nil, line: nil, character: nil)
|
40
40
|
node = find_nearest_node(uri: uri, line: line, character: character) if uri && line && character
|
41
41
|
|
42
|
-
lazy_classes.select {|n| n.names[0..-2] == Array(node && node.names).first(n.names.size - 1) }.force
|
42
|
+
lazy_classes.select { |n| n.names[0..-2] == Array(node && node.names).first(n.names.size - 1) }.force
|
43
43
|
end
|
44
44
|
|
45
45
|
private
|
46
46
|
|
47
|
-
|
47
|
+
attr_reader :file_store, :result_store
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
def lazy_modules
|
54
|
-
result_store.each_value.lazy.flat_map(&:modules)
|
55
|
-
end
|
49
|
+
def lazy_constants
|
50
|
+
result_store.each_value.lazy.flat_map(&:constants)
|
51
|
+
end
|
56
52
|
|
57
|
-
|
58
|
-
|
59
|
-
|
53
|
+
def lazy_modules
|
54
|
+
result_store.each_value.lazy.flat_map(&:modules)
|
55
|
+
end
|
60
56
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
}
|
65
|
-
end
|
57
|
+
def lazy_classes
|
58
|
+
result_store.each_value.lazy.flat_map(&:classes)
|
59
|
+
end
|
66
60
|
|
67
|
-
|
68
|
-
|
61
|
+
def fetch_result
|
62
|
+
file_store.each { |content, path|
|
63
|
+
calculate(content, path)
|
64
|
+
}
|
65
|
+
end
|
69
66
|
|
70
|
-
(
|
71
|
-
|
67
|
+
def find_nearest_node(uri:, line:, character:)
|
68
|
+
result = result_store[file_store.path_from_remote_uri(uri)]
|
72
69
|
|
73
|
-
|
74
|
-
begin
|
75
|
-
result = Parser.parse(content, path)
|
76
|
-
rescue => e
|
77
|
-
LanguageServer.logger.warn("Parse failed (local: #{path.local_path}, remote: #{path.remote_path})")
|
78
|
-
LanguageServer.logger.warn(e)
|
70
|
+
(result.modules + result.classes).select { |node| node.lines.include?(line) }.min_by { |node| node.lines.size }
|
79
71
|
end
|
80
72
|
|
81
|
-
|
82
|
-
|
73
|
+
def calculate(content, path)
|
74
|
+
begin
|
75
|
+
result = Parser.parse(content, path)
|
76
|
+
rescue => e
|
77
|
+
LanguageServer.logger.warn("Parse failed (local: #{path.local_path}, remote: #{path.remote_path})")
|
78
|
+
LanguageServer.logger.warn(e)
|
79
|
+
end
|
80
|
+
|
81
|
+
result_store[path] = result if result
|
82
|
+
end
|
83
83
|
end
|
84
84
|
end
|