From d0417d64bb53a48ab95eaa74a8f92c6f9d47352a Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Mon, 24 Aug 2020 12:48:32 -0400 Subject: [PATCH] Add `Cop.documentation_url` --- CHANGELOG.md | 2 ++ lib/rubocop.rb | 1 + lib/rubocop/cop/base.rb | 16 +++++++++++++++ lib/rubocop/cop/documentation.rb | 22 +++++++++++++++++++++ lib/rubocop/cops_documentation_generator.rb | 5 +++-- spec/rubocop/cop/cop_spec.rb | 16 +++++++++++++++ 6 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 lib/rubocop/cop/documentation.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 59c9bd2a8e3..4d6af342fb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ * [#8531](https://github.com/rubocop-hq/rubocop/issues/8531): Add new `Lint/EmptyFile` cop. ([@fatkodima][]) * Add new `Lint/TrailingCommaInAttributeDeclaration` cop. ([@drenmi][]) * [#8578](https://github.com/rubocop-hq/rubocop/pull/8578): Add `:restore_registry` context and `stub_cop_class` helper class. ([@marcandre][]) +* [#8579](https://github.com/rubocop-hq/rubocop/pull/8579): Add `Cop.documentation_url`. ([@marcandre][]) + ### Bug fixes diff --git a/lib/rubocop.rb b/lib/rubocop.rb index 787e1da8614..c458bd750e8 100644 --- a/lib/rubocop.rb +++ b/lib/rubocop.rb @@ -34,6 +34,7 @@ require_relative 'rubocop/cop/base' require_relative 'rubocop/cop/cop' require_relative 'rubocop/cop/commissioner' +require_relative 'rubocop/cop/documentation' require_relative 'rubocop/cop/corrector' require_relative 'rubocop/cop/force' require_relative 'rubocop/cop/severity' diff --git a/lib/rubocop/cop/base.rb b/lib/rubocop/cop/base.rb index 3fafc5b0f8b..c8525373377 100644 --- a/lib/rubocop/cop/base.rb +++ b/lib/rubocop/cop/base.rb @@ -56,6 +56,14 @@ def self.autocorrect_incompatible_with [] end + # Cops (other than builtin) are encouraged to implement this + # @return [String, nil] + # + # @api public + def self.documentation_url + Documentation.url_for(self) if builtin? + end + def initialize(config = nil, options = nil) @config = config || Config.new @options = options || { debug: false } @@ -297,6 +305,14 @@ def complete_investigation ### Actually private methods + def self.builtin? + return false unless (m = instance_methods(false).first) # any custom method will do + + path, _line = instance_method(m).source_location + path.start_with?(__dir__) + end + private_class_method :builtin? + def reset_investigation @currently_disabled_lines = @current_offenses = @processed_source = @current_corrector = nil end diff --git a/lib/rubocop/cop/documentation.rb b/lib/rubocop/cop/documentation.rb new file mode 100644 index 00000000000..651bcde37c9 --- /dev/null +++ b/lib/rubocop/cop/documentation.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + # Helpers for builtin documentation + module Documentation + module_function + + # @api private + def department_to_basename(department) + "cops_#{department.downcase}" + end + + # @api private + def url_for(cop_class) + base = department_to_basename(cop_class.department) + fragment = cop_class.cop_name.downcase.gsub(/[^a-z]/, '') + "https://docs.rubocop.org/rubocop/#{base}.html##{fragment}" + end + end + end +end diff --git a/lib/rubocop/cops_documentation_generator.rb b/lib/rubocop/cops_documentation_generator.rb index 50cd0043c2e..9dffe47ceac 100644 --- a/lib/rubocop/cops_documentation_generator.rb +++ b/lib/rubocop/cops_documentation_generator.rb @@ -2,6 +2,7 @@ # Class for generating documentation of all cops departments class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength + include ::RuboCop::Cop::Documentation # This class will only generate documentation for cops that belong to one of # the departments given in the `departments` array. E.g. if we only wanted # documentation for Lint cops: @@ -210,7 +211,7 @@ def print_cops_of_department(department) selected_cops.each do |cop| content << print_cop_with_doc(cop) end - file_name = "#{Dir.pwd}/docs/modules/ROOT/pages/cops_#{department.downcase}.adoc" + file_name = "#{Dir.pwd}/docs/modules/ROOT/pages/#{department_to_basename(department)}.adoc" File.open(file_name, 'w') do |file| puts "* generated #{file_name}" file.write("#{content.strip}\n") @@ -243,7 +244,7 @@ def cop_code(cop) def table_of_content_for_department(department) type_title = department[0].upcase + department[1..-1] - filename = "cops_#{department.downcase}.adoc" + filename = "#{department_to_basename(department)}.adoc" content = +"=== Department xref:#{filename}[#{type_title}]\n\n" cops_of_department(department).each do |cop| anchor = cop.cop_name.sub('/', '').downcase diff --git a/spec/rubocop/cop/cop_spec.rb b/spec/rubocop/cop/cop_spec.rb index 065c0860b06..b337ac3fadf 100644 --- a/spec/rubocop/cop/cop_spec.rb +++ b/spec/rubocop/cop/cop_spec.rb @@ -56,6 +56,22 @@ end end + describe '.documentation_url' do + subject(:url) { cop_class.documentation_url } + + describe 'for a builtin cop class' do + let(:cop_class) { RuboCop::Cop::Layout::BlockEndNewline } + + it { is_expected.to eq 'https://docs.rubocop.org/rubocop/cops_layout.html#layoutblockendnewline' } # rubocop:disable Layout/LineLength + end + + describe 'for a custom cop class', :restore_registry do + let(:cop_class) { stub_cop_class('Some::Cop') { def foo; end } } + + it { is_expected.to eq nil } + end + end + it 'keeps track of offenses' do cop.add_offense(nil, location: location, message: 'message')