From 91bae59a7b92a3a7adaad1c3bf1bc89cf9b46237 Mon Sep 17 00:00:00 2001 From: Vinicius Stock Date: Thu, 30 Jun 2022 16:07:30 -0400 Subject: [PATCH] Refactor RuboCop requests with separate runners --- lib/ruby_lsp/requests.rb | 3 +- lib/ruby_lsp/requests/diagnostics.rb | 18 +++--- lib/ruby_lsp/requests/formatting.rb | 29 +++------ lib/ruby_lsp/requests/rubocop_request.rb | 61 ------------------- .../support/rubocop_diagnostics_runner.rb | 55 +++++++++++++++++ .../support/rubocop_formatting_runner.rb | 45 ++++++++++++++ rakelib/check_docs.rake | 5 +- 7 files changed, 124 insertions(+), 92 deletions(-) delete mode 100644 lib/ruby_lsp/requests/rubocop_request.rb create mode 100644 lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb create mode 100644 lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb diff --git a/lib/ruby_lsp/requests.rb b/lib/ruby_lsp/requests.rb index b79cb88e8..7b85b257b 100644 --- a/lib/ruby_lsp/requests.rb +++ b/lib/ruby_lsp/requests.rb @@ -18,7 +18,6 @@ module Requests autoload :FoldingRanges, "ruby_lsp/requests/folding_ranges" autoload :SelectionRanges, "ruby_lsp/requests/selection_ranges" autoload :SemanticHighlighting, "ruby_lsp/requests/semantic_highlighting" - autoload :RuboCopRequest, "ruby_lsp/requests/rubocop_request" autoload :Formatting, "ruby_lsp/requests/formatting" autoload :Diagnostics, "ruby_lsp/requests/diagnostics" autoload :CodeActions, "ruby_lsp/requests/code_actions" @@ -30,6 +29,8 @@ module Support autoload :SelectionRange, "ruby_lsp/requests/support/selection_range" autoload :SemanticTokenEncoder, "ruby_lsp/requests/support/semantic_token_encoder" autoload :SyntaxErrorDiagnostic, "ruby_lsp/requests/support/syntax_error_diagnostic" + autoload :RuboCopDiagnosticsRunner, "ruby_lsp/requests/support/rubocop_diagnostics_runner" + autoload :RuboCopFormattingRunner, "ruby_lsp/requests/support/rubocop_formatting_runner" end end end diff --git a/lib/ruby_lsp/requests/diagnostics.rb b/lib/ruby_lsp/requests/diagnostics.rb index bac13ee0d..5512ddfa6 100644 --- a/lib/ruby_lsp/requests/diagnostics.rb +++ b/lib/ruby_lsp/requests/diagnostics.rb @@ -16,9 +16,16 @@ module Requests # puts "Hello" # --> diagnostics: incorrect indentantion # end # ``` - class Diagnostics < RuboCopRequest + class Diagnostics < BaseRequest extend T::Sig + sig { params(uri: String, document: Document).void } + def initialize(uri, document) + super(document) + + @uri = uri + end + sig do override.returns( T.any( @@ -30,14 +37,7 @@ class Diagnostics < RuboCopRequest def run return syntax_error_diagnostics if @document.syntax_errors? - super - - @diagnostics - end - - sig { params(_file: String, offenses: T::Array[RuboCop::Cop::Offense]).void } - def file_finished(_file, offenses) - @diagnostics = offenses.map { |offense| Support::RuboCopDiagnostic.new(offense, @uri) } + Support::RuboCopDiagnosticsRunner.instance.run(@uri, @document) end private diff --git a/lib/ruby_lsp/requests/formatting.rb b/lib/ruby_lsp/requests/formatting.rb index 225b429c7..f81eb1ce6 100644 --- a/lib/ruby_lsp/requests/formatting.rb +++ b/lib/ruby_lsp/requests/formatting.rb @@ -16,44 +16,33 @@ module Requests # puts "Hello" # --> formatting: fixes the indentation on save # end # ``` - class Formatting < RuboCopRequest + class Formatting < BaseRequest extend T::Sig - RUBOCOP_FLAGS = T.let((COMMON_RUBOCOP_FLAGS + ["--auto-correct"]).freeze, T::Array[String]) - sig { params(uri: String, document: Document).void } def initialize(uri, document) - super - @formatted_text = T.let(nil, T.nilable(String)) + super(document) + + @uri = uri end sig { override.returns(T.nilable(T.all(T::Array[LanguageServer::Protocol::Interface::TextEdit], Object))) } def run - super + formatted_text = Support::RuboCopFormattingRunner.instance.run(@uri, @document) + return unless formatted_text - @formatted_text = @options[:stdin] # Rubocop applies the corrections on stdin - return unless @formatted_text + size = @document.source.size [ LanguageServer::Protocol::Interface::TextEdit.new( range: LanguageServer::Protocol::Interface::Range.new( start: LanguageServer::Protocol::Interface::Position.new(line: 0, character: 0), - end: LanguageServer::Protocol::Interface::Position.new( - line: text.size, - character: text.size - ) + end: LanguageServer::Protocol::Interface::Position.new(line: size, character: size) ), - new_text: @formatted_text + new_text: formatted_text ), ] end - - private - - sig { returns(T::Array[String]) } - def rubocop_flags - RUBOCOP_FLAGS - end end end end diff --git a/lib/ruby_lsp/requests/rubocop_request.rb b/lib/ruby_lsp/requests/rubocop_request.rb deleted file mode 100644 index d1b2ff4b6..000000000 --- a/lib/ruby_lsp/requests/rubocop_request.rb +++ /dev/null @@ -1,61 +0,0 @@ -# typed: strict -# frozen_string_literal: true - -require "rubocop" -require "cgi" - -module RubyLsp - module Requests - # :nodoc: - class RuboCopRequest < RuboCop::Runner - extend T::Sig - extend T::Helpers - - abstract! - - COMMON_RUBOCOP_FLAGS = T.let([ - "--stderr", # Print any output to stderr so that our stdout does not get polluted - "--force-exclusion", - "--format", - "RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter - ].freeze, T::Array[String]) - - sig { returns(String) } - attr_reader :file - - sig { returns(String) } - attr_reader :text - - sig { params(uri: String, document: Document).void } - def initialize(uri, document) - @file = T.let(CGI.unescape(URI.parse(uri).path), String) - @document = document - @text = T.let(document.source, String) - @uri = uri - @options = T.let({}, T::Hash[Symbol, T.untyped]) - @diagnostics = T.let([], T::Array[Support::RuboCopDiagnostic]) - - super( - ::RuboCop::Options.new.parse(rubocop_flags).first, - ::RuboCop::ConfigStore.new - ) - end - - sig { overridable.returns(Object) } - def run - # We communicate with Rubocop via stdin - @options[:stdin] = text - - # Invoke the actual run method with just this file in `paths` - super([file]) - end - - private - - sig { returns(T::Array[String]) } - def rubocop_flags - COMMON_RUBOCOP_FLAGS - end - end - end -end diff --git a/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb b/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb new file mode 100644 index 000000000..9578b9012 --- /dev/null +++ b/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb @@ -0,0 +1,55 @@ +# typed: strict +# frozen_string_literal: true + +require "rubocop" +require "cgi" +require "singleton" + +module RubyLsp + module Requests + module Support + # :nodoc: + class RuboCopDiagnosticsRunner < RuboCop::Runner + extend T::Sig + include Singleton + + sig { void } + def initialize + @options = T.let({}, T::Hash[Symbol, T.untyped]) + @uri = T.let(nil, T.nilable(String)) + @diagnostics = T.let([], T::Array[Support::RuboCopDiagnostic]) + + super( + ::RuboCop::Options.new.parse([ + "--stderr", # Print any output to stderr so that our stdout does not get polluted + "--force-exclusion", + "--format", + "RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter + ]).first, + ::RuboCop::ConfigStore.new + ) + end + + sig { params(uri: String, document: Document).returns(T::Array[Support::RuboCopDiagnostic]) } + def run(uri, document) + @uri = uri + + file = CGI.unescape(URI.parse(uri).path) + # We communicate with Rubocop via stdin + @options[:stdin] = document.source + + # Invoke RuboCop with just this file in `paths` + process_file(file) + @diagnostics + end + + private + + sig { params(_file: String, offenses: T::Array[RuboCop::Cop::Offense]).void } + def file_finished(_file, offenses) + @diagnostics = offenses.map { |offense| Support::RuboCopDiagnostic.new(offense, T.must(@uri)) } + end + end + end + end +end diff --git a/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb b/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb new file mode 100644 index 000000000..ffc0614a6 --- /dev/null +++ b/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb @@ -0,0 +1,45 @@ +# typed: strict +# frozen_string_literal: true + +require "rubocop" +require "cgi" +require "singleton" + +module RubyLsp + module Requests + module Support + # :nodoc: + class RuboCopFormattingRunner < RuboCop::Runner + extend T::Sig + include Singleton + + sig { void } + def initialize + @options = T.let({}, T::Hash[Symbol, T.untyped]) + + super( + ::RuboCop::Options.new.parse([ + "--stderr", # Print any output to stderr so that our stdout does not get polluted + "--force-exclusion", + "--format", + "RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter + "-a", # --auto-correct + ]).first, + ::RuboCop::ConfigStore.new + ) + end + + sig { params(uri: String, document: Document).returns(T.nilable(String)) } + def run(uri, document) + file = CGI.unescape(URI.parse(uri).path) + # We communicate with Rubocop via stdin + @options[:stdin] = document.source + + # Invoke RuboCop with just this file in `paths` + process_file(file) + @options[:stdin] + end + end + end + end +end diff --git a/rakelib/check_docs.rake b/rakelib/check_docs.rake index 0e3039874..194981247 100644 --- a/rakelib/check_docs.rake +++ b/rakelib/check_docs.rake @@ -8,7 +8,8 @@ task :check_docs do require "syntax_tree" require "logger" require "ruby_lsp/requests/base_request" - require "ruby_lsp/requests/rubocop_request" + require "ruby_lsp/requests/support/rubocop_diagnostics_runner" + require "ruby_lsp/requests/support/rubocop_formatting_runner" request_doc_files = Dir["#{Dir.pwd}/lib/ruby_lsp/requests/*.rb"] request_doc_files << "#{Dir.pwd}/lib/ruby_lsp/requests.rb" @@ -22,6 +23,8 @@ task :check_docs do error_messages = RubyLsp::Requests .constants # rubocop:disable Sorbet/ConstantsFromStrings .each_with_object(Hash.new { |h, k| h[k] = [] }) do |request, errors| + next if request == :Support + full_name = "RubyLsp::Requests::#{request}" docs = YARD::Registry.at(full_name).docstring next if /:nodoc:/.match?(docs)