Skip to content

Commit 9e46e53

Browse files
authored
Merge pull request #27 from joe-re/rubocop-integration
Add rubocop integration
2 parents 8dc435e + 199aaf2 commit 9e46e53

File tree

6 files changed

+107
-1
lines changed

6 files changed

+107
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Not released
44

5+
- rubocop support #27
6+
57
## 0.8.0
68

79
- Use RubyVM::InstructionSequence.compile instead of open3

language_server.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,5 @@ Gem::Specification.new do |spec|
3939
spec.add_development_dependency "m"
4040
spec.add_development_dependency "awesome_print"
4141
spec.add_development_dependency "benchmark-ips"
42+
spec.add_development_dependency "rubocop"
4243
end

lib/language_server.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
require "language_server/logger"
33
require "language_server/protocol"
44
require "language_server/linter/ruby_wc"
5+
require "language_server/linter/rubocop"
56
require "language_server/completion_provider/rcodetools"
67
require "language_server/completion_provider/ad_hoc"
78
require "language_server/definition_provider/ad_hoc"
@@ -93,8 +94,9 @@ def on(method, &callback)
9394
text = request[:params][:contentChanges][0][:text]
9495
file_store.cache(uri, text)
9596
project.recalculate_result(uri)
97+
diagnostics = (Linter::Rubocop.new(text).call + Linter::RubyWC.new(text).call).flatten
9698

97-
diagnostics = Linter::RubyWC.new(text).call.map do |error|
99+
diagnostics = diagnostics.map do |error|
98100
Protocol::Interface::Diagnostic.new(
99101
message: error.message,
100102
severity: error.warning? ? Protocol::Constant::DiagnosticSeverity::WARNING : Protocol::Constant::DiagnosticSeverity::ERROR,
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
begin
2+
require 'rubocop'
3+
rescue LoadError
4+
end
5+
6+
module LanguageServer
7+
module Linter
8+
class Rubocop
9+
def initialize(source, config_path="")
10+
@source = source
11+
@config_path = config_path
12+
end
13+
14+
15+
def call
16+
return [] unless defined? ::RuboCop
17+
args = []
18+
args += ["--config", @config_path] if @config_path != ''
19+
args += ["--format", "json","--stdin", "lsp_buffer.rb"]
20+
o = nil
21+
begin
22+
$stdin = StringIO.new(@source)
23+
$stdout = StringIO.new
24+
config_store = ::RuboCop::ConfigStore.new
25+
options, paths = ::RuboCop::Options.new.parse(args)
26+
config_store.options_config = options[:config] if options[:config]
27+
runner = ::RuboCop::Runner.new(options, config_store)
28+
runner.run(paths)
29+
o = $stdout.string
30+
ensure
31+
$stdin = STDIN
32+
$stdout = STDOUT
33+
end
34+
return [] unless o
35+
JSON
36+
.parse(o)['files'].map { |v| v['offenses'] }
37+
.flatten
38+
.map { |v| Error.new(line_num: v['location']['line'].to_i - 1, message: v['message'], type: convert_type(v['severity'])) }
39+
end
40+
41+
private
42+
43+
def convert_type(type)
44+
case type
45+
when 'refactor' then 'warning'
46+
when 'convention' then 'warning'
47+
when 'warning' then 'warning'
48+
when 'error' then 'error'
49+
when 'fatal' then 'error'
50+
end
51+
end
52+
53+
end
54+
end
55+
end

test/fixtures/rubocop.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
AllCops:
2+
DisabledByDefault: true
3+
4+
Lint/UselessAssignment:
5+
Enabled: true
6+
StringLiterals:
7+
EnforcedStyle: single_quotes
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
require 'test_helper'
2+
3+
module LanguageServer::Linter
4+
class RubocopTest < Minitest::Test
5+
def setup
6+
@config_path = File.expand_path('./../../../fixtures/rubocop.yml', __FILE__)
7+
end
8+
9+
def test_fatal_error
10+
source = 'require "foo'
11+
actual = Rubocop.new(source, @config_path).call
12+
assert do
13+
actual == [
14+
Error.new(
15+
line_num: 0,
16+
message: "unterminated string meets end of file\n(Using Ruby 2.1 parser; configure using `TargetRubyVersion` parameter, under `AllCops`)",
17+
type: 'error'
18+
)
19+
]
20+
end
21+
end
22+
23+
def test_warning_error
24+
source = "a = 'a'"
25+
actual = Rubocop.new(source, @config_path).call
26+
assert do
27+
actual.first == Error.new(line_num: 0, message: "Useless assignment to variable - `a`.", type: 'warning')
28+
end
29+
end
30+
31+
def test_convention_error
32+
source = 'require "foo"'
33+
actual = Rubocop.new(source, @config_path).call
34+
assert do
35+
actual.first == Error.new(line_num: 0, message: "Prefer single-quoted strings when you don't need string interpolation or special symbols.", type: 'warning')
36+
end
37+
end
38+
end
39+
end

0 commit comments

Comments
 (0)