From 95f182ff3c4ae7999d562f04e8bcb77aa7ebbd1d Mon Sep 17 00:00:00 2001 From: Fumiaki MATSUSHIMA Date: Thu, 21 Sep 2017 00:39:03 +0900 Subject: [PATCH] [wip] experimental typo checker --- Gemfile | 2 ++ lib/language_server.rb | 6 ++++- lib/language_server/linter/did_you_mean.rb | 20 ++++++++++++++++ lib/language_server/linter/error.rb | 23 +++++++++++++++++++ .../linter/did_you_mean_test.rb | 17 ++++++++++++++ 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 lib/language_server/linter/did_you_mean.rb create mode 100644 lib/language_server/linter/error.rb create mode 100644 test/language_server/linter/did_you_mean_test.rb diff --git a/Gemfile b/Gemfile index 32eeb0c..57628bf 100644 --- a/Gemfile +++ b/Gemfile @@ -2,3 +2,5 @@ source 'https://rubygems.org' # Specify your gem's dependencies in language_server.gemspec gemspec + +gem 'did_you_mean', git: 'https://github.com/yuki24/did_you_mean', branch: 'static-typo-checker' diff --git a/lib/language_server.rb b/lib/language_server.rb index c385639..bfb6a25 100644 --- a/lib/language_server.rb +++ b/lib/language_server.rb @@ -2,6 +2,7 @@ require "language_server/logger" require "language_server/protocol" require "language_server/linter/ruby_wc" +require "language_server/linter/did_you_mean" require "language_server/completion_provider/rcodetools" require "language_server/completion_provider/ad_hoc" require "language_server/definition_provider/ad_hoc" @@ -94,7 +95,10 @@ def on(method, &callback) file_store.cache(uri, text) project.recalculate_result(uri) - diagnostics = Linter::RubyWC.new(text).call.map do |error| + diagnostics = [ + Linter::RubyWC.new(text).call, + Linter::DidYouMean.new(text).call + ].flatten.map do |error| Protocol::Interface::Diagnostic.new( message: error.message, severity: error.warning? ? Protocol::Constant::DiagnosticSeverity::WARNING : Protocol::Constant::DiagnosticSeverity::ERROR, diff --git a/lib/language_server/linter/did_you_mean.rb b/lib/language_server/linter/did_you_mean.rb new file mode 100644 index 0000000..1616101 --- /dev/null +++ b/lib/language_server/linter/did_you_mean.rb @@ -0,0 +1,20 @@ +require "open3" +require "language_server/linter/error" + +module LanguageServer + module Linter + class DidYouMean + def initialize(source) + @source = source + end + + def call + out, _, _ = Open3.capture3("ruby -r bundler/setup -r did_you_mean/static", stdin_data: @source) + + (JSON.parse(out, symbolize_names: true)[:undefined_names] || {}).values.flatten.map do |undefined_name:, symbol_type:, path:, lineno:, suggestions:| + Error.new(line_num: lineno - 1, message: "Undefined method #{undefined_name}. Did you mean? #{suggestions.join("\n")}", type: 'warning') + end + end + end + end +end diff --git a/lib/language_server/linter/error.rb b/lib/language_server/linter/error.rb new file mode 100644 index 0000000..8b95545 --- /dev/null +++ b/lib/language_server/linter/error.rb @@ -0,0 +1,23 @@ +require "open3" + +module LanguageServer + module Linter + class Error + attr_reader :line_num, :message, :type + + def initialize(line_num:, message:, type:) + @line_num = line_num + @message = message + @type = type + end + + def warning? + @type == "warning" + end + + def ==(other) + line_num == other.line_num && message == other.message + end + end + end +end diff --git a/test/language_server/linter/did_you_mean_test.rb b/test/language_server/linter/did_you_mean_test.rb new file mode 100644 index 0000000..47cd591 --- /dev/null +++ b/test/language_server/linter/did_you_mean_test.rb @@ -0,0 +1,17 @@ +require 'test_helper' + +module LanguageServer::Linter + class DidYouMeanTest < Minitest::Test + def test_error + linter = DidYouMean.new(<<-EOS.strip_heredoc) + def hi + foo + end + EOS + + assert { + linter.call == [Error.new(line_num: 1, message: "Undefined method foo. Did you mean? for", type: "warning")] + } + end + end +end